release-runbook-author
Turns one service's release into a written six-phase runbook: pre-flight checks, a smoke gate, a canary observation window, a named human promote gate, progressive rollout, and post-release verification. Fixes each phase's pass criteria as a delta against a recorded baseline rather than a bare absolute number, gives canary and rollout separate windows and separate thresholds, and emits a per-phase evidence table that becomes the release record. The multi-team cutover-sequence procedure - dependency-ordered gates with one named owner each, hard timeboxes, written rollback triggers, and the reverse-order rollback path - is worked in references for windows where several teams cut over interdependent services. Use when a single service is about to ship and its release steps exist only as tribal knowledge or a chat thread, or when a shared release window needs its cutover order, gate owners, and rollback path written down.
Install with skills.sh (any agent)
npx skills add testland/qa --skill release-runbook-authorrelease-runbook-author
What this owns, and what it does not
This owns the single-service release runbook: the ordered phases one service walks from pre-flight to post-release, the pass criteria written at each phase, and the way each phase compares what it observes against a baseline. The failure mode it addresses is a release where the steps live in someone's head, so "is it healthy?" gets answered by whoever is awake.
The cross-team sequencing problem - several interdependent services, owned by several teams, that must change over in a dependency order inside one shared window - is a separate procedure, worked in references/cutover-sequence.md. The division is clean: that procedure decides which service goes when and who may say stop for the window; this runbook is what a single service runs inside the slot it was given. If your release touches one service, you need only the runbook.
It also does not own the statistics of canary analysis. Choosing a significance test, computing effect size, and correcting for the sample-size penalty of a small traffic share belong to a dedicated statistical comparison procedure. This runbook says which phase compares what against what, and what the runbook must state in advance. It stops at the point where you need a p-value.
The rule that governs every gate
Rollback is a named human decision made on evidence. It is never an automatic metric trigger. A crossed threshold halts the phase and puts a decision in front of a person; it does not choose the recovery. Published guidance lists three distinct recoveries from a bad deployment: rolling back (undo the changes, revert to the last known working configuration), rolling forward (fix the issue mid-rollout with a hotfix), and deploying new infrastructure (stand up the last known working configuration afresh) (Azure Well-Architected, safe deployment practices (opens in new window)). A threshold cannot pick among three options.
What automation owns is the halt: when a health alert fires, "the rollout should immediately halt" and an investigation determines the next course of action (Azure Well-Architected, safe deployment practices (opens in new window)). So write every threshold in the runbook as a condition that produces a decision, never one that produces an action.
Baseline first: the comparison convention every phase uses
The single most common way a release runbook produces a wrong verdict is reading a production number with nothing to read it against. A 1 percent error rate is alarming for one service and normal for another, and neither the runbook nor the person on the call can tell which without a baseline.
Record the baseline before the deploy starts. For each metric in the runbook: the same query, the same aggregation, over a window at least as long as the phase you will later compare it to. Write the values into the runbook as literals before phase 1 runs, not from memory afterwards.
Then state every phase's pass criterion as two conditions, both of which must hold:
| Condition | Shape | Catches |
|---|---|---|
| Absolute floor | "5xx rate at or below 0.5 percent" | Unacceptable regardless of what the baseline was |
| Delta against baseline | "5xx rate at or below 1.5 times baseline" | A real regression that still sits under the absolute floor |
During the canary phase the comparison has a stronger form available, and the runbook should use it. The canary is defined as "a partial and time-limited deployment of a change in a service and its evaluation", where "the part of the service that receives the change is 'the canary,' and the remainder of the service is 'the control'" (Google SRE Workbook, Canarying Releases (opens in new window)). The control is running concurrently, on the same traffic mix, at the same hour, so it removes the confounders a time-shifted baseline leaves in. The mechanism that makes it usable is per-population breakdown: "If we break down metrics by population servicing the request (the canary versus the control), we can observe the separate metrics" (Google SRE Workbook, Canarying Releases (opens in new window)). If your dashboards cannot split by deployment version, fix that before writing a canary phase, because an aggregate number dilutes the canary's signal by exactly its traffic share.
Which comparison each phase gets:
| Phase | What is observed | Compared against | Comparison shape |
|---|---|---|---|
| 1. Pre-flight | Discrete facts about the build | Nothing | Binary pass or fail, with evidence per row |
| 2. Smoke gate | Suite result on the target environment | The last green run of the same suite | Absolute: zero failures |
| 3. Canary | Metrics of the canary population | The concurrent control population | Absolute floor and ratio to control |
| 4. Promote gate | The canary table plus every anomaly below threshold | The criteria written in advance | Named human decision on stated evidence |
| 5. Rollout | Metrics of the whole service | Recorded pre-deploy baseline window | Absolute floor and ratio to baseline, time-shifted |
| 6. Post-release | The same metrics at window close | The same recorded baseline | Ratio plus an explicit stability statement |
Say plainly in the runbook that phases 5 and 6 are the weaker comparison. Once the canary is promoted there is no concurrent control left, so time of day, traffic mix, and any unrelated concurrent change are folded into the delta. That is a reason to keep the canary phase honest, not a reason to skip the later ones.
Phase 1 - Pre-flight
Pre-flight items are checks, not actions. Each row is a fact about the release that was already true before the runbook opened; the phase confirms it and records where the confirmation came from. Nothing in pre-flight changes production.
Rows worth carrying in most runbooks:
| Check | Evidence to record |
|---|---|
| CI green on the release ref | The run URL or command output, not "yes" |
| No open blocking issues for this release | The query and its zero result |
| Schema or data migration verified against a production-shaped copy | The dry-run artifact and the commit it ran at |
| The previous version is retrievable and deployable | Artifact ID of the last known good build |
| Baseline metric values recorded | The literal numbers, with the window they came from |
| Named owner available for the promote gate | Person and their availability window |
The last two are the ones teams skip, and they are the two that make the later phases meaningless when missing. Establish predeployment checks (code review, security scans, compliance checks), since "Different tests catch different classes of failures" (Azure Well-Architected, safe deployment practices (opens in new window)).
A checklist beats judgment here, and each entry should be earned: "Every question's importance must be substantiated, ideally by a previous launch disaster" (Google SRE Book, Reliable Product Launches at Scale (opens in new window)). A pre-flight row that has never once caught anything is a row to delete.
A failed pre-flight row ends the release at phase 1, the same standard Scrum applies to work that does not meet the Definition of Done: it "cannot be released" and returns to the backlog (Scrum Guide (opens in new window)).
Phase 2 - Smoke gate
A narrow critical-path suite run against the target environment, not the full regression pack. Its job is to answer "is this build fundamentally functional" in single-digit minutes, so that a broken build never reaches real users at all.
The runbook records four things and no more: the command, the environment it targeted, the wall-clock duration, and the result. Everything else goes in the uploaded artifact.
Pass criterion is absolute and zero-tolerance: no failures. This is one of the few phases where a delta is the wrong instrument. "One more failure than last time" is not a threshold worth defining, because a smoke suite that tolerates any failure has stopped being a gate. If a smoke test is flaky, fix or remove the test before the release, not during it.
Two conventions worth writing down explicitly, both practitioner conventions rather than published guidance:
Gating each phase of exposure on a health check is the published shape: "Deployments must pass health checks before each phase of progressive exposure can begin" (Azure Well-Architected, safe deployment practices (opens in new window)).
Phase 3 - Canary observation
Deploy to a small slice, then watch. The purpose is stated precisely in the original description of the pattern: it reduces risk "by slowly rolling out the change to a small subset of users before rolling it out to the entire infrastructure and making it available to everybody", and it provides "early warning for potential problems before impacting your entire production infrastructure or user base" (Martin Fowler, CanaryRelease (opens in new window)).
The runbook fixes four things before the deploy, in writing:
Be honest about what a 30-minute window buys. It is a smoke-plus-signal check, not a bake: "Bake times should be measured in hours and days rather than minutes" and should increase per rollout group to cover time zones and usage patterns (Azure Well-Architected, safe deployment practices (opens in new window)). A runbook that calls half an hour a bake period is mislabelling its own coverage. Either write "smoke coverage only" next to the window, or schedule a genuine bake and accept the slower release.
The observation table is the phase's output:
### Canary observation - 14:33 to 15:03 UTC, 5 percent traffic
| Metric | Absolute floor | Ratio limit | Control | Canary | Ratio | Verdict |
|---------------------|----------------|-------------|---------|--------|-------|---------|
| 5xx rate | 0.5% | 1.5x | 0.31% | 0.42% | 1.35x | PASS |
| p95 latency | 500ms | 1.2x | 240ms | 245ms | 1.02x | PASS |
| Checkout completion | 90% min | 0.95x min | 92.1% | 91.2% | 0.99x | PASS |
| New error signatures| 0 | n/a | 0 | 2 | n/a | ANOMALY |Surface anomalies that sit below every threshold. The point of the phase is early warning, so a signal that is real but under the limit is exactly the output the phase exists to produce. Two new error signatures with all four thresholds green is a PROCEED WITH CAUTION verdict carrying a named follow-up, not a silent PASS.
Phase 4 - Promote gate
The runbook stops here and does not advance without a named person saying so. This is the only phase whose output is a human sentence rather than a number.
The gate presents three options, and the reason there are three is the three-recovery fork from the rule section: a bad canary can be reversed, fixed forward, or investigated with the canary left in place. The change fail rate definition carries the same fork, describing deployments that require immediate intervention, "likely resulting in a rollback of the changes or a 'hotfix' to quickly remediate any issues" (DORA software delivery metrics (opens in new window)).
| Option | Means | Written consequence |
|---|---|---|
continue | Promote to rollout with the anomalies acknowledged | Each acknowledged anomaly becomes a named follow-up item |
pause | Extend observation or investigate with the canary still live | New window length and what evidence would end the pause |
rollback | Reverse to the previous version | The reverse steps, from the pre-flight last-known-good artifact |
Three properties this gate must have, all of which are the difference between a gate and a formality:
Phase 5 - Rollout
Promote outward in stages, smallest blast radius first. The published shape is exponential expansion from a single unit: "we may push by starting in one cluster and expand exponentially until all clusters are updated" (Google SRE Book, Release Engineering (opens in new window)). Each stage repeats the phase 3 pattern in miniature: expose, observe, confirm, expand.
Two things change relative to canary, and the runbook must state both:
A threshold trip during rollout halts the expansion and returns to phase 4's decision table with the current stage as context. It does not reverse anything by itself.
Phase 6 - Post-release
The release is not finished when traffic reaches 100 percent. Issues that surface five to ten minutes after full exposure escape a runbook that closes at promotion.
Post-release has three outputs:
Before the runbook is usable
Every row below is a defect in the document, not a judgment call.
| Check | Fails if |
|---|---|
| Baseline | Any metric has a threshold but no recorded pre-deploy value |
| Two-condition thresholds | Any metric has only an absolute floor, or only a ratio |
| Population split | A canary threshold exists but dashboards cannot break down by version |
| Separate windows | Canary and rollout share one window length or one threshold set |
| Window honesty | A sub-hour window is described as a bake period |
| Promote owner | Phase 4 names a team or a channel instead of one person |
| Gate integrity | Any path promotes without the phase 4 decision, including the all-green path |
| Trigger wording | Any threshold is written as an action rather than as a decision |
| Evidence | Any phase says PASS without naming the query, suite, or dashboard behind it |
| Reversibility | The last known good artifact is not identified in pre-flight |
| Business metric | The canary metric set contains no user-outcome metric |
Worked example
Service checkout-api, release v1.4.5, single service, walked through all six phases from baseline to post-release follow-ups: references/worked-example.md.
Output template
One document that is the plan before the release and the record after it - header and recovery rule, baseline, thresholds, the six phase tables, and the follow-up list: references/output-template.md.
Anti-patterns
Eleven runbook anti-patterns with why each fails and its fix: references/anti-patterns.md.
Limitations
Anti-patterns
View source (opens in new window)Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Reading production metrics with no recorded baseline | A 1 percent error rate is normal for one service and an incident for another, and the runbook cannot tell which | Record baseline values in pre-flight and state thresholds as deltas as well as absolutes |
| Absolute thresholds only | Catches "unacceptable always" and misses "clearly regressed but still under the floor" | Two conditions per metric, both must hold |
| Aggregate metrics during canary | A 5 percent canary dilutes its own signal twentyfold in the aggregate number | Break metrics down by canary versus control population |
| One observation window covering canary and rollout | Canary looks for early signal at low blast radius, rollout looks for stability at full exposure; different goals need different windows and thresholds | Two phases, two window lengths, two threshold sets |
| Calling a 30-minute window a bake | Published guidance measures bake time in hours and days, so the runbook claims coverage it did not buy | Label the window as smoke coverage, or schedule a real bake |
| Treating "no threshold tripped" as the verdict | The canary phase exists to give early warning, and an attributable anomaly under the limit is exactly that warning | Report sub-threshold anomalies as named follow-ups with a PROCEED WITH CAUTION verdict |
| Auto-promoting when the canary table is all green | The clean case is where a subtle regression hides, and a rollback after full exposure costs far more than a five-minute pause | The promote gate holds unconditionally, with one named owner |
| A threshold wired to automatic rollback | The threshold cannot choose between roll back, roll forward, and redeploy last known good | Thresholds halt and page; the named owner decides |
| Shortening the last rollout stage because the first went well | The last stage carries the most users, so it is where an undetected regression is most expensive | Windows lengthen as exposure grows |
| Tagging and announcing at promotion | Issues that surface minutes after full exposure land after the release was declared done | The administrative tail runs after the post-release window closes |
| Running the release with no runbook, ad hoc | The process becomes tribal knowledge, so nothing can be reviewed, improved, or handed over | Write the six phases before the release, and edit them in the retrospective |
Anti-patterns
View source (opens in new window)Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| A metric threshold wired to automatic rollback | The threshold cannot choose between rolling back, rolling forward, and redeploying known-good, and in a multi-team window it cannot know how many other teams must reverse with it | Thresholds halt and page; the named release authority decides (see the rule section) |
| One org-wide go or no-go at the end of the window | Failures surface only after every service has cut over, so the reverse path is at its longest and most entangled | A DECISION gate per service, in dependency order, before its dependents start |
| A gate owned by a team name or a rota alias | At 02:00 nobody is sure who is allowed to say stop, and two people act concurrently | Exactly one named person per gate, availability confirmed in advance |
| Timeboxes with no hard-stop policy | Teams read a timebox as a target, gates slip individually, and the window silently overruns | One hard-stop time for the window with a stated consequence, and extension only as its own DECISION gate |
| Rolling back in forward order | The dependency reverses while its dependent still calls the new contract, turning a bad release into an outage | Reverse the completed prefix of the gate list, confirming each step |
| A rollback list with scope but no order and no owners | The reverse becomes a second uncontrolled cutover under time pressure | Ordered reverse steps, one owner each, confirmation between steps |
| A state-writing gate treated as reversible | The plan promises a reversal that physically cannot happen | Mark POINT OF NO RETURN, or make the schema support both versions before the window |
| A 10-minute gate described as a bake period | Published guidance measures bake time in hours and days, not minutes, so the window is buying smoke coverage while claiming bake coverage | Call it a smoke check, or split the window so dependents run on a later day |
| A dependency cycle scheduled anyway | The graph cannot be ordered, so the sequence is fiction and the first gate exposes it | Break the cycle before scheduling, with a both-versions-tolerant contract or a flag |
Output template
View source (opens in new window)Output template
Produce one document. It is the plan before the window and the record after it.
# Release cutover plan - {release_name}
**Window:** {start_utc} to {hard_stop_utc}
**Release authority:** {one named person}
**Hard stop consequence:** reaching {hard_stop_utc} with open gates puts the
full reverse path to the release authority as a decision.
## Rollback rule for this window
Rollback is a decision made by {release authority} on stated evidence.
Thresholds halt the sequence; they do not reverse it. Recovery may be
roll back, roll forward, or redeploy last-known-good, and only the release
authority chooses which.
## Dependency graph
{service -> service edges, one per line}
{parallel tracks listed explicitly}
{preconditions on out-of-window services, each with the person who confirmed it}
## Gate sequence
| Gate | Kind | Step | Depends on | Owner | Timebox (UTC) | Status |
|------|------|------|-----------|-------|---------------|--------|
## Authority table
| Role | Named person | Scope |
|------|--------------|-------|
| Release authority | | all DECISION gates and recovery calls |
| {service} owner | | {gate IDs} execution |
## Rollback triggers
| Gate | Condition that puts a decision on the table | Evaluated by | Evidence | Reverse scope |
|------|--------------------------------------------|--------------|----------|---------------|
## Reverse path
{completed ACTION gates in reverse order, one owner per step,
confirmation required between steps}
{gates marked POINT OF NO RETURN and what recovery means past them}
## Runtime log
| Time (UTC) | Gate | Action | Verdict | Evidence | Called by |
|------------|------|--------|---------|----------|-----------|Update the Status column and append to the runtime log in place as the window runs. Authority handoffs are log rows. At close, the document is the release record, and the coupling that forced a coordinated window is worth carrying into the retrospective.
Cutover sequencing - multi-team release windows
View source (opens in new window)Cutover sequencing - multi-team release windows
Deep dive for release-runbook-author. Sequences a multi-team release cutover into dependency-ordered gates: the cross-service dependency graph, a numbered gate list where every gate carries exactly one named owner, a hard timebox, and a written rollback trigger, then the reverse-order rollback path and the window hard-stop rule. Consult when two or more teams must cut over interdependent services inside one shared release window and nobody has yet written down the order, who calls each gate, or what reverses it.
What this owns, and what it does not
This owns the cross-team sequencing problem: several services, owned by several teams, that must change over in a specific order inside one shared window, where the failure mode is "team C started before team A's gate was confirmed" and "nobody knew who was allowed to say stop".
It deliberately does not cover the single-service release runbook: the pre-flight checklist, the smoke gate, canary observation thresholds and their statistical comparison, the human promote gate, the progressive rollout, and post-release verification for one service. That is the host release-runbook-author SKILL.md, run per service, inside the timebox this plan gives that service. Write it separately and reference it from the gate row. If your window contains one service, you do not need this reference at all.
Needing a cross-team cutover sequence is itself a coupling signal worth recording. DORA sets the opposite target - teams releasing "independently of the services it depends on" - and names this failure as a "big-bang" deployment that forces orchestration across many hand-offs and dependencies (DORA, loosely coupled teams (opens in new window)). Sequence the window you have, and put the coupling that forced it into the post-window record.
The rule that governs every gate
Rollback is a named human decision made on evidence. It is never an automatic metric trigger.
A threshold being crossed is an input to that decision, not the decision. Published guidance lists three distinct, non-interchangeable recoveries from a bad deployment:
Something has to choose between those, and in a multi-team window that choice also determines how many other teams reverse, so it cannot be delegated to a threshold in one service's monitoring.
What is automatic is the halt, not the reversal. When a health signal trips during a rollout phase, "the rollout should immediately halt" and an investigation into the alert determines the next course of action (Azure Well-Architected, safe deployment practices (opens in new window)). So: automation stops the sequence, a named human restarts it or reverses it.
Every gate table below therefore has an owner column and a trigger column, and the trigger text is always a condition that puts a decision in front of a person, never an action.
Step 1 - Build the dependency graph
For each service in the window, record three things:
| Field | What to capture |
|---|---|
| Consumes | Which other in-window services it calls at runtime, and whether it calls a contract that changes in this release |
| Consumed by | Which in-window services call it |
| Shared state | Datastores, schemas, queues, or caches shared with another in-window service |
Then write edges in one direction only: "X must be live before Y". If Y calls a contract that only exists in X's new version, X cuts over first. If neither consumes the other's changed surface, there is no edge and they are parallel tracks.
Three graph shapes need handling before you can sequence anything:
Record the graph as text, not as a picture, because the gate list is generated from it and the runtime log has to quote it.
Step 2 - Convert the graph into an ordered gate list
Walk the graph in dependency order. Each service contributes at least two gates, and the distinction between them is the whole mechanism:
| Gate kind | What happens | Who owns it |
|---|---|---|
| ACTION | One observable state change: a router switch, a flag flip, a scale-up, a queue drain | The owning team's named engineer |
| DECISION | A named human states go or no-go on stated evidence | The release authority, one person for the whole window |
Rules for generating the list:
The governing principle for the whole list, from the same chapter: "Changes to any aspect of the release process should be intentional, rather than accidental" (Google SRE Book, Release Engineering (opens in new window)). A gate that exists because the sequence needs it, with no owner and no evidence, is an accident waiting to be discovered at 02:00.
Step 3 - Assign exactly one named owner per gate
One human name per gate. Not a team, not a rota alias, not a Slack channel.
Write the assignment as its own table so nobody has to reconstruct it from the gate list mid-window.
Step 4 - Set the timebox and the hard stop
A timebox is the wall-clock time by which the gate must be cleared, not an estimate of how long the work takes. Estimates slip quietly; deadlines are observable.
Say this plainly to whoever reads the plan: the specific clock values are a scheduling convention, not a published standard. No source cited here prescribes "10 minutes for a router switch". What published guidance does constrain is any gate with an observation period: "Bake times should be measured in hours and days rather than minutes" and should increase per rollout group to cover different time zones and usage patterns (Azure Well-Architected, safe deployment practices (opens in new window)).
That has a sharp consequence for cross-team windows, and it is the most common thing this plan gets wrong: a gate labelled "observe for 10 minutes" is not a bake period. It is a smoke check. If a service genuinely requires a bake before its dependents proceed, the dependents do not belong in the same window. Either split the window across days, or accept in writing that the window is buying smoke coverage rather than bake coverage. Do not relabel one as the other.
To size the window:
The hard stop is one wall-clock time for the whole window, set once, in the plan. Reaching it with gates incomplete triggers the reverse-order rollback path from Step 6. Extending past it is allowed, but only as an explicit DECISION gate called by the same named release authority, recorded in the log with a reason. Emergency acceleration follows the same rule: define in advance "who can approve SDP acceleration in an emergency and the criteria that must be met for acceleration to be approved" (Azure Well-Architected, safe deployment practices (opens in new window)).
Step 5 - Write the rollback trigger for each gate
Every gate gets a trigger, written before the window, with three parts:
| Part | Example |
|---|---|
| Observable condition | "checkout smoke suite fails, or 5xx rate above 1 percent sustained 5 minutes" |
| Who evaluates it | one named person, usually the gate's DECISION owner |
| What evidence they read | the specific dashboard, log query, or suite output, named in the plan |
Trigger classes worth covering per service:
| Class | Typical condition |
|---|---|
| Smoke failure | The service's own post-cutover suite does not pass |
| Reliability signal | Error rate or availability outside the agreed band |
| Latency signal | A named percentile beyond its agreed band |
| Data correctness | Reconciliation mismatch, pipeline lag beyond an agreed bound |
| Dependency saturation | A downstream service degraded by the new traffic shape |
| External signal | Support volume, a partner report, a customer escalation |
| Timebox | The gate did not clear by its clock time |
Two things the trigger text must not do:
Rollback itself, when chosen, is a planned reversible action rather than an improvisation. That is the whole point of holding the previous version ready: "if anything goes wrong you switch the router back to your blue environment" (Martin Fowler, BlueGreenDeployment (opens in new window)), and for a gradual rollout "the rollback strategy is simply to reroute users back to the old version until you have fixed the problem" (Martin Fowler, CanaryRelease (opens in new window)).
Step 6 - Derive the reverse-order rollback path
Rollback runs the dependency order backwards. The service that cut over last reverses first.
The reason is mechanical, not stylistic. If a dependency reverses while its dependent is still on the new version, the dependent is now calling a contract that no longer exists, and you have converted a bad release into an outage. So reversal is the completed prefix of the gate list, read bottom-up.
To generate it:
Mark the irreversible gates. Some forward actions do not reverse mechanically, and the plan is dishonest if it implies they do. Stateful changes are the usual case: "Rolling back changes, especially database, schema, or other stateful component changes, can be complex" (Azure Well-Architected, safe deployment practices (opens in new window)). For any gate whose forward action writes state in a shape the old version cannot read:
Step 7 - Check the plan before the window opens
Run the plan against these invariants. Every one is a defect in the plan, not a judgment call:
| Check | Fails if |
|---|---|
| Ownership | Any gate has zero owners, two owners, or a team name instead of a person |
| Authority | More than one person owns DECISION gates without a recorded handoff |
| Ordering | Any dependent service's ACTION gate precedes its dependency's DECISION gate |
| Cycle | The dependency graph is not acyclic |
| Timebox | Any gate has no clock time, or the critical path exceeds the window |
| Hard stop | The window has no single hard-stop time, or no stated consequence for reaching it |
| Trigger | Any gate has no written trigger, or a trigger that names an action instead of a decision |
| Evidence | Any trigger cites no named dashboard, query, or suite |
| Reverse path | The rollback order is not the reverse of the forward order |
| Irreversibility | Any state-writing gate is not marked as a point of no return or explicitly cleared as reversible |
| Availability | Any owner has not confirmed availability for their gate's clock time |
Worked example
A four-service, three-team window (one dependency chain plus one parallel track) worked end to end - dependency graph, gate list, rollback triggers, and the reverse path: cutover-worked-example.md (opens in new window).
Output template
One document that is the plan before the window and the record after it - plan header, rollback rule, dependency graph, gate sequence, authority table, rollback triggers, reverse path, and runtime log: cutover-output-template.md (opens in new window).
Anti-patterns
Nine cutover anti-patterns with why each fails and its fix: cutover-anti-patterns.md (opens in new window).
Limitations
Worked example
View source (opens in new window)Worked example
Window: four services, three teams, one dependency chain plus one parallel track.
Graph. The payments API calls the new authentication contract, so authentication must be live first. The web app calls the new payments contract, so payments must be live before it. The event pipeline consumes only published events whose shape is unchanged, so it has no in-window edge and runs parallel.
auth-service -> payment-api -> web-app
event-pipeline (parallel, no in-window dependency)Gate list.
| Gate | Kind | Step | Depends on | Owner | Timebox (UTC) |
|---|---|---|---|---|---|
| G0 | DECISION | Window opens, preconditions confirmed | - | Priya (release authority) | 20:00 |
| G1 | ACTION | auth-service router switch to new version | G0 | Alice | 20:10 |
| G2 | DECISION | auth-service smoke pass, go or no-go | G1 | Priya | 20:25 |
| G3 | ACTION | payment-api router switch | G2 | Bob | 20:35 |
| G4 | DECISION | payment-api smoke pass, go or no-go | G3 | Priya | 20:50 |
| G5 | ACTION | event-pipeline router switch | G0 (parallel) | Dave | 20:35 |
| G6 | DECISION | event-pipeline lag and reconciliation check | G5 | Priya | 20:50 |
| G7 | ACTION | web-app router switch | G4 | Carol | 21:00 |
| G8 | DECISION | web-app smoke pass, window go or no-go | G7 | Priya | 21:20 |
| G9 | DECISION | Observation closes, window declared complete | G8, G6 | Priya | 22:00 |
Hard stop: 22:00 UTC. Reaching it with any gate incomplete puts the full reverse path to Priya as a decision.
Rollback triggers.
| Gate | Condition that puts a decision on the table | Evaluated by | Evidence | Reverse scope |
|---|---|---|---|---|
| G2 | auth smoke suite fails, or auth 5xx above 1 percent for 5 minutes | Priya | auth-smoke suite output, auth error-rate dashboard | auth only |
| G4 | payment smoke fails, or transaction error rate above 0.5 percent | Priya | payments-smoke output, transaction error panel | payments, then auth |
| G6 | pipeline lag above 10 minutes, or reconciliation mismatch above 0 rows | Priya | pipeline lag panel, nightly reconciliation query | pipeline only |
| G8 | web smoke fails, or checkout completion below its agreed band | Priya | web-smoke output, checkout funnel panel | all four services |
| Any | Hard stop 22:00 reached with gates open | Priya | the runtime log | all completed gates |
Reverse path if the decision at G8 is to roll back the window. Completed ACTION gates are G1, G3, G5, G7. Reversed, with the parallel track handled independently:
1. web-app router back to previous version (Carol) confirm
2. payment-api router back to previous version (Bob) confirm
3. auth-service router back to previous version (Alice) confirm
4. event-pipeline router back (parallel track) (Dave) confirmEach step confirmed before the next begins. Note that G3's forward action was marked reversible during Step 6 review; had it written transaction rows in a new shape, it would carry POINT OF NO RETURN and step 2 above would read "roll forward with hotfix" instead.
Output template
View source (opens in new window)Output template
One document. It is the plan before the release and the record after it.
# Release runbook - {service} {version}
**Promote-gate owner:** {one named person, availability window}
**Last known good artifact:** {id}
**Recovery rule:** a crossed threshold halts the phase and puts a recovery
decision to {owner}. Recovery may be roll back, roll forward, or redeploy
last known good, and only {owner} chooses which.
## Baseline - recorded {window} before deploy
| Metric | Value | Query |
|--------|-------|-------|
## Thresholds
| Metric | Absolute floor | Ratio limit (canary vs control) | Ratio limit (rollout vs baseline) |
|--------|----------------|----------------------------------|------------------------------------|
## Phase 1 - Pre-flight
| Check | Verdict | Evidence |
|-------|---------|----------|
## Phase 2 - Smoke gate
**Command:** **Environment:** **Duration:** **Result:**
## Phase 3 - Canary ({share} traffic, {window}, coverage: smoke | bake)
| Metric | Absolute floor | Ratio limit | Control | Canary | Ratio | Verdict |
|--------|----------------|-------------|---------|--------|-------|---------|
**Anomalies below threshold:**
**Verdict:** PASS | PROCEED WITH CAUTION | HALT
## Phase 4 - Promote gate
**Decision:** continue | pause | rollback
**Made by:** **At:** **On this evidence:**
**Acknowledged anomalies carried forward:**
## Phase 5 - Rollout
| Stage | Share | Window | Metrics vs baseline | Verdict |
|-------|-------|--------|---------------------|---------|
## Phase 6 - Post-release
| Metric | Baseline | Observed | Ratio | Verdict |
|--------|----------|----------|-------|---------|
**Administrative tail:** tag / changelog / notification, each with a timestamp.
## Follow-ups
- [ ] {product defects acknowledged at the promote gate}
- [ ] {runbook defects the release exposed}Worked example
View source (opens in new window)Worked example
Service checkout-api, release v1.4.5, single service, no cross-team dependencies.
Baseline recorded at 13:55 UTC, 60-minute window: 5xx rate 0.31 percent, p95 latency 240ms, checkout completion 92.1 percent, distinct error signatures 41.
Phase 1, pre-flight.
| Check | Verdict | Evidence |
|---|---|---|
CI green on release/v1.4.5 | PASS | gh run list --branch release/v1.4.5 --limit 1, run 8841 green |
| Blocking issues closed | PASS | gh issue list --label blocker --milestone v1.4.5 returns 0 |
| Migration dry run | PASS | migration-dry-run artifact at abc123 |
| Last known good retrievable | PASS | Artifact checkout-api:1.4.4 |
| Baseline recorded | PASS | Values above, 12:55 to 13:55 UTC |
| Promote owner available | PASS | Priya, 14:00 to 17:00 UTC |
Phase 2, smoke gate. npm run smoke -- --target=staging, 4m32s, 22 tests, 0 failures. PASS.
Phase 3, canary. 5 percent traffic from 14:33, 30-minute window, labelled in the runbook as smoke coverage rather than a bake. Table as shown in phase 3 above: all four thresholds green, two new error signatures observed.
NullPointerException at Cart.addItem:42 1 occurrence, absent from control
RateLimitExceeded 1 occurrence, present in control at similar rateVerdict: PROCEED WITH CAUTION. The rate-limit signature also appears in the control, so it is not attributable to the change. The null-pointer signature is absent from the control population and is therefore a real delta, below every threshold.
Phase 4, promote gate. Priya, 15:05 UTC, chose continue, on the evidence of the canary table plus the two signatures. The null pointer became follow-up item 1.
Phase 5, rollout. 25 percent at 15:08, observed 20 minutes, ratios within widened limits. 100 percent at 15:30.
Phase 6, post-release. 60-minute window to 16:30 against the 13:55 baseline: 5xx 0.33 percent (1.06x), p95 244ms (1.02x), checkout completion 92.0 percent (1.00x). Stable. Tag, changelog, and notification issued at 16:32. Follow-ups: the null pointer, plus a runbook defect - a 30-minute canary was too short to accumulate enough occurrences of the new signature for the promote gate to judge it confidently, so the canary window for this service moves to 60 minutes.
Related skills
feature-flag-experiment-validator
Validates the statistical significance of an A/B / feature-flag experiment result - computes per-metric effect size + p-value (chi-square for proportions, Welch's t-test for continuous metrics), applies a multiple-comparison correction (Bonferroni / Benjamini-Hochberg) when N>1 metric, surfaces practical-vs-statistical-significance distinction, and emits a ship/don't-ship verdict per metric. Use when an experiment has finished and someone is about to ship the winning variant off a dashboard readout, when a result rests on a small sample, or when more than one metric was compared - the rigorous version of "the variant looks better in the dashboard."
prod-canary-validator
Builds a canary-validation workflow that compares a canary deploy's metrics against the baseline (current main) - picks the metric set (error rate, p50/p95/p99 latency, business KPIs like checkout-completion), defines per-metric thresholds (absolute + relative-to-baseline), runs a statistical-comparison check (effect size + significance) over the canary's observation window, and emits a promote/rollback verdict. Use as the gate between canary deploy and full rollout - the deterministic version of "the on-call eyeballs the dashboard for 30 min.
synthetic-monitor-author
Drafts a synthetic monitor configuration for one critical user journey - picks the platform (Datadog Synthetics, Pingdom, Checkly, New Relic, etc.), authors the scripted-transaction body (Playwright-style for browser checks; HTTP-step for API checks), wires the cadence (typical 1-15 min), defines per-step assertions (DOM presence, API status, response shape) and aggregate alert thresholds (consecutive-failure count + on-call routing). Includes the RUM-coverage gap method for deciding which journeys to monitor: score real-user journeys from RUM / CrUX data by session volume times business value, diff against the existing monitor inventory, and emit a ranked gap list. Use when a critical journey needs continuous-in-production verification per ISTQB-canonical shift-right ("a test approach to test a system continuously in production"), or when synthetic coverage was never systematically derived from real usage data.