Runbook: Database Migration Rollback
Prisma migrations are forward-only by default. This runbook covers the procedure for backing out a migration that went wrong, using the hand-written rollback.sql convention from Phase 3E.
Prerequisites
- Destructive migrations (DROP COLUMN, ALTER TYPE, DATA DELETE) ship with a
rollback.sqlin the same migration directory - You've verified the target rollback SQL reproduces the prior schema on a scratch DB first
Decision tree
Is the migration already applied to production?
- No → just revert the commit and deploy. No SQL needed.
- Yes → proceed below.
Is the migration additive-only (ADD COLUMN, CREATE TABLE)?
- Yes → rollback is optional. Deploy the previous app version; the new schema is a superset, so the old code ignores the extras. File a cleanup ticket to drop the unused columns later.
- No → you need the rollback SQL.
Procedure
1. Pause writes
bash
# Set the API to read-only mode (flag-flag based — requires app support)
kubectl set env deployment/spade-api READ_ONLY_MODE=trueIf read-only mode isn't implemented yet, scale down to 0 replicas and accept the downtime.
2. Back up the current state
bash
pg_dump --format=custom --file=/tmp/pre-rollback-$(date +%s).dump $DATABASE_URLThis is the last-resort restore if the rollback SQL has bugs.
3. Apply the rollback SQL
bash
psql $DATABASE_URL -f packages/db/prisma/migrations/<timestamp>_<name>/rollback.sqlRead the output carefully. Any error = stop and investigate.
4. Update the Prisma migration history
The _prisma_migrations table tracks which migrations are applied. After a rollback, mark the migration as rolled back:
bash
DATABASE_URL=... pnpm --filter @breezycorp/db exec prisma migrate resolve \
--rolled-back <migration-name>5. Deploy the previous app version
bash
kubectl rollout undo deployment/spade-api
kubectl rollout undo deployment/spade-worker6. Resume writes
bash
kubectl set env deployment/spade-api READ_ONLY_MODE-Verify /health/ready returns 200.
7. Write a post-mortem
Required for any migration rollback. The template lives in docs/templates/postmortem.md (TODO).
What if the rollback SQL is wrong?
- Don't panic, don't run more SQL
- Restore from the pre-rollback dump taken in step 2
- Restore from the latest nightly snapshot if the pre-rollback dump is also corrupt (see
runbooks/restore-drill.md) - Declare an incident and escalate
Prevention
- Test the rollback locally before merging any destructive migration
- Reviewer requirement: PRs with destructive migrations need a second reviewer who has run the rollback on a scratch DB
- CI check:
db:migrate:diffensures forward migrations apply; we don't yet automate rollback testing — TODO
Known gotchas
- Column drops with data: the rollback SQL can't recreate lost data. Destructive migrations must either be preceded by a data-export migration OR never merged without explicit data loss approval
- Foreign key direction changes: if a rollback changes FK direction, ordering matters — drop FKs first, then alter, then re-add
_prisma_migrationsstaleness: ifmigrate resolve --rolled-backis skipped,migrate deploywill try to apply the migration again on the next deploy
Related
docs/slo.md§ Data Durability (RPO/RTO)runbooks/restore-drill.md— worst-case recoveryCLAUDE.md§ Database Migration Workflow