Shadow Traffic, Canary Rollout, and Progressive Deployment for LLMs
LLM rollouts combine the hardest parts of software deployment: no unit tests, diffuse failure modes, and delayed signals. The sequence is (1) shadow mode: duplicate prod requests to the candidate model, log, and compare with zero user impact. It catches obvious distribution issues but is not a quality guarantee. (2) Canary rollout: shift traffic progressively, 1% → 10% → 25% → 50% → 75% → 100%, with gates at each step on latency percentiles, cost/request, error/refusal rate, output length distribution, and user-feedback rate. (3) A/B testing for distinct alternatives once stability is confirmed. Non-determinism does not go away: accuracy can vary by up to 15% across runs with identical inputs, from GPU FP non-associativity plus batch-size variance. Cost is a variable, so a 20% better model can be 3x more expensive per call. Rollback speed decides how bad a bad rollout gets, and if rollback needs a redeploy you are too slow. Policy lives in config/flags and the model lives in the registry with pinned digests, so rollback means flipping policy, reverting the threshold, and pinning the old model in seconds.
Learning Objectives
- Distinguish shadow mode (zero-impact compare), canary (live traffic progressive), and A/B (stability-confirmed comparison).
- Enumerate five LLM-specific canary metrics (latency, cost/request, error/refusal, output-length distribution, user feedback).
- Explain why LLM non-determinism (up to 15%) changes what "stable" means in a rollout.
- Design a rollback path that takes seconds (policy flip) not hours (redeploy).
The Problem
You ship a new model. Offline evals show a 3% accuracy gain, so you flip it on in production. Within 24 hours cost is up 40%, user thumbs-down is up 8%, and three customer tickets report "weird answers." You roll back, but the redeploy takes 3 hours and your weekend is gone.
Each of those failures had an earlier catch. Shadow mode would have shown the 40% cost spike before any user saw it. A canary would have stopped at 10% when thumbs-down moved. A policy-flag rollback would have taken 30 seconds. This lesson is about the discipline that fills the gap between "offline evals look good" and "real users are happy."
The Concept
Shadow mode
Candidate receives the same requests as production; outputs are logged, not returned to users. Zero user impact. Log:
- Output content (diff against production).
- Token counts (cost delta).
- Latency.
- Refusal and error.
Shadow catches cost blow-ups, length regressions, obvious refusal changes, and hard errors. It does not catch the quality difference users would perceive. Treat it as a smoke test, not a quality test.
Canary rollout
Shift traffic progressively and gate each step. A typical progression: 1% → 10% → 25% → 50% → 75% → 100%. Gate on 5 metrics at each step:
- Latency percentiles: P50, P95, P99. Breach: canary has P99 > 1.5x baseline.
- Cost per request: blended $. Breach: >20% above baseline.
- Error / refusal rate: 5xx plus explicit refusals. Breach: 2x baseline.
- Output length distribution: mean + P99. Breach: distributional shift.
- User-feedback rate: thumbs-down / ticket filings. Breach: 1.5x baseline.
Non-determinism is the new variance
Identical inputs can produce different outputs. Three causes:
- GPU FP non-associativity (floating-point reduction order varies by batch).
- Batch-size variance (same prompt in a batch of 128 vs batch of 16).
- Sampling (temperature > 0).
Measured: up to 15% accuracy variation run-to-run on identical eval sets. "Stable" in a rollout means metrics sit within expected variance of baseline, not that they match it. Set gates above the noise floor.
Cost is a variable
A 20% better model can be 3x more expensive per call, which is why cost/request is one of the five gates. A "better" model that breaks unit economics is a rollback case.
Rollback is the weapon
- Policy flag (feature flag system): flip the percentage in config. Takes seconds.
- Model pinning (registry digest): a pinned model does not auto-upgrade.
- Rollback means reverting the flag and setting the pinned digest back to the previous one. Seconds, not hours.
If your stack needs a redeploy to roll back, fix that before you start rolling out.
Tooling
Argo Rollouts / Flagger: Kubernetes progressive delivery controllers. They integrate with Istio/Linkerd weighted routing.
Istio weighted routing: traffic split at the service-mesh level.
KServe / Seldon Core: model serving with built-in canary.
Feature flags: LaunchDarkly, Flagsmith, Unleash. A policy-level flip with no redeploy.
Metrics cadence
Canary gates check every 5-15 minutes depending on traffic volume. 1% traffic with 10 req/min gives 50-150 data points per window, which is enough for latency but noisy for user feedback. 10% gives ~10x more. Pause long enough at each step to accumulate the samples the gate needs.
The A/B step is optional
If the new model is distinctly different (behavior, cost curve, tone), A/B test it at 50% after the canary passes. If it is just an improved version, go to 100% once the canary gates pass.
Numbers you should remember
- Canary progression: 1% → 10% → 25% → 50% → 75% → 100%.
- Non-determinism ceiling: up to 15% run-to-run variance on identical inputs.
- Five canary metrics: latency, cost, error/refusal, output length, user feedback.
- Cost gate: >20% above baseline is a breach.
- Rollback: seconds, not hours.
Use It
code/main.py simulates a canary rollout with injected regressions. It reports which stage the rollout halts at and which gate triggered.
Two simplifications to know about. The latency gate checks P99 only (no P50 or P95), and the output-length gate is P99 above 1.4x baseline rather than a full distribution comparison. Run it, then compare the six scenarios against the gate table in The Concept.
Ship It
This lesson produces outputs/skill-rollout-runbook.md. Given a candidate model, a baseline, and a risk tolerance, it designs a shadow→canary→100% plan.
I'd run main.py before trusting any of the multipliers above. It walks six stages (1%, 10%, 25%, 50%, 75%, 100%) against five gates and prints PASS or HALT per stage, plus a rollback line when a gate trips. The scenarios are a clean promotion, a 10% cost regression, a 25% cost regression, an 80% latency regression, a 60% thumbs-down regression, and a mix of 15% cost creep with a 45% thumbs-down rise.
The 10% cost case is the one worth staring at. It passes at every stage because the simulator adds up to 8% noise to each metric, and 1.10 times 1.08 is still under the 1.2 cost gate. The 15% cost regression sits closer to the line, and in the last scenario it halts at 25% with cost and thumbs-down both tripping. (main.ts runs the same scenarios on a different random generator, so it halts that last one at the 1% stage on thumbs-down alone.) The gates are not strict thresholds on the true value. They are thresholds on a noisy reading, and that is the same reason the gates need to sit above the noise floor.
I'd also keep the toy in proportion. Its noise width is the same at every stage, so 1% traffic reads as cleanly as 75%. Real canaries get noisier as traffic drops, and user feedback suffers most, as the metrics cadence section describes. The simulator also has no shadow phase and no A/B step, and its rollback is a printed line rather than a flag flip. Exercise 3 is where you build the part it leaves out.
Exercises
- Run
code/main.py. Inject a 25% cost regression. At which stage does the canary halt, and why that stage? Then try 15% and compare. - Your new model has 3% accuracy gain offline but cost/request is +18%. Is it a ship? Depends on the policy, so write both paths.
- Design a rollback that takes under 60 seconds end-to-end. List the required infrastructure.
- Non-determinism shows ±7% on your eval. Set canary gates so you don't false-alarm. What multipliers do you use?
- Shadow mode catches a 40% cost spike before canary. Write the alert rule that fires in shadow.
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| Shadow mode | "duplicate to new" | Zero-impact send-to-candidate for logging |
| Canary | "progressive traffic" | Gradual user-exposed rollout with gates |
| Gates | "rollout checks" | Metric thresholds that block progression |
| Non-determinism | "LLM variance" | Irreducible run-to-run differences |
| Policy flag | "flag flip rollback" | Config-level rollback, seconds not hours |
| Model pin | "registry digest" | Immutable reference to a model version |
| Argo Rollouts | "K8s progressive" | Kubernetes-native canary/rollback controller |
| KServe | "inference K8s" | Model serving with canary primitives |
| Istio weighted | "mesh split" | Service-mesh traffic splitter |