Skip to content

Schema Migration

Four rules: backward-compatible, staged, tested in staging, reversible by default. A migration that breaks one rule is not a migration — it is an outage scheduled in advance.

Owners: Developer, Tech Lead Phase it lives in: How We Build (Volume IV) The corpus principle this enacts: Rollback possible is not. A confirmed time is.

Where it lives in the chain

How to do this

The four rules, every migration:

  1. Backward-compatible — the migration runs against running code. The old code must still work after the migration runs. Adding a column is fine; renaming one is not — split it into add + dual-write + backfill + cutover + drop.
  2. Staged — runs in devstagingproduction, with a soak window in each. The JWT outage shipped because staged-deploy was skipped.
  3. Tested in staging — the migration is executed against staging data and verified. Not dry-run. Not "should be fine."
  4. Reversible by default — every migration has a tested down. A migration whose down was never run is a migration that cannot be rolled back.

What good practice looks like

The PR contains: the up, the down, the test that runs both, and the soak time named in the description. The down is run in CI against a fresh database state — if up then down doesn't produce the original schema, the build fails. The migration file references the story (GR-204_add_grading_shortcut_event.sql) so the postmortem trail is intact.

A migration that can't be reversed is a one-way door. Some are necessary — but they are named as one-way doors in the ADR, planned for, and reviewed at the change advisory board, not done casually.

200apps · How We Work · NWIRE