Appendix A - 8. Staged deploy gates (canary → smoke → promote)
The Structure of Staged deploy gates (canary → smoke → promote) — its shape at a glance:
The pipeline is a staircase: a cheap pre-launch predicate gates the whole thing, then a canary takes no traffic, smoke tests it against its own URL, and promotion happens only on green.
flowchart LR
Pre{Pre-launch green?} -->|no| Skip([Never start])
Pre -->|yes| Canary[Deploy canary, no traffic]
Canary --> Smoke{Smoke passes?}
Smoke -->|no| Block([Promotion blocked])
Smoke -->|yes| Promote([Promote to production])
Accessible description: a pre-launch predicate gates the deploy; if green a canary revision is deployed taking no traffic and smoke-tested against its own URL, and promotion to production happens only when the smoke stage passes, otherwise promotion is blocked.
Projected from the catalogue entry gates-and-merge-train / Staged deploy gates (canary → smoke → promote).
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — A deploy pipeline that escalates canary → smoke → promote, blocking promotion to production until each cheaper stage passes on a traffic-free revision, so a bad build is caught before users see it, not after.
Motivation
Shipping a build straight to production means a regression lands on users; the failure is the incident. This is standard practice everywhere, but it matters far more at agentic velocity, precisely because deploys are frequent and agent-initiated: the more often you ship, the more often an un-gated bad build reaches users.
Applicability
- Canary capability: the ability to deploy a revision that takes no production traffic.
- A smoke suite that meaningfully exercises the canary URL (real dependencies, not stubs).
- Promotion + rollback primitives and revision GC.
- A pre-launch green signal (lints / changed-since-main / flaky-class check) as the pre-launch predicate.
Structure
The Structure diagram appears at the top of this page.
Sample Code
The deploy is a staircase of gates, each cheaper than the next stage it guards. The pre-launch predicate avoids paying build minutes for a doomed deploy; the canary and smoke gates catch a break on a revision no user can reach.
def staged_deploy(pre_launch_green, build, deploy_canary, smoke, promote) -> int:
if not pre_launch_green(): # cheapest gate: lints green, no known flaky class, changed-set clean
print("pre-launch predicate red — not starting"); return 0
artifact = build()
canary = deploy_canary(artifact) # a revision taking NO production traffic
if not smoke(canary.url): # exercise the canary's own URL with real dependencies
print("smoke failed on canary — promotion blocked"); return 1
promote(canary) # only a green canary reaches users
return 0
Consequences
- Staging costs real build minutes. The gate is not free; the pre-launch predicate exists to avoid spending them on a deploy that was never going to pass.
- Smoke ≠ full coverage. A thin smoke suite lets real breaks through the gate; the canary is only as good as what the smoke actually checks.
- The gate infrastructure itself can drift. Validating via the deploy pipeline assumes the pipeline is sound; a drifted canary/smoke path gives false confidence (don't validate via untested infra).
- Deploy latency. Each stage adds wall-clock before users get the change.
Example use within DocAble
- The staged deploy driver (canary tag → smoke → promote → revision GC).
- The pre-deploy predicate (lints green · no flaky class · changed-since-main green).
- Per-phase deploy heartbeats (see Lifecycle & observability).
Related Patterns
- Layer — the last stair of the staircase, downstream of merge-train-mis-batching; the most expensive gate, reached only after the cheap ones passed.
- See also (complement) — the product-target per-pass validators and conformance checks run inside the shipped artifact; these staged gates wrap the deploy of that artifact.