Skip to main content
PProfileMatch

Deployment Strategies: Rolling, Blue-Green, Canary and Rollbacks

DevOpsLesson 3 of 46 min read

What you'll learn

  • Compare rolling, blue-green and canary releases and pick one for a given risk
  • Use feature flags to decouple deploying code from releasing a feature
  • Perform and plan a rollback, including the database migration problem
  • Decide what to monitor before declaring a release healthy

On this page

  1. Four ways to put new code in front of users
  2. Blue-green on Kubernetes: switching the selector
  3. Feature flags: deploy is not release
  4. Rollbacks: the part you plan before you need it
  5. Common mistakes and the symptoms they cause
  6. How this shows up in real projects and interviews

An online grocery store is about to replace its checkout page. The old one works but converts poorly. The new one has been tested, reviewed and demoed. Is it safe to ship to everyone at 10 a.m. on a Saturday? Tests can say the code behaves as specified. They cannot say how it behaves under real traffic, real data and real customers' odd browsers. Deployment strategies are different ways of limiting the damage of being wrong.

Four ways to put new code in front of users

StrategyHow it worksRollbackCost and catch
RollingReplace instances gradually, a few at a time, so old and new versions briefly coexistRoll forward or re-run the previous version; speed depends on batch sizeCheapest and the default in many platforms. Both versions must tolerate each other's data and APIs
Blue-greenRun the new version (green) beside the live one (blue), test it, then switch traffic all at onceSwitch traffic back to blue, which is still runningNeeds double capacity during the switch. Clean cut-over but everyone moves at the same instant
CanarySend a small share of real traffic (say 5 percent) to the new version, watch metrics, increase graduallyRoute the share back to zeroNeeds traffic-splitting infrastructure and good metrics; catches problems with real users at low blast radius
RecreateStop the old version, start the new oneRedeploy the old versionSimple but has downtime; acceptable for internal tools or batch jobs

Choosing is a risk decision. A rolling update with readiness checks is enough for most internal services. Blue-green suits releases where you want to test the complete new environment before anyone sees it. Canary is best when a bad release would be expensive and you have the metrics to judge it quickly. For how rolling updates are configured on a cluster, see the Kubernetes course.

Blue-green on Kubernetes: switching the selector

A common way to do blue-green on Kubernetes is to run two Deployments labelled version: blue and version: green, and let a Service pick which one receives traffic through its label selector. The switch is a one-line change, and the rollback is the same change in reverse.

Terminal
# 1. Deploy the new version next to the live one (not receiving traffic yet)
kubectl apply -f checkout-green.yaml
kubectl rollout status deployment/checkout-green

# 2. Smoke-test green directly, without touching users
kubectl port-forward deployment/checkout-green 9090:8080 &
curl -fsS http://localhost:9090/health

# 3. Switch the Service selector from blue to green
kubectl patch service checkout \
  -p '{"spec":{"selector":{"app":"checkout","version":"green"}}}'

# 4. Roll back instantly if metrics look wrong
kubectl patch service checkout \
  -p '{"spec":{"selector":{"app":"checkout","version":"blue"}}}'

Keep blue running until you are confident, then scale it down. The cost is the temporary double footprint. Note that a switch cannot hide an incompatible database change, which is the topic of the rollback section below.

Feature flags: deploy is not release

A feature flag is a runtime condition that decides whether a code path is active. With flags, you can merge and deploy unfinished or risky code switched off, then enable it for staff, then for a percentage of users, then for everyone, with no new deployment at each step. Turning it off is also instant. The percentage rollout needs one property above all: a given user must always land in the same bucket, otherwise customers see the old and new checkout alternately on every page load. Hashing the flag name and user id gives a stable bucket.

flag.mjs
import { createHash } from "node:crypto";

// Deterministic bucket 0..99 from the flag name and the user id.
function bucket(flagName, userId) {
  const digest = createHash("sha256").update(`${flagName}:${userId}`).digest();
  return digest.readUInt32BE(0) % 100;
}

function isEnabled(flag, userId) {
  if (!flag.enabled) return false;
  if (flag.allowList?.includes(userId)) return true;
  return bucket(flag.name, userId) < flag.percent;
}

const newCheckout = { name: "new-checkout", enabled: true, percent: 25, allowList: ["qa-bot"] };
const users = ["qa-bot", "shopper-1", "shopper-2", "shopper-4", "shopper-6", "shopper-7", "shopper-8"];

for (const id of users) {
  const b = String(bucket(newCheckout.name, id)).padStart(2);
  console.log(id.padEnd(10), "bucket", b, "->", isEnabled(newCheckout, id));
}

// Raising the percentage only ever adds users: nobody flips back and forth.
const wider = { ...newCheckout, percent: 50 };
const at25 = users.filter((u) => isEnabled(newCheckout, u));
const at50 = users.filter((u) => isEnabled(wider, u));
console.log("at 25%:", at25.join(", "));
console.log("at 50%:", at50.join(", "));
console.log("superset:", at25.every((u) => at50.includes(u)));

Output

qa-bot     bucket 50 -> true
shopper-1  bucket 83 -> false
shopper-2  bucket 30 -> false
shopper-4  bucket 46 -> false
shopper-6  bucket 15 -> true
shopper-7  bucket  2 -> true
shopper-8  bucket 97 -> false
at 25%: qa-bot, shopper-6, shopper-7
at 50%: qa-bot, shopper-2, shopper-4, shopper-6, shopper-7
superset: true

Save it as flag.mjs and run node flag.mjs. The QA account is enabled through the allow list despite its bucket of 50, and raising the rollout from 25 to 50 percent only adds shoppers. Real systems use a flag service or library rather than a hand-rolled one, but this is the mechanism inside them. The downside of flags is clutter: every flag is a branch in your code that must be tested both ways and removed once the rollout is complete.

Rollbacks: the part you plan before you need it

  • Rolling back code is easy if artifacts are immutable. If you deployed image checkout:3.4.1 and the previous was 3.4.0, rollback is deploying 3.4.0 again. If you used latest, you do not actually know what to go back to. On Kubernetes, kubectl rollout undo deployment/checkout restores the previous template.
  • Rolling back data is hard. If version 3.4.1 renamed a column and 3.4.0 expects the old name, the code rollback fails at the first query. The standard remedy is expand and contract: first add the new column while keeping the old one (expand), deploy code that writes both and reads the new one, and only in a later release remove the old column (contract). Every release is then compatible with the one before it.
  • Prefer rolling forward for small bugs. A tiny fix through a fast pipeline can be quicker and safer than a rollback, if the pipeline is fast enough and the problem is understood. Roll back when customers are hurting and the cause is unclear.
  • Define the trigger in advance. Decide which signals (error rate, latency, checkout completion rate) and which thresholds make you roll back, before the release. Otherwise a 3 a.m. debate decides it.

Common mistakes and the symptoms they cause

  • Incompatible database and code versions in a rolling update. Symptom: errors only while some instances are old and some new, such as column does not exist from the old pods. Fix: expand/contract migrations.
  • Canary with no metrics. Symptom: 5 percent of users see a bug and nobody knows for a day. Fix: define dashboards and automatic abort rules first.
  • Sticky sessions or per-request randomness in canaries. Symptom: users flip between versions and report inconsistent behaviour. Fix: route by a stable key such as user id.
  • Flags that never get removed. Symptom: code full of dead branches and a flag nobody dares touch. Fix: put a removal ticket and date on every flag.
  • Rollback that depends on a rebuild. Symptom: the rollback pipeline takes twenty minutes or fails because a dependency changed. Fix: keep previous artifacts and redeploy them.

How this shows up in real projects and interviews

A standard interview question is "how would you release a risky change with minimal user impact?" A good answer combines a flag or canary with named metrics, a stated rollback trigger and a database compatibility plan. Be honest that each approach costs something: infrastructure, complexity or time. In practice, most teams use rolling updates by default, flags for large features, and reserve blue-green or canary for the services where an outage is expensive.

Try it yourself

Pick a feature in a project you know and write a one-page release plan for it. State the strategy you would use and why, the flag or traffic split, the three metrics you would watch, the exact threshold that triggers a rollback, and how the database change (if any) stays compatible with the previous version.

Show a hint

Use the expand and contract pattern for any schema change: add, dual-write, migrate, switch reads, then remove in a later release.

Key takeaways

  • Rolling, blue-green and canary trade cost and complexity against how much damage a bad release can do.
  • Feature flags separate deploying code from releasing a feature and enable instant, targeted rollback.
  • Stable bucketing (hash of flag and user) keeps each user's experience consistent during percentage rollouts.
  • Rollback is easy for code with immutable artifacts and hard for data; use expand and contract migrations.
  • Decide the metrics and thresholds that trigger a rollback before the release starts.

Test yourself

A trick from this lesson's ride: Blue-green: two lanes, one traffic cone. Moving the cone back is the undo. Four more tricks like it wait along the route, each followed by a question.

Ride this lesson

Frequently asked questions

What is the difference between blue-green and canary deployment?

Blue-green runs two full environments and switches all traffic from the old one to the new one at once, with the old one kept for instant rollback. Canary sends only a small, growing share of real traffic to the new version while you watch metrics, limiting the impact of a bad release.

What are feature flags and why use them?

Feature flags are runtime switches that turn code paths on or off without redeploying. They let you ship unfinished code safely, enable features for specific users or percentages, and disable a problematic feature instantly if something goes wrong.

How do you roll back a deployment safely?

Redeploy the previous immutable artifact (for example the earlier image tag), or use your platform's undo such as kubectl rollout undo. For database changes, make migrations backward compatible with expand and contract so the old code still works. Define rollback triggers and practise the procedure in advance.