The Database Migration Rule of Three
Three discrete changes for every schema migration: add, dual-write, remove. The pattern that lets you ship migrations without downtime.
The three phases
Schema migrations break in production when add and remove happen in the same change. Splitting into three discrete phases keeps every state of the system serviceable.
- Phase 1: add. New column, table, or index added to the schema; old code still uses the old structure.
- Phase 2: dual-write. New code writes to both old and new structures; old code unchanged; historical data backfilled.
- Phase 3: remove. New code reads and writes only the new structure; old code retired; old structure dropped.
- Phase boundaries. Each phase is a separate deploy; do not collapse them into one PR even when tempting.
Why this works
The three-phase pattern preserves rollback capability at every stage. The migration is reversible until the very last step.
- Zero downtime. Both old and new code can run at every phase boundary; the system stays serviceable.
- Rollback always available. Phases 1 and 2 are reversible with no data loss; phase 3 is the only one-way door.
- Auditable. Each phase is a separate deploy; the team can verify before proceeding to the next.
- Stage gating. Production telemetry between phases catches problems before they compound.
Pitfalls
Three failure modes turn the rule into theatre. All three are upstream of the migration mechanics.
- Skipping phase 2. 'This is a small table' becomes the downtime story; the shortcut is what causes the outage.
- Phase timing. Phase 1 to 2 within hours; phase 2 to 3 over weeks; rushing the gap loses the rollback option.
- Forgetting backfill. New column empty for old rows; queries break unexpectedly when readers assume populated data.
- Cross-service ordering. Multi-service migrations need phase coordination; dual-write may need to span repos.