system-design

Deployment Strategies: Blue-Green, Canary, Rolling

How to replace running software without dropping requests — and how to limit the damage when the new version is broken. Because it will sometimes be broken.

Prerequisites: CI/CD, Load Balancers Time to read: ~16 minutes


The problem

You have version 1 running, serving live traffic. You need to deploy version 2. The naive approach:

❌ Stop v1. Deploy v2. Start v2.

🚨 This causes downtime (the gap while nothing’s running) and is all-or-nothing — if v2 is broken, everyone hits the broken version simultaneously, and you’ve dropped every in-flight request.

The deployment strategies below solve two problems: zero downtime (no gap in service) and limited blast radius (a bad deploy affects few users, not all). Every strategy is a different point on the speed/safety/cost trade-off.


Rolling deployment

Replace instances gradually, a few at a time, while the rest keep serving.

Start: [v1][v1][v1][v1]
Step:  [v2][v1][v1][v1]   ← replace one, wait for it to be healthy
Step:  [v2][v2][v1][v1]
Step:  [v2][v2][v2][v1]
End:   [v2][v2][v2][v2]

Zero downtime, no extra infrastructure (you reuse the same instances), gradual. ❌ 🚨 Two versions run simultaneously during the rollout — so v1 and v2 must be compatible (especially with a shared database — this is exactly the zero-downtime migration constraint). Rollback is slow (you have to roll forward/back instance-by-instance). A bad version does reach some users before you notice.

The default for Kubernetes and most orchestrators.

🚨 Graceful shutdown matters: each instance being replaced must drain (finish in-flight requests, stop accepting new ones) before it’s killed, or the rollout drops requests. → Load Balancers


Blue-green deployment

Run two complete environments — blue (current) and green (new). Deploy v2 to green (idle), test it, then switch all traffic from blue to green at once.

Blue (v1) ← all traffic          Blue (v1) ← idle now
Green (v2) ← idle, being tested   Green (v2) ← all traffic
              ─── switch ───►

✅ 🚨 Instant switch and instant rollback — the old environment (blue) stays running, so if green is bad, flip back to blue in seconds. This fast rollback is the key advantage. ✅ Test the new version fully before any real traffic. ❌ 🚨 Double the infrastructure (two full environments) — expensive. ❌ All-or-nothing — the switch sends everyone to v2 at once; if v2 has a subtle bug that testing missed, everyone hits it (though you can flip back fast). ❌ Database migrations are still tricky (both environments share the database, or you need careful handling).

Best when instant rollback is worth the cost of doubled infrastructure.


Canary deployment

🚨 The safest, and usually the best for high-stakes services. Route a small percentage of traffic to v2, watch it closely, and gradually increase if it’s healthy.

Step 1:  1% → v2, 99% → v1    ← watch error rate, latency for this 1%
Step 2:  5% → v2              ← metrics still good? continue
Step 3:  25% → v2
Step 4:  100% → v2            ← full rollout
(at any step, if metrics degrade → roll back, only that % was affected)

✅ 🚨 Minimal blast radius — a bad version affects 1% of users, not 100%. You catch problems with real production traffic before they hit everyone. ✅ Real-world validation — testing catches what tests can’t (real traffic, real data, real load). ✅ Gradual, controlled, data-driven progression. ❌ Slower (progressive rollout takes time), more complex (traffic splitting, metric monitoring), requires good observability to judge canary health. ❌ Two versions run simultaneously (compatibility, like rolling).

🚨 Automated canary analysis is the mature version: the system automatically compares the canary’s metrics (error rate, latency) to the baseline and auto-promotes or auto-rolls-back based on thresholds. Tools like Argo Rollouts, Flagger, and Spinnaker do this. Mentioning it is a strong signal.

🎙️ “For a high-stakes service I’d use canary deployment — route 1% of traffic to the new version, watch error rate and latency on real traffic, and progress to 5%, 25%, 100% only if metrics stay healthy. A bad version hits 1% of users, and automated canary analysis can promote or roll back based on the metrics.”


The comparison

  Rolling Blue-Green Canary
Downtime Zero Zero Zero
Extra infrastructure None 2× (double) Small (canary instances)
Rollback speed Slow (instance by instance) Instant (flip back) Fast (only % affected)
Blast radius of a bad deploy Some users All users (but flip back fast) Minimal (1%)
Real-traffic validation Partial No (test before switch) Yes
Complexity Low Medium High (traffic split + metrics)
Best for Default, most services Instant rollback needed High-stakes, gradual validation

Shadow / dark deployment — send v2 a copy of real traffic but don’t use its responses (serve v1’s). You validate v2 against real production load with zero user risk. 🚨 Great for testing performance and correctness of a risky change (and the technique behind strangler-fig migrations). Cost: v2 processes real traffic (double compute), and you must handle side effects (v2 must not actually charge the card).

A/B testing — route different users to different versions to compare business outcomes (conversion, engagement), not just technical health. Uses the same traffic-splitting mechanism as canary but for a different purpose (product experimentation, not deploy safety). Often built on feature flags.

Feature flags — 🚨 the most flexible option, and often the best. Deploy the code with the feature off, then turn it on for a percentage of users independently of deployment — and turn it off instantly if it breaks, with no rollback deploy. This decouples deploy from release, which is transformative. → Feature Flags


The universal constraint: version compatibility

🚨 Every zero-downtime strategy has old and new versions running simultaneously (rolling and canary literally, blue-green briefly during the switch and around database changes). This means:

This is why deployment strategy and database migration strategy are deeply linked — you can’t deploy safely if the versions can’t coexist.


Choosing

🎙️ The mature answer often combines them: “Rolling deploy as the mechanism, canary for the rollout (1% then gradual with automated analysis), and feature flags so we can deploy dark and control release independently — turning off a broken feature instantly without a rollback deploy.”


⚖️ Trade-offs

Strategy Gain Cost
Rolling Zero downtime, no extra cost Slow rollback; bad version reaches some users
Blue-green Instant rollback; test before switch Double infrastructure; all-or-nothing switch
Canary Minimal blast radius; real-traffic validation Slower; complex; needs good observability
Shadow Validate on real load, zero user risk Double compute; side-effect handling
Feature flags Decouple deploy/release; instant off Flag management; technical debt if not cleaned up

In the real world


🚨 Interview traps


🎙️ Soundbites


🛠️ Try it

1. Do a rolling deploy and watch compatibility. With 4 instances behind a load balancer, roll out a new version one at a time. Then deploy a version that changes the API response shape incompatibly, and watch requests fail during the rollout while both versions run. That failure is the version- compatibility constraint, felt directly.

2. Blue-green with instant rollback. Run two environments, switch traffic to green, then “discover” green is broken and flip back to blue. Time the rollback — it’s seconds, because blue never stopped. Compare to how long a rolling rollback would take.

3. Canary with metrics. Route 10% of traffic to a new version that has a higher error rate. Watch your monitoring show the canary’s errors while the baseline stays healthy. Seeing the bad canary caught by metrics — while 90% of users are unaffected — is the whole argument for canary.

4. Feature flag rollback. Deploy code behind a flag (off). Turn it on, “discover” it’s broken, turn it off. No deploy, no rollback — just a flag flip in seconds. Compare the speed and simplicity to a deployment rollback.


Check yourself

1. What two problems do zero-downtime deployment strategies solve? **Downtime** and **blast radius**. The naive approach (stop the old version, deploy the new, start it) creates a gap where nothing is serving — downtime — and drops every in-flight request. Zero-downtime strategies (rolling, blue-green, canary) all eliminate that gap by keeping some version serving throughout the transition. Separately, they address blast radius — how many users a *broken* new version affects. The naive approach and blue-green's switch are all-or-nothing (everyone hits the new version at once), while canary and rolling limit exposure (a bad version reaches a small fraction first). The strategies differ in how they balance these along with rollback speed and infrastructure cost: canary minimizes blast radius but is slower and complex; blue-green gives instant rollback but doubles infrastructure and switches everyone at once; rolling is cheap and gradual but rolls back slowly. The right choice depends on how much a bad deploy costs you.
2. What's the key advantage and disadvantage of blue-green deployment? The key advantage is **instant rollback**: because you run two complete environments and the old one (blue) keeps running untouched while the new one (green) takes traffic, reverting a bad deploy is just flipping traffic back to blue — a change that takes *seconds*, with the previous version already warm and running. You also get to fully test green before sending it any real traffic. The key disadvantage is **double the infrastructure** — you must run two complete production environments simultaneously, which is expensive at scale. A secondary disadvantage is that the switch is all-or-nothing: it sends *everyone* to the new version at once, so a subtle bug that testing missed hits 100% of users immediately (though you can flip back fast). Blue-green is the right choice when the value of second-scale rollback justifies the doubled cost — typically a critical service where any prolonged degradation is unacceptable.
3. Why is canary deployment considered the safest strategy? Because it minimizes blast radius while validating on real production traffic. It routes only a small percentage of traffic (start at 1%) to the new version, monitors that slice's error rate and latency against the baseline, and increases the percentage gradually (5%, 25%, 100%) only if the metrics stay healthy — rolling back at any step if they degrade. So a broken version affects at most that small percentage of users for the short time before it's caught, never everyone. Crucially, this validation happens against *real* traffic, real data, and real load, which catches problems that pre-production testing cannot (subtle production-only bugs, performance under real load, edge cases in real data). The mature form — automated canary analysis — has the system compare canary metrics to the baseline and automatically promote or roll back, removing human latency from the decision. The trade-offs are that it's slower (progressive rollout takes time) and requires good observability to judge canary health, but for high-stakes, large-scale services, limiting a bad deploy to 1% of users is worth it.
4. Why must old and new versions be compatible during any zero-downtime deploy? Because every zero-downtime strategy has both versions running simultaneously for some period — rolling and canary literally run them side by side throughout the rollout, and blue-green runs them together briefly during the switch and around any shared database. During that overlap, both versions serve real requests against the *same* backing systems, most importantly a shared database. So a change must work for both: if v2 renames a database column, v1 (still running) breaks when it queries the old name; if v2 requires a new required field, v1's writes fail validation. This is exactly why database schema changes must follow the expand-contract pattern (add the new thing, make both versions work, then remove the old thing across multiple deploys) and why API changes must stay backward compatible during the overlap. It's the reason deployment strategy and database migration strategy are inseparable — you cannot deploy safely if the two versions can't coexist against shared state, so the migration must be designed to support the overlap window.
5. How do feature flags improve on deployment-based rollout, and what's the cost? They decouple *deploy* from *release*. With deployment strategies alone, deploying code and activating a feature are the same event, so rolling back a broken feature means rolling back a deploy (rebuilding, redeploying, all instances). Feature flags let you deploy the code with the feature *off* (dark), then turn it on — for a percentage of users, specific users, or everyone — as a separate, instant configuration change, and turn it *off* just as instantly if it misbehaves, with no deploy at all. This is faster and simpler than any deploy rollback, enables gradual feature rollout independent of code deployment, allows deploying incomplete work safely (hidden behind a flag), and supports A/B testing on the same mechanism. The costs: flag management overhead (flags accumulate and must be cleaned up, or they become permanent technical debt and a source of untested code-path combinations), the risk of a mis-configured flag, and added conditional complexity in the code. But the ability to release and un-release a feature in seconds without touching the deployment makes flags one of the most valuable deployment-safety tools, usually layered on top of a deployment strategy rather than replacing it.

Further reading