Hermes Wiki
Architecture/Challenges/design-a-deployment-pipeline-with-canary-analysis-and-rollback

Design a Deployment Pipeline with Automated Canary Analysis and Rollback

Scenario prompt

Design the deployment pipeline for a platform with dozens of services, each deployed multiple times a day by different teams. Requirements:

  • A new build must roll out to a small slice of production traffic before it reaches everyone
  • The pipeline must automatically detect a bad deploy from real production signal (error rate, latency, saturation) and roll it back — without a human watching a dashboard
  • Rollback has to be fast enough that a bad deploy's blast radius stays small, even under load
  • Different services have wildly different risk profiles (a stateless read API vs. a service that writes to a shared database) and the pipeline shouldn't force one-size-fits-all caution on all of them
  • The system itself must not become a single point of failure that blocks every team's deploys when it degrades

Mihir's attempt

[!todo] Write your own attempt here before reading the model solution below — how you'd define "bad deploy," and what you'd do differently for a stateless read API vs. a service performing writes to a shared database.

Model solution

Progressive traffic shifting, not instantaneous cutover. A new version deploys alongside the current one and receives a small, deliberately chosen slice of production traffic (often 1-5%) before any promotion decision is made. This is the direct deployment-time expression of Blue-Green and Canary Deployments — the canary slice exists specifically so a bad build's damage is bounded to a fraction of traffic while there's still real production signal to judge it by, rather than either the full "big bang" cutover of naive blue-green or a slow, purely time-based rollout that isn't actually watching anything.

Automated analysis against the canary's own baseline, not a static threshold. The pipeline compares the canary's error rate, p99 latency, and saturation against the stable version serving the rest of traffic in the same time window — not against a hardcoded number that drifts stale as traffic patterns change. This is the same reasoning as SLIs, SLOs, and Error Budgets: judge against a moving, contextual baseline, not an arbitrary fixed line. A statistically significant regression versus baseline triggers automatic rollback; a marginal or ambiguous result holds the rollout and pages a human rather than guessing in either direction.

Rollback as a pre-computed, instant action — not a re-deploy. The stable version's infrastructure stays warm and routable throughout the canary window; rollback means flipping traffic weight back to 100% stable, not re-running a deploy pipeline under pressure. This mirrors the incident escalation principle that a safety mechanism has to be fast and pre-staged, because the moment you need it is the worst moment to discover it's slow.

Risk-tiered gating, not one policy for every service. A stateless read API can run a short canary window with an aggressive auto-promote; a service performing writes to a shared database needs a longer bake time, smaller initial traffic slice, and — critically — its rollback plan has to account for writes the canary already made (a canary that already wrote bad rows isn't fixed by shifting traffic away from it). Coupling this to Idempotency Keys and the Transactional Outbox Pattern on write-path services means a rolled-back canary's in-flight writes can be safely retried or reconciled rather than left in an undefined state.

The pipeline degrades toward caution, not toward blocking everyone. If the analysis service itself is unavailable, the fallback isn't "hold every team's deploy platform-wide" — it's per-service: fail that one canary decision closed (hold at current traffic split, alert, let a human decide) while unrelated services' pipelines keep running. A shared deployment control plane that becomes a single choke point for every team's release velocity the moment it hiccups defeats the purpose of decentralizing deploys in the first place.

Gaps to revisit

  • How do you set a canary traffic percentage and bake time per service without every team hand-tuning it — some default derived from traffic volume and historical incident rate?
  • Multi-region rollouts compound this: does the canary run region-by-region, or a single global canary slice? Region-by-region is safer but slower and needs its own coordination logic.
  • Database schema changes can't ride the same canary mechanism as stateless code — how does the pipeline sequence a schema migration (expand/contract) against a canary code deploy that assumes the new schema?
  • Who owns the analysis thresholds when a service's normal traffic is genuinely noisy (spiky, low-volume) and statistical significance is hard to reach quickly?

Principal Engineer Lens

This challenge is really about where you put judgment in a system that has to make a go/no-go call without a human in the loop fast enough to matter. The temptation is to reach for hard thresholds because they're simple to explain in an architecture review — but a Principal Engineer should be able to articulate why a moving baseline comparison is more robust, and more importantly, be explicit about the failure mode of automation here: an automated rollback that fires on noise erodes trust the same way a noisy pager does, and teams start ignoring or bypassing it. The risk-tiering question — one policy doesn't fit a stateless read API and a stateful write path — is also a good test of whether someone is designing for the org's actual service diversity or just the happy path they know best, which is exactly the kind of judgment call that separates a Principal-level design from a merely functional one.

Reel Script

Setup: Every team deploys multiple times a day, and nobody wants to babysit a dashboard for twenty minutes after every push. But a fully automatic rollout with no safety net is how one bad build takes down production for everyone.

Concept walkthrough: Explain progressive traffic shifting — a small slice of real traffic hits the new version first — and why the promotion decision has to compare that slice against a live baseline of the stable version in the same window, not a hardcoded number. Then walk through why rollback has to be a pre-staged traffic-weight flip, not a re-deploy under pressure.

Real example tie-in: Contrast a stateless read API (short canary, aggressive auto-promote) against a service writing to a shared database (longer bake, smaller slice, and a rollback plan that has to reconcile writes the canary already made) — this is where idempotency and the outbox pattern from earlier notes come back into play.

Tradeoffs & alternatives: Naive blue-green (instant full cutover) is simpler but has no bounded blast radius; a purely time-based rollout with no analysis is "hope as a strategy." Automated canary analysis costs more engineering effort but is what actually bounds risk while keeping deploy velocity high — and note the failure mode where the analysis service itself becomes a bottleneck if it isn't designed to fail per-service rather than platform-wide.

Principal Engineer takeaway: The hardest part isn't the traffic-shifting mechanics — it's deciding, in code, what "bad" means well enough that the system can act on it without a human in the loop, and being honest about where that judgment is more fragile than it looks in the design doc.

Hermes Wiki