Module 08 · Lesson 04

Shipping without breaking the design

Schema migrations are the part of deployment that cannot be rolled back by redeploying.

Intermediate 7 min read Updated 2026-11-02

What you will be able to do

  • Compare rolling, blue-green and canary deployment
  • Run a schema change that is safe for mixed code versions
  • Use feature flags to separate deploy from release
  • Define automatic rollback criteria

Three deployment shapes

StrategyHowRollbackCost
RollingReplace instances in batchesRedeploy old version, minutesCheap, mixed versions live
Blue-greenTwo full environments, switch trafficSwitch back, secondsDouble capacity during deploy
CanarySend 1-5% of traffic to the new versionRoute away, secondsNeeds good per-version metrics

Canary is the default for user-facing services because it limits exposure to a bad build. Rolling is fine for stateless workers. Blue-green is expensive but useful when you need an instant switch — for example a release with a risky dependency upgrade.

The migration problem

During a rolling deploy, old and new code both run. If the new code expects a column the old code does not write, the old instances produce rows the new code cannot read. This is the failure mode that turns a routine release into a data incident.

Expand
Add the new nullable column or table. Deploy code that writes to both old and new shapes but reads the old one.
Backfill
Migrate existing rows in batches with throttling, so the database is not locked for minutes.
Switch reads
Deploy code that reads the new shape, keeping dual writes. Verify with a comparison query.
Contract
Stop writing the old shape, wait out the longest-lived old instance, then drop the column in a later deploy.
day 1  ALTER TABLE users ADD COLUMN email_norm text;   -- nullable, no default rewrite
       app writes both columns, reads email
day 2  backfill 10k rows per minute
day 3  app reads email_norm, writes both
day 5  app writes email_norm only
day 9  ALTER TABLE users DROP COLUMN email;
Some migrations are not reversible
Dropping a column or rewriting a data type destroys information. Deploy it separately, after the code that used it is gone, and keep a backup window. "We'll roll back" is not true for a destructive migration.

Flags and rollback

  • Deploy dark, enable later. Ship the code path disabled, turn it on for internal users, then 1%, then everyone.
  • Name flags with an owner and an expiry. A flag with no removal date is a permanent branch in production.
  • Roll back on data, not vibes. Error rate above baseline, p99 above the SLO, or a business metric dropping for ten minutes triggers automatic rollback.
  • Keep the previous artefact warm. Rollback that requires rebuilding the last image takes longer than the incident allows.

One more principle worth stating in an interview: deploys should be boring. Small, frequent releases with automatic rollback make failures small; large monthly releases make every failure a project. The design consequence is that the pipeline is part of the architecture, not an afterthought.

How to close the production discussion
"Deploys are canary with automatic rollback on error rate. Schema changes follow expand-contract so old and new code can coexist, and risky behaviour is behind a flag that can be turned off without a deploy."

Worth remembering

  • During any deploy, two versions of the code are running — every change must tolerate that.
  • Never rename or drop a column in one step; expand, migrate, contract.
  • A flag is a runtime decision; a deploy is a code decision. Keeping them separate shrinks the blast radius.
  • Rollback needs a metric threshold, not a human noticing.

Checkpoint

Answer all 3 questions to finish the lesson.
1. Why can't you rename a column in a single deploy?
Mixed versions during a rolling deploy mean both names must work at once. Expand-contract resolves this over several releases.
2. Which deployment strategy exposes the smallest share of users to a bad build?
Canary limits traffic to a small percentage, so a regression affects few users before it is detected.
3. What is the correct order for a destructive schema change?
The code must stop depending on the column before it disappears, otherwise running instances break mid-deploy.