Claude Code prepares a schema migration that replaces customer_status with a new enum. The patch is tidy. Migration tests pass. The review packet contains this line:
Rollback: restore the previous schema and redeploy the prior commit.
It sounds responsible. It proves almost nothing.
The old application may fail to read rows written after the migration. The database snapshot may have expired. A feature flag may cover the main read path while a queue consumer keeps writing the new format. The restore might take forty minutes against a ten-minute incident budget.
The agent has described recovery. It has not shown that recovery is available.
Before approving a production state change, I want a rollback rehearsal tied to the exact change. The rehearsal verifies the target, runs the real recovery mechanism in isolation, covers state outside Git, checks compatibility, and records how long recovery took.
verify the recovery target exists
A vague instruction such as “deploy the old version” leaves the reviewer to reconstruct the target. Start with exact identifiers:
rollback_target:
app_commit: 3d5a9be
schema_version: 184
database_snapshot: customer-db-pre-184
feature_flag_state:
customer_status_v2: false
Then resolve each target before approval. Check that the deployment artifact for commit 3d5a9be still exists, that schema version 184 matches it, and that the snapshot belongs to the correct database and environment.
Fail closed when one target is missing. An expired snapshot does not become a recovery plan because the prompt contains its old name.
This is the next step after writing a rollback note before Claude Code changes code. The note defines the intended path. The rehearsal tests it.
run the real restore in isolation
A copied runbook command is weak evidence. Run the same recovery mechanism against an isolated copy or disposable environment:
rehearsal:
environment: restore-sandbox-2026-08-08
source_snapshot: customer-db-pre-184
restore_command: ./ops/restore-schema --to 184
deploy_artifact: app-3d5a9be.sha256:8f71...
started_at: 2026-08-08T08:04:12Z
finished_at: 2026-08-08T08:10:37Z
exit_code: 0
The sandbox identity matters. A rehearsal must not become an unapproved production write. Bind the agent’s capability record to the sandbox environment and the narrow restore methods it needs. Production remains read-only during the exercise.
An exit code of zero is useful, but it only says the command finished. It does not prove the old service can operate on the restored state.
map every forward effect to recovery
git revert covers repository state. A production run may also change a database, a feature flag, an MCP object, a queue schema, or an external service.
List the forward effects and map each one to a recovery action:
side_effect_coverage:
- forward: deploy app commit 91c2f40
recovery: deploy app commit 3d5a9be
- forward: migrate schema 184 to 185
recovery: restore snapshot customer-db-pre-184
- forward: set customer_status_v2=true
recovery: set customer_status_v2=false
One unmapped effect blocks approval. This rule is especially important for MCP writes because the repository may contain no trace of the external object that changed. An effect receipt can identify the observed target and resulting state. The rehearsal record should point to that receipt rather than relying on the agent’s summary.
test old code against new data
The awkward rollback test is the one most likely to find a real problem:
- Start from schema version
184. - Apply migration
185. - Write a customer record that uses the new enum value.
- Restore the old application and schema path.
- Ask the old application to read the record.
A failed probe might look like this:
compatibility_probe:
fixture: customer-created-under-v185
old_reader_result: fail
reason: unknown_status_value
rollback_decision: blocked
This failure is good evidence. The restore command worked, but the recovered application cannot read data produced after the migration. The team may need an expand-and-contract migration, a dual-read period, or a forward fix instead of rollback.
Claude Code should report that constraint. It should not turn a clean restore log into confidence about the whole service.
put a time budget on the rehearsal
Recovery that finishes eventually may still fail the operating target. Record the full duration and compare it with the incident budget:
recovery_budget:
maximum_minutes: 10
rehearsed_minutes: 6.42
includes:
- artifact retrieval
- snapshot restore
- application deployment
- cache warm-up
- verification probes
result: pass
A rehearsal against ten fixture rows cannot predict the restore time for a two-terabyte database without an explicit scaling assumption. Record the data volume and test environment beside the timing result. Otherwise the number is decoration.
verify the recovered behaviour
A generic health endpoint can stay green while the customer path is broken. Check the behaviour affected by the change:
post_recovery_checks:
schema_version: 184
old_app_health: pass
customer_read_path: pass
customer_write_path: pass
queue_consumers: pass
error_rate: within_baseline
synthetic_customer_read: pass
Choose probes from the failure mode. A queue format change needs a producer and consumer check. A permission change needs an allowed action and a denied action. A feature flag change needs evidence that every relevant reader and writer follows the restored state.
These checks belong in the Claude Code review packet beside the patch, test evidence, boundary events, and approval record.
save a compact rollback rehearsal record
The reviewer should not need to read a full terminal transcript. Preserve the raw evidence, then summarize it on one page:
rollback_rehearsal:
change_id: customer-status-v2
forward_commit: 91c2f40
recovery_target: 3d5a9be
target_verified: true
external_effects_covered: 3/3
compatibility_probe: pass
duration_minutes: 6.42
budget_minutes: 10
post_recovery_checks: pass
rehearsed_at: 2026-08-08T08:10:37Z
evidence_refs:
- restore-log-sha256:4a17...
- probe-results-sha256:9c02...
decision: rollback_path_verified
Expire this record when the code, schema, tool configuration, deployment artifact, or recovery environment changes. Recovery evidence belongs to a specific change and state. It is not a permanent property of the project.
My approval rule is straightforward: name the target, rehearse the real restore path in isolation, cover every external effect, test compatibility, meet the recovery budget, and verify the recovered service. If one check is missing, the rollback plan is still a draft.
Claude Code: Building Production Agents That Actually Work connects rollback rehearsals with MCP permissions, effect receipts, evals, traces, and review packets for production coding-agent work.