The scariest sentence in enterprise software is "we'll do the migration over the weekend." It assumes one big-bang moment where everything flips and nothing breaks. It rarely holds.
Expand, migrate, contract
We run schema and system changes in three phases. Expand: add the new shape alongside the old. Migrate: dual-write and backfill until the new path is trusted. Contract: remove the old shape once nothing reads it.
// 1. expand: write to both
await Promise.all([legacy.save(row), next.save(row)]);
// 2. migrate: backfill + verify parity
// 3. contract: drop legacy once reads are 100% on nextReversibility is the feature
Every step is independently deployable and reversible. If a phase misbehaves, you roll back one small change, not the whole cutover. That is what turns a scary weekend into a boring Tuesday.
The goal is no single moment where everything depends on everything working.
