Most teams have a recurring bug: one that came back. The first fix looked right, the tests passed, the PR merged. Months later, a refactor reintroduced the same defect, and someone spent an afternoon rediscovering what the team had already learned.
A normal postmortem is built for outages. It is too heavy for a bug that is “just” fixed twice. What you need is smaller: a bug autopsy, one page that answers one question. Why did the first fix not hold? Here is the template we would use, followed by a worked example.
What is a bug autopsy?
A bug autopsy is a one-page record written when a bug is fixed for the second time, or when you suspect it will come back. It captures the pattern, not just the patch, so the next reviewer can recognize the same defect even when the code looks different.
The template
Copy this into your repo as docs/bug-autopsies/TEMPLATE.md or into a PR description:
# Bug autopsy: <short name of the pattern>
**First seen:** PR #__ **Fixed in:** PR #__ **Came back in:** PR #__
## 1. The pattern (one sentence, no file names)
What does the code do wrong, in general terms?
## 2. Why the first fix did not hold
- [ ] No test covered the failing case
- [ ] The fix patched one call site, not the pattern
- [ ] The fix was reverted or overwritten by a refactor
- [ ] Reviewer did not know the bug had happened before
- [ ] Other: ____
## 3. How to recognize it next time
Two or three signs a reviewer can spot in a diff.
## 4. The lock
- Regression test added: <path or PR>
- Review rule / lint rule / checklist item added: <where>
## 5. Where it lives
Files and modules where this pattern is likely to reappear.
A worked example (illustrative, not a real incident)
This is a made-up case to show how the template reads.
Pattern: a function reads a field from an object that can be null, and the null path was only guarded at one call site.
Why the first fix did not hold: the fix added a null check at the caller that crashed, and no test covered the null input. Months later a refactor moved the call and dropped the check. Reviewers saw a clean diff.
How to recognize it: a helper that formats or summarizes an optional record; a refactor that moves a call out of a guarded block; a test file with no null case.
The lock: a unit test with a null record, and a checklist line in the PR template: “optional inputs to formatting helpers have a null test”.
Notice what is missing: no blame, no timeline of the incident, no file-by-file diff. The value is in sections 1 to 4.
Make it stick: three habits
- Write the autopsy in the fix PR, while the context is fresh. Ten minutes now can save an afternoon later.
- Search before you approve. Before approving a refactor of a risky module, search past autopsies and fix PRs for the files it touches.
- Turn section 4 into something executable. A test or a lint rule outlasts a document nobody reopens.
Where automation can help
The hard part of an autopsy is not writing it. It is remembering to look for it. Reviewers are human, and the PR in front of them looks new.
bugweg is a GitHub App built around that gap. Its Bug Genealogy feature compares each issue it flags with the bugs it flagged earlier in the same repository, using embeddings and a similarity threshold. When there is a match, the annotation says it is a recurring bug, how many times it has been seen in the repo, which PR it was first seen in when that is known, and how similar it is to the earlier occurrence. You can read the longer explanation in What Is Bug Genealogy?
Limits, so you can judge it fairly:
- It only knows bugs bugweg itself flagged in that repo, not every bug your repo ever had.
- It runs on PRs that reach the AI review stage, not on every PR.
- A match is a prompt to look, not a verdict. It does not catch every bug and does not replace human review.
- An optional block rule (the Recurrence Firewall, see the features page) only blocks a merge if your branch protection requires the bugweg check.
The trial is one-time: 3 free PR reviews per developer (each new push to a pull request counts as a review). A card is required to start; it is verified with a $0 authorization and is not charged during the trial. Nothing is charged unless you choose a paid plan and complete checkout yourself, and a paid plan is needed to keep getting reviews after the trial. See pricing for plans; all prices are in Canadian dollars (CAD), plus applicable taxes.
If you only take one thing from this post, take the template. It works with or without any tool.
GitHub is a trademark of GitHub, Inc. bugweg is not affiliated with, endorsed by or sponsored by GitHub.
See it on your own repo. 3 free PR reviews per developer, one time (each push counts as a review). Card required to start; nothing is charged unless you choose a paid plan.
Install on GitHub