Deployment strategies: recreate, rolling, blue-green, canary, shadow
Friday, 18:00: a deploy replaces all twelve booking servers at once, and checkout is down until 18:09, the busiest hour of the week. The code change itself was correct.
Some releases will be bad. Here are five ways to swap old code for new so a bad one stays small and short.
- Problem
The hotel booking API ships 10 times a week. Every deploy is a moment when old code stops and new code starts, and roughly one release in twenty has a bug the tests missed.
- Why the simple system fails
Stopping everything and starting the new version causes downtime; replacing everything at once means a bad release hits 100% of guests before anyone notices; and a schema change can make rolling back impossible.
- Engineering concept
Control exposure: decide how much traffic the new version gets, when, how you detect a problem, and how fast you can undo it. Keep the database compatible with both versions.
- Technology
Load balancers and health checks, rolling updates, blue-green environments, canary routing with automated analysis, traffic mirroring, expand/contract migrations.
- Trade-offs
Smaller blast radius and faster rollback cost extra capacity, slower rollouts, routing infrastructure, and the discipline of running two versions side by side.
The problem
2 min- Omar · On-call SREThe 18:00 deploy stopped all 12 booking instances at once. Ready again at 18:09: about 165 bookings lost.
- Omar · On-call SREA startup migration renamed
guest_nametoguest_full_nameon a 40-million-row table and locked it. - You · Engineer at StayHubWhy not replace them a few at a time?
The big idea
4 minChange the database in steps both versions understand; then any rollout is possible.
"Never deploy at 18:00 on a Friday" only moves the outage to a quieter hour. Every deploy answers one question: how does traffic move from old code to new?
How it works
5 minEach strategy trades downtime, rollback speed, cost and : how many guests a bad release reaches.
- 1Recreate
Stop all, start new. Downtime every time; a bad release hits 100%. Friday's choice.
- 2Rolling update
A replaces a few servers at a time; old and new run together. No downtime, but no comparison: a quiet bug rolls to 100%.
- 3Blue-green
runs a full second copy, then flips all traffic. Undo in seconds, but 100% of guests get the new version at once.
- 4Canary
A sends a small slice (5%) of real traffic to the new version, compares it with stable, then 25%, then 100%. A bad release reaches few guests.
- 5Expand / contract
changes the database in releases: add the new column, write both and backfill, read the new one, drop the old one later. Every step works for old and new code.
Watch a decision being made
4 minYou plan the rename properly; Lina checks
Rename the column on a 40-million-row table with zero downtime, and roll out so a bad release reaches at most 5% of guests.
- 1Split the schema change
Release 1: add the column (instant). 2: write both, backfill in batches of 10,000. 3: read the new one. 4: drop the old.
sql-- Release 1 (EXPAND) ALTER TABLE bookings ADD COLUMN guest_full_name text; -- Release 2: backfill in small batches UPDATE bookings SET guest_full_name = guest_name WHERE id BETWEEN $1 AND $2 AND guest_full_name IS NULL; -- Release 4 (CONTRACT), only when no old code remains ALTER TABLE bookings DROP COLUMN guest_name;Why: Running code always finds its columns; rollback never meets a missing one.
- 2Run migrations outside app startup
A separate pipeline step with
SET lock_timeout = '3s'.Why: Friday's rewrite at boot froze every instance; a time-limited step fails fast instead.
- 3Choose canary steps and define "worse"
5% for 10 min, 25% for 15 min, 100%. Fail if 5xx > 2× stable, p95 > 1.5× stable, or bookings/min per instance < 90% of stable. At least 500 canary requests per step.
Why: At ~1,000 req/min, 5% gives 500 requests in 10 minutes. A business metric catches quiet bugs.
- 4Rehearse the rollback
On a quiet morning, deploy release 2, roll back to 1, confirm nothing is lost.
Why: A rollback never run is a hope.
In real life
3 minAWS Elastic Beanstalk: blue/green
- Publicly documents deploying to a second environment, then swapping URLs
- Rolling back is swapping back: seconds
- At the swap, every user moves at once
Fits when instant, complete rollback matters most.
Netflix: automated canary analysis
- Has publicly described Kayenta (open-sourced with Google) scoring canaries in Spinnaker
- New versions are compared with a baseline on many metrics
- Needs good metrics and routing
Fits frequent releases where quiet bugs are the main risk.
What goes wrong
3 minCostly bugs return 200 OK.
Compare with stable on errors, latency and a business metric (average tax per booking).
Old code breaks at once; rollback becomes impossible.
Expand/contract over several releases.
One timeout looks like 10% errors; a real bug can show none.
A minimum sample per step: wait for traffic, not just time.
Trade-offs
3 min| Strategy | Blast radius | Rollback | Extra capacity |
|---|---|---|---|
| Recreate | 100%, with downtime | Redeploy (minutes) | None |
| Rolling | Grows to 100% | Another rollout | +1 to +25% |
| Blue-green | 100% at the flip | Flip back (seconds) | +100% during switch |
| Canary | 1–25% until promoted | Weight to 0 (seconds) | +5 to +25% |
- Karim · CTOBlue-green. Flip, and if anything's wrong, flip back in five seconds.
- Omar · On-call SREFive seconds after someone notices. The tax bug went unnoticed for three hours, on 100% of guests.
- Karim · CTOA canary needs per-version metrics, a router, rules. That's weeks.
Your job as tech lead
4 minClaude Code can write the code for this. You still own the decisions, and you have to know what good looks like to accept or reject its work.
- Which strategy each service uses: canary for Booking, not the old recreate script.
- What the canary is judged on: 5xx, p95 and a business signal. At least 500 requests per step.
- How schema changes ship: expand/contract, never a rename in the same release as its code.
We need to replace bookings.guest_name with guest_full_name in the Booking service. Last time a one-step rename took checkout down for 9 minutes. Plan expand/contract across separate releases: add the column, dual-write, backfill the 40-million-row table in small batches without long locks, switch reads, stop writing the old column, then drop it. Each release must work with the one before it. Give me a numbered list of releases with the migration, code change and rollback for each. No code yet. Deploys go through the canary: 5%, 25%, 100%.
- Claude says, for each release, that it works with the previous version.
- No migration drops or renames a column alongside the code that depends on it.
- The backfill says its batch size, duration and locks.
- A destructive migration at app startup.
- "We can always roll back" while dropping or renaming a column.
- Judging a release on readiness checks and status codes only.
Check yourself
2 minNext episode: the new cancellation flow has been merged and deployed for a month, and Maya still can't switch it on for just one hotel.
Mark it complete to update your skill map.
Go deeper (optional)You don't need this to understand the lesson. Open it when a project needs the details: Technical deep dive, Interactive simulation, Visual diagram, Analogy, Check your understanding, Practice ladder, Explain it yourself.
Technical deep dive
You plan the rename properly; Lina checks
Rename bookings.guest_name to guest_full_name on a 40-million-row table, with zero downtime, and roll the booking service out so a bad release reaches at most 5% of guests.
- 1Split the schema change
Release 1:
ADD COLUMN guest_full_name text(nullable, instant). Release 2: write both columns; backfill in batches of 10,000 rows. Release 3: read the new column. Release 4: drop the old one.Why: At every moment, the running code (old or new) finds the columns it needs. Rollback never meets a missing column.
- 2Run migrations outside app startup
Migrations run as their own pipeline step before the deploy, with a lock timeout (
SET lock_timeout = '3s'), never inside each instance's boot.Why: Friday's 40M-row rewrite at boot held a lock and delayed every instance. A separate, time-limited step fails fast instead of freezing checkout.
- 3Choose canary steps
5% for 10 minutes, then 25% for 15 minutes, then 100%, each step gated by analysis against the stable version.
Why: Small first step, enough time and traffic to judge. At ~1,000 req/min, 5% gives 500 requests in 10 minutes.
- 4Define "worse"
Fail if 5xx ratio > 2× stable, p95 > 1.5× stable, or bookings per minute per instance < 90% of stable; require ≥ 500 canary requests per step.
Why: Health checks catch crashes, not wrong answers. A business metric catches the quiet bugs; a minimum sample avoids judging on noise.
- 5Rehearse the rollback
On a quiet morning, deploy release 2, roll back to release 1, and confirm the app works and no data is lost.
Why: A rollback that's never been run is a hope. Lina's check: also confirm the backfill job stops cleanly when rolled back.
-- Release 1 (EXPAND): instant, nullable, no table rewrite
ALTER TABLE bookings ADD COLUMN guest_full_name text;
-- app v1 writes BOTH columns, still reads guest_name
-- Release 2 (MIGRATE): backfill in small batches, short locks
UPDATE bookings SET guest_full_name = guest_name
WHERE id BETWEEN $1 AND $2 AND guest_full_name IS NULL;
-- app v2 reads guest_full_name, still writes both
-- Release 3: app v3 stops writing guest_name (wait until no v2 instance remains)
-- Release 4 (CONTRACT): only now remove the old shape
ALTER TABLE bookings DROP COLUMN guest_name;Interactive simulation
Visual diagram
A canary release of booking-api v42 with automated analysis. Click components or start the walkthrough.
Analogy
- Changing a restaurant's menuClose for an hour, reopen with the new menuDeploying new codeRecreate: brief downtime
- Changing a restaurant's menuSwap menus table by tableDeploying new codeRolling: two versions run together
- Changing a restaurant's menuOpen a second dining room, move everyoneDeploying new codeBlue-green: flip all traffic
- Changing a restaurant's menuGive the new menu to one table in twentyDeploying new codeCanary: a small slice of users
- Changing a restaurant's menuCook every order twice, chef tastes the copyDeploying new codeShadow: copied traffic, hidden answers
- Changing a restaurant's menuReorganise the pantry so both menus workDeploying new codeDatabase changes both versions can use
Where the analogy breaks: a guest can tell you the dish is bad. Software users mostly just leave. That's why you need numbers, not complaints, to judge a release.
Check your understanding
bookings.price (integer euros) with bookings.price_cents while deploying with rolling updates:- 1.Drop the
pricecolumn - 2.Deploy code that reads
price_cents(still writes both) - 3.Add nullable
price_centscolumn - 4.Deploy code that stops writing
price - 5.Deploy code that writes both columns, still reads
price - 6.Backfill
price_centsfor old rows in batches
Practice ladder
Trace a canary at a quiet hour
Estimation Warm-upThe booking service gets 1,000 requests per minute at peak and 200 per minute at 07:00. The canary's first step is 5%, and the analysis needs at least 500 canary requests.
- How long must the first step last at peak, and at 07:00?
- A release with a bug that fails 4% of requests goes out at 07:00 with a fixed 10-minute first step. How many canary requests and failures does the analysis see? Can it judge?
- What rule would you change?
Order the price_cents migration
Design PracticeStayHub wants to replace bookings.price (whole euros) with bookings.price_cents, deployed with canary releases. Lina's plan has gaps. Fill in releases 2 and 4.
- Release 1:
ADD COLUMN price_cents integer(nullable). App writespriceandprice_cents; readsprice. - Release 2: ___
- Release 3: App stops writing
price. Wait until no release-2 instance is running. - Release 4: ___
For each, say why it's safe if the release is rolled back.
Roll out the new cancellation fees
Design PracticeA release changes how cancellation fees are computed:
- Code changes in the booking service (12 instances, ~1,000 requests/min at peak).
- A new column
cancellation_policy_versiononbookings. - A wrong fee doesn't error: the guest just pays too much or too little.
- About 300 cancellations happen per hour.
Design the rollout: migration steps, strategy and steps, what the analysis compares, and how you'd undo it.
Ban Friday deploys?
Decision StretchAfter the 18:00 outage, Karim proposes: no deploys after 15:00 on Fridays, and none on weekends.
Facts: ~10 releases a week; Friday afternoon and Sunday evening are the two busiest booking periods; the booking service now has expand/contract migrations and canary releases with automatic rollback; on-call is staffed all weekend.
Write a short decision: "We chose X over Y because…, accepting the risk of…".
Explain it yourself
Explain to a teammate why your team uses canary releases and expand/contract migrations instead of just running a rolling update after the migration script.