DevOps Practitioner · Module 6: Safe delivery and release engineering
Schema Changes Without Downtime: Expand and Contract
Code and schema change on different clocks. Expand adds the new shape without removing the old, both versions run against both shapes, then contract removes what nobody reads. Skipping a phase is how downtime happens.
10 min reading
Objectives
- Explain why simultaneous code and schema changes break running versions
- Apply expand (add without removing) then migrate, then contract
- Keep old and new code compatible with both schema shapes during rollout
- Verify each phase with both versions serving before moving on
Why this matters
A deploy renames a database column and ships the code that reads the new name. During the rolling update, old pods query the new column and new pods query the old one; both fail against the single renamed reality. The application was down from the first pod until the last, and the rollback cannot help because the schema change already ran. Code and schema are two systems deployed as one event, and the event breaks every mixed-version moment the rollout necessarily contains.
Concepts
Expand and contract separates the clocks. Expand adds the new shape alongside the old: new column added, old column kept; new field optional, old field still written. Both code versions work because both shapes exist. Migrate moves the data: backfill the new shape from the old, dual-write during the transition. Contract removes the old shape only after every running version reads the new one: old column dropped, old field unread. Each phase deploys and verifies independently; the L42 lab runs all three on a fixture app.
Compatibility rules per phase keep the mixed moments safe. During expand, new code must tolerate the old shape (new column may be empty on old rows). During migrate, writes go to both shapes or the backfill is fiction. During contract, no running code may reference the old shape; search every caller, including the batch jobs and reports everyone forgets, before dropping anything.
Verification is version-shaped. After expand, old and new code both pass against the widened schema. After migrate, reads from the new shape match the old. After contract, the suite passes with the old shape gone. A phase whose verification is skipped pushes its risk into the next phase, where it arrives disguised as someone else's bug.
Worked example
A fixture app with a users table renames handle to display_name. Expand adds display_name while code writes both and reads handle. Migrate backfills and switches reads. Contract drops handle after proving no caller references it. Mixed-version traffic serves correctly through all three phases because both shapes always cover both code versions.
Common wrong move
Renaming in one migration with the code change in one deploy. It works in staging (single version, no mixed moment) and breaks in production (rolling update guarantees mixed moments). The staging pass is not evidence; mixed-version traffic is the test.
Quick check
An optional 4-question self-check. Answers never leave your device, are not stored, and never count toward any assessment.
Lesson feedback
No published feedback yet.
Log in and complete the lesson to leave feedback.
Exercise
On a fixture app, rename a field through expand, migrate and contract with both code versions serving at each phase, and quote the verification per phase.
Pass criteria
The record shows three phase deploys, mixed-version traffic serving at each, and the final schema with no references to the old shape.