How to Make Backward-Compatible Schema Changes
A migration is safe to deploy when the version of the app already running keeps working against the new schema without changes.
During a rolling deploy the old and new app versions run at the same time, so every migration must be readable and writable by both. Additive changes (new nullable column, new table, new index) are compatible; removals and tightened constraints are not until code stops depending on them.
Compatible vs breaking
| Change | Backward compatible? |
|---|---|
| Add nullable column | Yes |
| Add table or index | Yes |
| Drop column still read by old code | No |
| Add NOT NULL without default | No |
| Rename column in place | No |
Turn a breaking change additive
migration.sql
-- breaking: old code inserts without status
-- ALTER TABLE orders ADD COLUMN status text NOT NULL;
-- compatible: nullable now, tighten later once code always sets it
ALTER TABLE orders ADD COLUMN status text;Gotchas
- The compatibility target is the app version still running, not the new one you are deploying.
- A required NOT NULL can be reached later with a backfill plus a validated constraint.
Related guides
How to Add a Column Safely in a MigrationAdd a column in a zero-downtime migration by making it nullable or giving it a constant default, avoiding the…
How to Do Zero-Downtime Migrations With Expand-ContractShip zero-downtime schema changes with the expand-contract (parallel change) pattern: expand the schema, migr…