Deploys, migrations and safe releases
Most outages start with a change you made on purpose. This is how you make that survivable.
What problem does a staged rollout solve?
People design for hardware failure, which is rare, and skip the most common trigger of downtime: someone shipped a change. Redundancy does nothing about a bug deployed to every machine at once.
The interesting question is not "how do you deploy" but "how do you make a bad change cheap": cheap to notice, cheap to stop, and cheap to undo.
What a staged rollout costs you
Every concept in here states a cost. A technique with no price is a technique you have not understood yet — and naming the cost out loud is most of what separates a senior answer from a junior one.
When to reach for a staged rollout, and when not to
Use it when
- Any system with an availability target. The deploy path is part of that number.
- Any schema change on a table big enough that people will notice.
- Whenever a follow-up asks how you ship without downtime, or how you undo a bad release.
Do not, when
- Do not build an elaborate release pipeline for a service with no users yet. Ship, then add safety as the stakes rise.
- Do not put a feature flag on everything. Each one doubles the number of paths you are not testing.
How a staged rollout actually works
Rolling deploy is the baseline, and the load balancing page covers the mechanics: take an instance out, drain its in-flight requests, update it, wait for the health check, put it back. The property that makes it work is that the new version has to run happily alongside the old one, because for a few minutes both are live. Everything else in this topic follows from that one constraint.
Canary goes further: send a small share of traffic (1%, then 5%, then 25%) to the new version, and compare error rate and latency against the old one before continuing. The value is that a bad release affects 1% of users for five minutes instead of 100% for however long it takes someone to notice. Say what you compare and what stops the rollout automatically, because "we would watch it" is not a mechanism.
Blue-green runs two full environments and switches traffic between them. Rollback is instant, which is its selling point. It costs double the infrastructure during the switch, and it does not solve the database, which is shared between both and cannot be switched back.
That last point is the important one: rollback is easy for code and hard for data. Once a migration has changed a schema or rewritten rows, going back is not a matter of redeploying. So the safe pattern is expand and contract, and it is worth being able to recite. Add the new column without touching the old one. Deploy code that writes both and reads the old. Backfill the existing rows in batches. Deploy code that reads the new column, and verify. Only then, in a later release, drop the old column. Five steps, several days, and at every point you can stop and go back.
Some specifics that make migrations survivable. Backfill in batches with a pause between them, because one enormous update locks a table and takes the site down. Adding a column with a default rewrites the table on some database versions (PostgreSQL before 11, for example). Know whether yours does. Adding an index should be done without blocking writes, which most databases support with an explicit option people forget to use. And a migration should be separate from the deploy that uses it, so a slow migration does not hold up a release, and a rollback of the code does not require a rollback of the schema.
Feature flags separate deploying from releasing: the code ships dark, is enabled for staff, then a percentage, then everyone, and switching it off does not need a deploy. Two disciplines keep them from becoming the problem. Flags need an owner and a removal date, or you accumulate hundreds and no combination is tested. And a flag that changes data shape is not a safe flag. Turning it off later leaves rows written in the new shape.
Finally, the rollback decision should be automatic and boring. Define, in advance, the error rate or latency that stops a rollout, and wire it up. An incident where someone has to decide whether to roll back, while being asked for updates, is an incident that lasts twice as long.
- Promising four nines and never mentioning how you deploy.
- A migration that renames or drops in the same release that stops using the column.
- Backfilling two billion rows in one statement.
- Feature flags with no owner and no removal date.
- A rollback plan that depends on a human noticing and deciding.
The a staged rollout follow-up an interviewer will ask
"You need to rename a column on a table with two billion rows, with no downtime. How?"
Never as a rename, because a rename breaks every running instance of the old code the moment it lands, and during any rolling deploy old and new code are live at the same time. So it is expand and contract, over several releases. First, add the new column, nullable, with no default so the table is not rewritten. That is deployable on its own and changes nothing. Second, deploy code that writes both columns and still reads the old one. Now every new row is correct, and the old code that is still running is unaffected. Third, backfill the existing rows in batches, a few thousand at a time with a pause between them, so I never hold a long lock or saturate replication; this is a background job I can stop and resume, and I track progress. Fourth, once the backfill is complete and verified (I would compare counts and spot-check rows rather than assume), deploy code that reads the new column. At this point I can still roll back, because the old column is still being written. Fifth, in a later release, stop writing the old column, and finally drop it. The costs I would name: one logical change became five deploys over several days, the table carries duplicate data in the middle, and there is a window where a bug in the dual-write path silently corrupts the new column. So I would add a check comparing the two columns during the backfill, and drop the old column only when that has been clean for a while.
Your turn
Redundancy protects you from a machine dying. What does it not protect you from, and what does instead?
Learn it from the source
This page gets you interview-ready. These take you to where the idea is actually specified or measured — read them when you want the real depth.
- Martin Fowler — BlueGreenDeployment ↗The pattern, including the database problem everyone skips.
- Google SRE Workbook — Canarying Releases ↗Choosing canary size and duration so the signal is real.