System designCore10 min

Deployment strategies

Most outages start with a change. A deployment strategy decides how a new version replaces the old one: all at once, a few servers at a time, onto a second copy of the fleet, or to a small slice of traffic first. The good ones share three habits: expose the change to as few users as possible at first, detect trouble automatically, and make going back fast and safe.

1 · The problem

Most outages start with a change, so how you change matters

An airline's check-in API runs on 120 servers in three regions and handles about 30,000 check-ins an hour. Version 4.8 contains a bug: check-in fails for passengers traveling with an infant, about 2% of all check-ins. Nobody knows that yet. The question is how many passengers find out before the team does.

Roughly 70% of outages are due to changes in a live system, and three practices limit the damage: progressive rollouts, quick and accurate detection, and safe rollback. Deployment strategies are different mixes of those three:

Strategy

How the new version arrives

Rollback

Recreate

Stop every old server, then start new ones

Another full redeploy; downtime both ways

Rolling

Replace servers a batch at a time

Roll the old version back the same way

Blue-green

Start a full new fleet, then switch all traffic

Switch traffic back

Canary

Send a small share of traffic to the new version, compare, then widen

Send that share back

Waves

Canary-like steps across hosts, zones and regions

Automatic, on alarms

Recreate is the only one with planned downtime, and the only one where old and new versions never run at the same time. Every other strategy has a period when both versions serve traffic side by side, which shapes everything else on this page.

2 · Rolling

A rolling update replaces a few servers at a time, gated on health

A rolling update replaces old servers with new ones in batches, keeping enough of them serving at all times. Kubernetes Deployments do this by default with two dials: maxUnavailable, how many may be down, and maxSurge, how many extra may be started, both 25% unless set. For the 120 check-in servers, that means at least 90 serving and at most 150 running during the rollout.

A new server only takes traffic once it says it is ready:

Each step waits for health. A version that never becomes ready stalls the rollout instead of replacing the fleet; Kubernetes reports it as stalled after progressDeadlineSeconds.

Draining matters as much as readiness. A load balancer stops sending new requests to a server being removed but lets in-flight ones finish; AWS load balancers wait 300 seconds by default before completing the removal (Load balancing).

Rolling is cheap, needing only a few extra servers, and is the default for good reason. Its weaknesses: readiness checks catch crashes, not wrong answers, so the infant bug sails through; by the time anyone notices, much of the fleet may run 4.8; and rolling back is another rolling update, minutes long. Kubernetes keeps the last 10 old versions by default so kubectl rollout undo has something to return to.

3 · Blue-green

Blue-green switches all traffic between two complete fleets

Blue-green deployment keeps two production environments. Blue runs 4.7 and serves everyone; green gets 4.8 and is tested while idle. Then the router switches all traffic to green in one step. If anything goes wrong, it switches back:

PassengersRouterone switchBlue: 120 servers4.7, servingGreen: 120 servers4.8, tested, idleShared databasemust suit both versionsall traffic nowafter the switch

Rollback is the same switch in reverse, seconds instead of a redeploy. The price is a second full fleet, and a database both versions can use.

Three catches:

  • Double capacity during the switch, or the time to build it.
  • The database is usually shared. Schema changes must work for both versions, so they are deployed separately from the application, ahead of it (Zero-downtime migrations).
  • All or nothing. The switch exposes every passenger to 4.8 at once. Rollback is fast, but the infant bug still hits 2% of everyone until someone notices.

4 · Canary

A canary exposes a small share of traffic and compares it with the rest

A canary is a partial, time-limited deployment of a change, plus its evaluation. A small part of production, the canary, runs 4.8; the rest, the control, keeps 4.7; and the two are compared over the same period. If the canary looks no worse, the rollout widens; if it looks worse, it stops.

The share is the point. With 5% of check-ins going to the canary, the infant bug in its first 30 minutes affects far fewer passengers than a full release would have:

Released to everyone
300 failed check-ins
5% canary
15 failed check-ins

30,000 check-ins an hour × 0.5 hours × 2% = 300 failures for a full release; × 5% on the canary = 15. The canary's own error rate still shows the bug clearly: 2% against the control's near zero.

The comparison must be fair to be useful:

  • Compare canary and control at the same time. A before-and-after comparison mixes the change with whatever else changed that hour, such as traffic or a dependency.
  • Pick metrics tied to what users feel, such as error rate and latency by version. A metric that swings wildly with no user impact makes a poor canary signal.
  • Run one canary at a time. Overlapping canaries contaminate each other's signal and make the system's state hard to reason about.

Tools encode the steps. AWS CodeDeploy's Canary10Percent5Minutes shifts 10% of traffic, then the remaining 90% five minutes later; its linear options move 10% every one or three minutes. Argo Rollouts lists steps such as setWeight and pause, and can run an analysis in the background that aborts the rollout if it fails. Canary and blue-green also combine: keep the old fleet whole, move traffic in steps, and switch back at any point.

5 · Waves

At scale, a release moves in waves with waits in between

The airline runs three regions, so a bad release should never reach all three at once. Amazon's pipeline does this, and it generalizes the canary to the whole fleet:

  1. One box

    A single server, container, or small share of function invocations gets the new code first. Then a bake of at least one hour.

  2. Wave 1: a low-traffic region

    One Availability Zone or cell at a time. Then at least 12 hours of bake.

  3. Wave 2: a high-traffic region

    Real load, still one zone at a time.

  4. Later waves

    Several regions in parallel, but each still one zone or cell at a time, with two to four hours of bake after each wave.

During every step, alarms watch fault rates, latency at p50, p90 and p99, resource use, log errors, failing health checks and synthetic canary tests. If any fires, the deployment rolls back automatically, to the previously deployed package. Deployments also run only in time windows when on-call engineers respond quickly, which excludes nights, weekends and holidays.

Zones and cells are natural wave boundaries because they are already failure domains: a bad release confined to one zone is an outage the rest of the system is built to survive (Redundancy & failure domains, Bulkheads). The bake times look slow, but some bugs only appear at a certain hour, on a certain load, or after a cache expires.

6 · Rollback

Rollback only works if the old version can still run

Every strategy above ends with "and if it's bad, go back". That assumes the old version can cope with whatever the new version did while it ran. Suppose 4.8 also changed how boarding passes are stored, from XML to JSON. Servers on 4.8 write JSON; roll back to 4.7, and 4.7 servers meet JSON they can't read. The rollback causes a second outage.

The answer is a two-phase deployment. Split the change into two releases, each safe to roll back:

Phase

Writes

Reads

If rolled back

Before: 4.7

XML

XML

Nothing to undo

Prepare: 4.8

XML

XML and JSON

Safe: no JSON exists yet

Activate: 4.9

JSON

XML and JSON

Safe: 4.8 can read JSON

It is the same idea as expand and contract for databases (Zero-downtime migrations) and compatible schema changes for messages (Schema evolution & encoding): make every reader ready before any writer changes. Amazon tests this directly: its one-box stage runs new code beside old, so a mixed fleet is exercised before the wider rollout.

One more way to make going back cheap: decouple deploying from releasing: ship 4.8 with the infant change behind a flag that is off, then turn it on for a few passengers at a time; turning it off is faster than any rollback (Feature flags & configuration).

7 · In practice

Start small, watch automatically, and keep the way back open

Situation

Strategy

Downtime is acceptable, or versions can't coexist

Recreate

Ordinary stateless services

Rolling, with readiness checks

Instant switch and rollback, capacity to spare

Blue-green

User-facing changes where bugs show in metrics

Canary, compared against a control

Many zones or regions

Waves with bake times and automatic rollback

For the check-in API, the checklist:

  • Never deploy everywhere at once: one box, then a small share, then zones and regions in turn.
  • Gate every step on readiness and on user-facing metrics, compared with the old version at the same time.
  • Roll back automatically on alarms, and keep the previous version ready to run.
  • Make every release safe to roll back: prepare readers before activating writers, and change schemas separately.
  • Drain servers before stopping them.
  • Deploy when people are around to respond, and bake long enough for slow bugs to show.
  • Put risky behavior behind flags, so it can be turned off without a deploy.

Check yourself

5 questions

1. A rolling update of 120 servers uses Kubernetes's defaults. How many servers may be running during the rollout, at most?
2. Readiness checks pass on version 4.8, yet 2% of check-ins fail. Which strategy limits how many passengers hit the bug before it is caught?
3. Why compare the canary with a control running at the same time, not with the old version's metrics from the previous hour?
4. Version 4.8 starts writing JSON that 4.7 can't read. What makes a rollback safe?
5. What does blue-green deployment trade for instant rollback?

Takeaways

Remember this

  • Roughly 70% of outages come from changes; limit exposure, detect problems automatically, and keep rollback fast and safe.
  • Rolling updates replace servers in health-gated batches and are cheap, but readiness checks don't catch wrong answers.
  • Blue-green switches all traffic between two complete fleets: instant rollback, double capacity, and a shared database both versions must suit.
  • A canary sends a small share of traffic to the new version and compares it with a control at the same time; at scale, waves add zones, regions and bake times.
  • A rollback is only safe if the old version can read what the new one wrote: prepare readers before activating writers.