Architectural · Deployment · DWG NO. 057

Canary Release

Roll a new version out to a small slice of traffic first, and only expand it once it's proven healthy under real usage.

TypeDeployment
ScopeTraffic-split rollout
ComplexityMedium
Common inGradual rollouts, risk-sensitive releases, A/B-adjacent infrastructure

Blue-Green catches a bad deploy only after it's serving 100% of traffic

Even careful pre-release testing doesn't always catch every problem — some issues only appear under real production traffic patterns or scale. Switching every user to a new version at once means a bad deploy affects everyone simultaneously before anyone notices.

A small percentage first, then a gradual ramp

The new version is deployed alongside the old one, and the router sends only a small percentage of traffic to it initially (the "canary"). Metrics are watched closely; if healthy, the percentage is increased in steps until it reaches 100%. If something looks wrong at any step, traffic is routed back to the old version entirely.

Router: 95% / 5%95%5%Stable v1Canary v2watch metrics, then ramp up

A percentage-based router with a metric check gate

A small, deterministic slice of requests goes to the canary version. The ramp function only increases that slice if the canary's error rate looks healthy.

canary-router.js
let canaryPercent = 5;

function routeRequest(requestId) {
  const bucket = hashToPercent(requestId); // deterministic 0-99 for a given request
  return bucket < canaryPercent ? "v2 (canary)" : "v1 (stable)";
}

function hashToPercent(id) {
  let h = 0;
  for (const c of id) h = (h * 31 + c.charCodeAt(0)) >>> 0;
  return h % 100;
}

function maybeRampUp(canaryErrorRate) {
  if (canaryErrorRate < 0.01 && canaryPercent < 100) {
    canaryPercent = Math.min(100, canaryPercent * 2);
    console.log(`canary healthy — ramping up to ${canaryPercent}%`);
  } else if (canaryErrorRate >= 0.01) {
    canaryPercent = 0;
    console.log("canary unhealthy — rolled back to 0%");
  }
}

console.log(routeRequest("req-101"));
maybeRampUp(0.002); // healthy → ramps 5% to 10%

Trade-offs