Schema evolution: expand, migrate, contract
Keep mixed application versions working while a schema changes, a backfill runs, and readers move to the new representation.
18 min7 steps
By the end you can
- Distinguish backward and forward compatibility during a mixed-version rollout
- Order an expand/contract migration without breaking old instances
- Explain how dual writes and backfills avoid gaps and stale overwrites
- Choose verification and rollback gates before contracting a schema
Two versions share one database
Direct rename
- Old app reads full_name
- Column disappears
- Old app fails
Expand first
- Keep full_name
- Add display_name
- Both versions can run
An online rollout has mixed versions. Adding a nullable column can preserve old callers; renaming a column they still use cannot. Inventory queries, jobs, exports and rollback binaries, not just the newest service.
Name the reader and the writer
Backward compatible reader
- New reader accepts old data
- Example: absent optional field
Forward compatible reader
- Old reader accepts new data
- Example: safely ignores unknown field
Here, backward compatibility means a new reader accepts old data; forward compatibility means an old reader accepts new data. Both matter during coexistence. Optional fields help only if readers tolerate absence and unknown fields. SQL queries and wire formats have different rules.
Keep both representations current
Deploy writers that maintain both columns before backfilling. A local transaction can keep the pair atomic. Independent stores cannot share that guarantee automatically: use durable change delivery, retry and reconciliation. Two best-effort writes can diverge after the first succeeds.
Do not overwrite a newer value
t0
Backfill reads version 7
t1
User commits version 8
t2
Backfill must not overwrite version 8
Backfill in resumable, throttled batches with idempotent retries. Use a version check, conditional update, or another database-specific synchronization protocol so a stale snapshot cannot overwrite a live update. A completed scan is not proof of convergence.
Expand before switching readers
1
Add compatible schema
2
Deploy writers maintaining both forms
3
Backfill and verify convergence
4
Switch readers; observe
5
Retire old dependencies; contract
Keep the old representation valid through the rollback window. Reader cutover needs evidence: matching values, acceptable lag and error rates, and no unsupported old writers. Contract only after old consumers and the rollback dependency have been retired.
Order the migration, passing each gate before moving on.
- 1Backfill historical rows and verify convergence
- 2Switch reads and observe through the rollback window
- 3Add the new column without removing the old one
- 4Deploy writers maintaining both columns everywhere
- 5Retire old consumers, then remove the old column
A green deploy is not the final gate
Insufficient
- New app passes tests
- One canary works
Required evidence
- All consumers inventoried
- Backfill reconciled
- Rollback window closed
- Old access absent
A destructive contract step changes rollback options. An old binary that needs a dropped column is no longer a safe rollback. Keep backups and a recovery plan, but restoring data is not an instant online rollback.
Check your mastery
Mastery
80%
4 of 5 fresh questions
Try a fresh variant, then score at least 80% on the mastery check. Wrong answers link back to the teaching step.
Loading question…
Mastery check
5 questions on fresh variants. One attempt each, no time limit. Score 80% or more to master the lesson.
Answer this step to continue.