How to run a retrospective
A retrospective happens after the fire is out. The people in the room already half-remember why it went wrong, and the system they are talking about will hit this same class of failure again unless something structural changes. So build the fix into the shape of the system rather than into everyone's resolve to be more careful next time. Any lesson you leave sitting in human memory is one the next engineer ends up rediscovering at the worst possible moment. This is MANIFESTO.md turned on the last phase of the loop, the phase that makes the lifecycle a loop instead of a line, because its output flows back to Triage as new work. The objective hasn't changed, but here it is restated for closing the loop: minimise what the next engineer must hold in their head to avoid this class of failure, by moving the lesson out of memory and into the structure, and keep the blame on the system, not the people. When two of the moves below pull against each other, you don't settle it with a slogan. You settle it by asking who controls the input and how wide the blast radius is. None of it is free.
Reproduce the failure before you explain it
A cause you have not reproduced is a story you are telling yourself; rebuild the failure from the signals before you reason about why it happened.
A retro run on memory tends to drift toward the most confident voice in the room rather than the most correct one. So reconstruct the run instead of recounting it (tenet XXIV). Pull the timeline together from logs, traces and metrics, and then drive the system back into the failing state using those signals. The invariant you are about to assert - say, "a null tenant id slipped past the parser" - is only a hypothesis until a reproduction confirms it. Encode that reproduction as the thing every later fix has to satisfy, so the next person can re-run your reasoning instead of having to trust your recollection.
- An escaped bug: a failing test built from the exact inputs in the logs vs "we think it was the retry path", a guess no one can re-run.
- A latency incident: the trace and the saturated resource that pinned it, rather than "the database felt slow", a feeling with no artefact behind it.
- A data corruption: the offending record replayed through the real code path, not a whiteboard arrow from someone's memory of the on-call night.
Tension: This pulls against the cost of the meeting and against simplicity (tenet XXI). A perfect reproduction of a rare race can cost more than the fix itself, so bound the effort to whatever pins the cause. And where you genuinely cannot reproduce it, write down the smallest piece of evidence that tells your hypothesis apart from its rivals rather than promoting a guess to a finding.
Ask yourself: Have I rebuilt this failure from the signals, or am I about to assign a cause I can only assert, and could the next engineer re-run my reasoning without me in the room?
Ask what the system let happen, not who did it
A competent person made a reasonable call given what they could see; the finding is the structure that made the wrong thing easy and the right thing hard.
The manifesto's premise is that correctness lives in the shape of the system and not in human vigilance, and the retro is where that premise gets tested under pressure. When process is the only thing left holding a boundary, it has to be blameless by construction (tenet XXV). Go looking for the person who erred and all you teach everyone is to hide the next one, which is how the signal goes dark. So re-aim the question at the structure. The deploy that took down production was reachable by one unguarded command, and the finding is the missing guard, not the hand on the keyboard.
- A misconfiguration: "the flag had no validation and a silent default" vs "they set it wrong", a verdict on a person that fixes nothing in the system.
- A skipped step: the runbook step that was easy to miss because nothing enforced it, rather than "they forgot", which leaves the same trap armed for the next on-call.
- A bad deploy: the absent canary that would have caught it, not the engineer who shipped on a Friday under a structure that allowed it.
Tension: This pulls against accountability, and the two get confused all the time. Blameless does not mean consequence-free for the system. Name the structural gap sharply and own it, but don't let "the structure failed" turn into a place where a real ownership question can hide. The person is not the cause. The unowned gap still needs an owner, named and dated like every other follow-up below.
Ask yourself: If the most careful engineer on the team had been at the keyboard, would the structure still have let this through, and is my finding about the system or about a person?
Find where the signal was missing or silent
The failure that fired with no alert and the error that was swallowed are the real finding; the blind spot becomes a new signal.
A retro is partly an audit of your own observability, because every incident is also a report on what you couldn't see (tenet XVIII). Ask what would have caught this an hour sooner and you'll usually land on a spot where the system was guessing instead of knowing. Worse is the error that was caught and then swallowed (tenet XIII), a failure mode that someone made invisible on purpose, so the system carried on serving wrong answers with a calm face. What you want out of this isn't "watch more carefully". It's a specific alert, or a removed catch, that makes the next instance impossible to miss.
- A silent failure: a new alert on the metric that moved first vs "we'll keep an eye on it", a promise of vigilance with no structure behind it.
- A swallowed error: delete the bare
exceptthat ate the exception so it surfaces and pages, rather than a log line no dashboard reads and no one is paged on. - A slow burn: an SLO and a threshold that fires while there is still time to act, not a graph someone might notice after the customers do.
Tension: This pulls hard against alert fatigue and against simplicity (tenet XXI). An alert that cries during every deploy just trains the team to mute it, and then you've spent signal and bought noise. Add the alert that distinguishes this failure with a low false-positive rate, tie it to an owner who has to act, and delete two stale alerts for every one you add.
Ask yourself: What single signal would have turned this incident from a surprise into a routine page, and is any failure mode here still being swallowed where no one will see it?
Convert the lesson into something that re-runs without you
The escaped bug becomes a permanent regression test and the near-miss an assertion; a lesson that lives only in memory or a slide is one you will relearn.
The reproduction you built to pin the cause isn't worth much until it is wired into the run so it fires on its own, forever, with nobody having to remember to check (tenet XXIV). A finding written in prose decays at the speed of attention. That same finding encoded as a test that fails in CI is a guardrail that outlives the meeting, the team, and the memory of the incident. So turn each confirmed cause into the cheapest mechanism that re-runs itself. The failing input becomes a regression test, and the violated invariant becomes an assertion that trips in staging long before it would ever reach production.
- An escaped bug: the reproduction promoted to a CI test that fails until fixed and stays forever vs an action item "be careful with null tenants", which no machine enforces.
- A near-miss: an assertion on the invariant that nearly broke, rather than a slide bullet that ages out of everyone's memory by the next sprint.
- A config drift: a check in the pipeline that rejects the bad shape at merge, not a wiki note that the next engineer never finds.
Tension: This pulls against the cost of brittle tests (XXI, XXIV). A test pinned to some incidental detail breaks on every unrelated change, and then someone deletes it in frustration and the lesson goes with it. So assert on the invariant that failed and not the surface of one particular run, and the guard survives refactors and carries on protecting the thing it was built to protect.
Ask yourself: Will this lesson fire on its own the next time someone gets it wrong, or does it lean on a human remembering a meeting they weren't even in?
Prefer removing the failure mode to adding a guard
The most reliable fix deletes the state that made the failure possible; the flag that cannot be misconfigured because it no longer exists beats any check you could add.
After an incident the reflex is to add things. A new validation, a new runbook step, another review gate. But every guard is more surface you have to maintain, and a guard can be forgotten or bypassed or set up wrong all by itself. Reach for subtraction first (XXI), which means removing the state before you go and guard it. The configuration that caused the outage can't be set wrong if there's no longer a knob to set, and the irreversible path can't be taken by accident once it's deleted and replaced with a reversible one (XX). A failure mode you've removed needs no test, no alert, no vigilance at all. It's just gone.
- A misconfigured flag: delete the flag and hard-wire the one correct behaviour vs adding validation to a knob that shouldn't exist, a guard you now own forever.
- A dangerous operation: make it reversible, soft-delete then purge on a TTL, so the mistake is recoverable, rather than guarding an irreversible action that one slip still triggers.
- A redundant code path: remove the second way of doing the thing, not documenting which of the two is safe and trusting everyone to remember.
Tension: This collides with the cost and risk of removal, and with reversibility (XX). Deleting a state is itself a change that can break some consumer you forgot about, so even the safe deletion still has to follow expand and contract: prove nothing depends on the state, sunset it through a deprecation window, then remove. Subtraction is the goal here. It isn't a licence to rip things out on incident-day adrenaline.
Ask yourself: Can I delete the state that made this possible instead of guarding it, and if I have to keep it, why does it need to exist at all?
Give every follow-up one owner and an expiry, or it is theatre
An unowned, undated action list changes nothing; each follow-up is a tracked issue with a named owner and a date, or it did not survive the meeting.
A retro that ends on a slide of good intentions is process with no structure behind it, and process is exactly what has to carry the load once code structure runs out (XXV). An action item with no owner is the ticket nobody dares pick up. An item with no date is the one that slips behind every shiny new feature until the next incident reopens it. So attach a single owner of record and an expiry to each follow-up the moment it gets named (XX), and then "who is doing this, and by when?" gets answered in the room rather than relitigated at the next retro about the same failure.
- A fix: a tracked issue with one named owner and a due date vs "we should add a test", a sentence with no one on the hook and no clock running.
- A guardrail: an issue with an owner and a sunset on any temporary mitigation, rather than a permanent workaround that quietly becomes load-bearing.
- An accepted risk: a dated decision record naming who accepted it and when to revisit, not a silent shrug that reads as "fixed" to everyone who wasn't there.
Tension: This pulls against the urge to fix everything, which is its own way of fixing nothing. Twenty owned action items with no slack is a list that ships none of them. Prioritise the few that close this class of failure, give those real owners and dates, and then explicitly drop the rest instead of parking them in a backlog that rots (XXI).
Ask yourself: Does every follow-up here name one owner and one date, and if this exact incident recurs in six months, will I find the work tracked or just the good intention?
Feed the loop back; the output is issues, not a document
The deliverable is triage-ready work handed to the front of the lifecycle, plus an updated runbook; the written report is the by-product.
A retro whose only artefact is a write-up has turned the loop back into a line, because the lesson terminates in a document instead of flowing into the next cycle (XXV). The real output is structural change queued as work. A regression test, a new alert, a deleted flag, a deprecation, and each one of those is a tracked issue that re-enters Triage with an owner and some sense of its blast radius, ready to be planned and shipped like any other change. You update the runbook in the same breath, because the next on-call inherits your understanding through the procedure, not through having sat in the meeting. The report is there to explain the issues. It doesn't replace them.
- A fix: a triage-ready issue with a reproduction and acceptance criteria vs a paragraph in a doc that describes the fix but never becomes work.
- A guardrail: a new alert or test entering the lifecycle as scoped work, rather than a recommendation that lives and dies in the retro's minutes.
- A runbook gap: the procedure updated so the next responder acts faster, not a lesson that stayed in the heads of the people who were on the call.
Tension: This pulls against the genuine value of the narrative and against premature closure. The story of what happened is worth writing once, as the single source the issues link back to (XIV), but don't let polishing the document stand in for filing the work, and don't close the loop before the issues that prevent recurrence have actually been triaged. The by-product must not outrank the deliverable.
Ask yourself: When this retro ends, does the front of the lifecycle have new, owned, triage-ready work and an updated runbook, or does it have only a document that no future cycle will execute?
The through-line
Every move here is the manifesto's one move wearing the retrospective's clothes: push the lesson out of human memory and into the structure of the system. A reproduced cause, a finding aimed at the system rather than the person, a missing signal made loud, a lesson wired into a test that re-runs without you, a failure mode deleted instead of guarded, every follow-up owned and dated, and the whole thing fed back as work into the front of the loop. The next engineer should avoid this entire class of failure while knowing nothing about the incident that taught it, not because they read the report, but because the test fails, the alert fires, the flag is gone, and the runbook already tells them.
Write the fix for the person who will hit this class of failure next year, who never attended the retro and never will. Make the test catch it before they do, make the dangerous state impossible to reach, and never make them relearn in production a lesson the structure could have held for them.