top of page

Postmortem Template That Encourages Honesty

Shift Quality Editorial
Jun 21
13 min read

Updated: Oct 18

The first draft of most postmortems is accurate and almost useless. Every section is filled in and every timestamp is right. The root cause is "a migration locked the orders table", and the action items are "lower the alert threshold" and "be more careful with migrations". Nobody lied. But the document leaves out the two things that would actually change something: that support knew about the outage 18 minutes before anyone was paged, and that the reviewer approved the migration using a rule that's only true for some defaults.

A template can't make a team honest. What it can do is ask questions that are hard to answer without honesty, and leave visible gaps where honesty is missing. This post gives you that template. It shows which sections do the work and how to check whether your postmortems are hiding things, then runs one composite incident through it twice, as a first draft and as an honest one.

Audit your last five postmortems first

Before you change the template, find out what your current one lets people leave out. Take your last five postmortems and score each on four checks:

  1. Unexplained gaps. Does the timeline have a gap of more than ten minutes between two events with no line saying what happened in between? An unexplained gap is usually where the most useful finding is.

  2. Luck written down. Does a "where we got lucky" section exist, and does it name something specific?

  3. Action types. Is each action labelled by what it changes: prevent the cause, detect it sooner, or reduce the impact? Count how many are training, reminders or "be more careful".

  4. Closure. Of the actions marked high priority, how many were closed within the time you'd committed to? Atlassian sets a target of four or eight weeks for its "priority actions", depending on the service. If you have no target, that's your first finding.

You can do this in an hour with a spreadsheet. What you're looking for is a pattern. If three of the five have unexplained gaps, the template is letting people skip the hard part of the timeline. If most actions are training, the analysis is stopping at the person instead of the system. Fix the sections the audit points to first.

The template

The structure below follows the sections Google uses in its published example postmortem, with three additions that close the gaps the audit usually finds: detection timings, a column that turns luck into risk, and a place for questions the team couldn't answer.

# Postmortem: [short, factual title]

Incident date: YYYY-MM-DD        Postmortem date: YYYY-MM-DD
Authors: [names]                 Status: Draft / In review / Final
Reviewers: [at least one person outside the team]

## Summary
[Two or three sentences: what broke, for how long, who was affected.]

## Impact
- Who was affected, and how: [customers, internal users, partners]
- Measured impact: [failed requests, failed orders, data affected]
- Duration: start / detected / paged / mitigated / resolved

## Background and glossary
[Anything a reader from another team needs to follow the document.]

## Detection
- How we found out: [alert / customer / support / chance]
- Time to detect: [start -> first person aware]
- Time to page: [first person aware -> responder engaged]
- If detection was manual, why didn't monitoring catch it?

## Timeline
[Timestamps, facts only. Every gap longer than 10 minutes gets a line
saying what was happening and what people could see at the time.]

## Trigger
[The event that started it.]

## Contributing conditions
[Everything that made the trigger dangerous or the recovery slow.
List several. Ask of each one: what let this through?]

## What went well
## What went wrong

## Where we got lucky
- [Lucky break] -> If it hadn't held: [what happens] -> [action or accepted risk]

## Action items
- [Action] - Type: prevent / detect / mitigate - Owner: [one name]
  - Due: [date] - Priority: [P0-P3] - Ticket: [link]

## Open questions
[What we still don't know, and who is finding out.]

Three things about it are deliberate. Detection gets its own section with two timings, because a slow detection can account for much of an outage and is the easiest thing to blur in a timeline. The trigger and the contributing conditions are separate headings, so a single "root cause" can't take up the whole analysis. And every lucky break has to end in an action or a written decision to accept the risk.

The timeline is where honesty is earned

A timeline that just lists events reads as a smooth sequence. Honesty shows up when you look at the gaps between events.

Take the composite incident used throughout this post. The first draft's timeline said: 14:02 checkout errors begin, 14:23 on-call paged, 14:43 recovered. That's accurate. But when the reviewer asked what happened between 14:02 and 14:23, it turned out that a customer complaint reached the support channel at 14:05. Support knew there was a problem 18 minutes before engineering did. They had no way to page on-call, and the checkout alert was set to fire only after errors had been sustained for 20 minutes.

That one question changes the actions. The first draft proposed lowering the alert threshold, which is reasonable. The honest draft adds a route for support to page on-call directly, which would have cut detection from 21 minutes to about three, whatever the alert threshold was.

The test: split the duration into the five timestamps in the template (start, detected, paged, mitigated, resolved) and look at the longest of the four gaps. If your actions don't reduce the longest gap, you're fixing the wrong part of the incident. The Google SRE book lists "a monitoring failure (which usually implies manual incident discovery)" as a reason to write a postmortem in its own right. If a person found the problem before your monitoring did, treat that as a finding, not a detail.

Keep the trigger apart from the conditions

The trigger is what happened. The contributing conditions are why it was able to do damage. Google's example postmortem keeps them apart: the trigger is a "latent bug triggered by sudden increase in traffic", and the root causes are written up separately as a cascading failure caused by a resource leak combined with high load.

In the composite, the trigger was a migration that added a column with a default value to an orders table with 80 million rows. The reviewer approved it because "adding a column with a default doesn't rewrite the table any more". That's true for a constant default: PostgreSQL 11 removed the rewrite for that case. But the default here was gen_random_uuid(), which is volatile. The PostgreSQL documentation says a volatile default "will cause the entire table and its indexes to be rewritten". ALTER TABLE takes an ACCESS EXCLUSIVE lock unless stated otherwise, so checkout writes queued behind the rewrite for as long as it ran.

If the postmortem stops at "migration locked the table", the action is "be careful with migrations". The honest version lists the conditions:

  • The review rule people relied on was only partly true, and nobody had written down the exceptions.

  • Nothing checked migrations automatically before deploy.

  • Migrations ran at peak hours.

  • Support had no route to on-call.

  • Nobody on shift knew whether cancelling the migration was safe, so cancelling took eight minutes of discussion.

The test: for each action, ask "would this stop the next incident of the same shape, caused by a different change?" A linter that blocks table-rewriting changes on large tables passes. Reminding the reviewer doesn't. If none of your actions pass, your analysis stopped at the trigger.

Where we got lucky, written down as risk

This is the section that most clearly separates honest postmortems from defensive ones, and Google's example postmortem includes it ("Where we got lucky"). Luck is a safeguard you didn't build that happened to work this time. Writing it down shows the gap before an incident where the luck doesn't hold.

The composite's first draft listed luck as reassurance: the incident happened during business hours, traffic was about 60% of peak, and no promotion was running. The honest draft takes each one a step further:

  • Business hours. Support was staffed and saw the complaint at 14:05. If it had been Friday at 17:00: the support channel is quiet, and detection falls back to the 20-minute alert. Action: add a support-to-on-call paging route, and set the checkout alert to fire after five minutes.

  • 60% of peak traffic. If it had been peak: more failed checkouts per minute for the same lock duration. Action: move migrations on large tables out of peak hours.

  • No promotion. Accepted risk, written down with the reason: once the first two actions are done, a promotion increases the impact but doesn't change how fast the team detects or recovers.

The test: every line in the section ends in an action or an accepted risk with a name next to it. A lucky break with neither is a future incident you've already written down.

Language that keeps the document honest

People stop writing what actually happened as soon as the document starts to read like a judgement of them. The Google SRE workbook includes a deliberately bad postmortem, and it calls out exactly this kind of language: "careless ignorance", "which is ridiculous", "I can't believe we survived this one!!!". It notes that animated language and dramatic descriptions of events "distract from the key message and erode psychological safety". The same bad example also proposes "Train humans not to run unsafe commands" as an action to prevent recurrence, which shows what happens to the actions once the language has made the person the problem.

The test: before review, search the draft for "should have", "failed to", "forgot", "careless", "human error" and exclamation marks. Rewrite each one as what the person could see at the time, and what made their action look reasonable. "The reviewer failed to spot the volatile default" becomes "The review checklist said column defaults are safe and didn't mention volatile ones." The second sentence leads to a fix. The blameless incident analysis guide covers the interviewing and barrier analysis behind this.

Actions typed by what they change

The SRE workbook's example postmortems give every action item a type: prevent, detect or mitigate. Typing the actions makes two problems obvious straight away. If every action is "prevent", the team is betting that it won't happen again and doing nothing to make the next one shorter. If every action is "detect", nothing is being fixed.

The composite's honest action list:

Action

Type

Owner

Due

Migration linter fails any change that rewrites a table over 1M rows

Prevent

Platform lead

2 weeks

Migrations on large tables run outside peak hours

Prevent

Release manager

1 week

Support can page on-call from the support tool

Detect

Support lead

2 weeks

Checkout error alert fires after 5 minutes, not 20

Detect

On-call owner

1 week

Runbook step: how to cancel a running migration and confirm the rollback

Mitigate

Database owner

3 weeks

Each action has one owner, a due date and a ticket in the team's normal backlog, not just in the document. The workbook makes the incentive point too: if you reward engineers for writing postmortems but not for closing the actions, you risk "an unvirtuous cycle of unclosed postmortems". Review open postmortem actions every month, and close, re-prioritise or escalate each one.

How much postmortem to write

Not every incident needs the full template, but you need to decide which ones do before something goes wrong. The SRE book's common triggers are a good starting list: user-visible downtime or degradation beyond a threshold, any data loss, on-call intervention such as a rollback or rerouting traffic, a resolution time above a threshold, and a monitoring failure. It adds that any stakeholder can ask for one.

Tier

Use when

What gets written

Review

Full postmortem

Any of the triggers above, security incidents, or work across several teams

The whole template

A reviewer outside the team, then shared widely

Short review

Internal impact only, one team, quick recovery

Summary, detection timings, timeline, actions

Team lead

Incident note

Recovered on its own, no user impact

A paragraph in the incident channel, linked from the ticket

None, but repeats get promoted

The trade-off is cost against learning. The full template takes real effort, and if every small incident gets one, people will start filling it in with the minimum. The exception to the tiers: if an incident note describes something that has happened before, promote it to a full postmortem. A repeat incident means the earlier actions didn't work.

Review before you share

The SRE book puts it bluntly: "An unreviewed postmortem might as well never have existed." Its review criteria make a good checklist for a reviewer from outside the team:

  • Was key incident data collected?

  • Is the impact assessment complete?

  • Was the root cause sufficiently deep?

  • Is the action plan appropriate, and are the fixes at the right priority?

  • Was the outcome shared with the people who need it?

Sharing changes how people write. A document that will be read by other teams, or picked as Google's "postmortem of the month" in an organisation-wide newsletter, gets written with more care and less defensiveness than one that stays in the team's wiki. Share the full version across engineering, and a summary with product, support and leadership. For incidents customers noticed, write a customer version focused on impact and what's changing.

A worked example

Here is the honest version of the composite postmortem from the sections above. It's a composite: the service, numbers and people are illustrative.

# Postmortem: Checkout errors during orders table migration

Incident date: 2026-03-15        Postmortem date: 2026-03-18
Authors: on-call engineer, database owner
Reviewers: payments team lead (outside the team)
Status: Final

## Summary
A migration adding a column to the orders table rewrote the table under
an exclusive lock. Checkout writes blocked for 41 minutes; about 1,200
checkouts failed.

## Impact
- Web checkout customers: about 1,200 failed checkouts
- Duration: start 14:02 / detected 14:05 / paged 14:23 /
  mitigated 14:38 / resolved 14:43

## Detection
- How we found out: a customer complaint in the support channel
- Time to detect: 3 minutes (14:02 -> 14:05)
- Time to page: 18 minutes (14:05 -> 14:23). Support had no paging
  route; the checkout alert needs 20 minutes of sustained errors.

## Timeline
- 14:00 Migration starts: ADD COLUMN public_id uuid NOT NULL
        DEFAULT gen_random_uuid() on orders (80M rows)
- 14:02 Checkout writes start queuing behind the lock; errors begin
- 14:05 First customer complaint reaches the support channel
- 14:05-14:23 Support escalates in the team channel; nobody on shift
        is watching it. No paging route exists.
- 14:23 Checkout alert fires; on-call paged
- 14:30 Lock traced to the migration session
- 14:30-14:38 Discussion: is cancelling safe mid-rewrite? No runbook
        step; the database owner confirms the rewrite rolls back.
- 14:38 Migration cancelled
- 14:43 Checkout error rate back to normal

## Trigger
A migration added a column with a volatile default, which rewrites
the whole table under an ACCESS EXCLUSIVE lock.

## Contributing conditions
- Review relied on "defaults don't rewrite any more", which is only
  true for constant defaults (PostgreSQL 11 and later)
- No automated check for table-rewriting changes before deploy
- Migrations allowed during peak hours
- Support had no route to on-call
- No runbook step for cancelling a running migration

## What went well
- Support spotted the problem within 3 minutes
- The cancelled migration rolled back cleanly

## What went wrong
- 18 minutes between support knowing and on-call being paged
- 8 minutes spent establishing whether cancelling was safe

## Where we got lucky
- Business hours -> Friday 17:00: detection falls back to the
  20-minute alert -> support paging route + 5-minute alert
- 60% of peak traffic -> at peak, more failures per minute ->
  large-table migrations moved out of peak hours
- No promotion running -> accepted risk (support lead), because
  detection and recovery no longer depend on it

## Action items
- Migration linter blocks table rewrites over 1M rows
  - Type: prevent - Owner: platform lead - Due: 2026-04-01 - P0
- Large-table migrations outside peak hours
  - Type: prevent - Owner: release manager - Due: 2026-03-25 - P1
- Support can page on-call from the support tool
  - Type: detect - Owner: support lead - Due: 2026-04-01 - P0
- Checkout alert at 5 minutes of sustained errors
  - Type: detect - Owner: on-call owner - Due: 2026-03-25 - P0
- Runbook step for cancelling a migration and confirming rollback
  - Type: mitigate - Owner: database owner - Due: 2026-04-08 - P1

## Open questions
- Do other services' review checklists carry the same default rule?
  (platform lead, by 2026-03-25)

Compare it with the first draft from the opening. The trigger is the same and so are the timestamps, but there are now five actions, three types of action, and an open question pointing at other teams. What changed is that the template wouldn't accept an unexplained gap, a single root cause, or luck written down as reassurance.

An honest postmortem comes from sections that are hard to fill in without honesty. Audit the last five postmortems for unexplained timeline gaps, whether luck is written down, action types and closure. In the composite checkout incident, the duration splits into start 14:02, detected 14:05, paged 14:23, mitigated 14:38 and resolved 14:43; the 18-minute gap between support knowing and on-call being paged was the largest. Keep the trigger, a migration with a volatile default, apart from five contributing conditions. Write each lucky break as a risk ending in an action or an accepted risk. Type every action as prevent, detect or mitigate. The first draft had two actions; the honest draft had five across all three types.

What to do this week

Score your last five postmortems on the four audit checks: unexplained gaps, luck written down, action types, and closure within a target. Add the Detection section, the trigger and contributing conditions split, the "if it hadn't held" column and the action type field to your template. Write down your criteria for a full postmortem before the next incident, and name a reviewer from outside the team. For the next incident, pair the postmortem with the incident response runbook so the mitigate actions land somewhere responders will find them at 3am. The Engineering Templates & Playbooks guide has the rest of the template set.

Final takeaway

An honest postmortem doesn't depend on people deciding to be honest. It comes from a document where the easy answers don't fit. Split the duration and fix the largest gap. Keep the trigger apart from the conditions that made it dangerous. Treat every lucky break as a risk that needs an action or a named decision. Type each action by what it changes, give it one owner and a ticket, and check it gets closed. Then have someone from outside the team review it and share it widely, because people write more carefully for readers they don't know.

Sources

  • Lunney, J. and Lueder, S., "Postmortem Culture: Learning from Failure", chapter 15 of Site Reliability Engineering: How Google Runs Production Systems (O'Reilly, 2016): common postmortem triggers, including "a monitoring failure (which usually implies manual incident discovery)"; define criteria before an incident; "An unreviewed postmortem might as well never have existed"; review criteria; "postmortem of the month". https://sre.google/sre-book/postmortem-culture/

  • Site Reliability Engineering (O'Reilly, 2016), Appendix D, "Example Postmortem": separate Root Causes and Trigger fields; Lessons Learned split into what went well, what went wrong and "where we got lucky". https://sre.google/sre-book/example-postmortem/

  • Beyer, B., Murphy, N. R., Rensin, D. K., Kawahara, K. and Thorne, S. (eds.), "Postmortem Culture: Learning from Failure", chapter 10 of The Site Reliability Workbook (O'Reilly, 2018): the bad example postmortem and its language; action items typed prevent, detect or mitigate; reward action item closeout to avoid "an unvirtuous cycle of unclosed postmortems". https://sre.google/workbook/postmortem-culture/

  • Atlassian, Incident Management Handbook, "Postmortems": designated "priority actions" have an agreed SLO of 4 or 8 weeks depending on the service. https://www.atlassian.com/incident-management/handbook/postmortems

  • PostgreSQL Global Development Group, PostgreSQL 11 Release Notes: ALTER TABLE can add a column with a non-null default without a table rewrite when the default is a constant. https://www.postgresql.org/docs/release/11.0/

  • PostgreSQL documentation, "ALTER TABLE": an ACCESS EXCLUSIVE lock is acquired unless noted; adding a column with a volatile default causes the entire table and its indexes to be rewritten. https://www.postgresql.org/docs/current/sql-altertable.html

Related reading

Keep learning. This article is part of the Advanced Quality Engineering path in the ShiftQuality Learning Center. Take quality from a team chore to an organizational property.

bottom of page