Blog · Guides · 2026-08-12

How to Write a Definition of Done for a Bug Fix (Checklist)

Klavity
TL;DRA definition of done for a bug fix is the checklist a fix must clear before the ticket closes: root cause identified, fix verified against the original reproduction, a regression test that fails before the fix and passes after, and related code paths checked. Closing on 'it works on my machine now' without that test is the single biggest driver of reopened bugs.

A definition of done for a bug fix is the fixed checklist every fix must clear before the ticket is closed: the root cause is identified, the fix is verified against the original reproduction steps, a regression test that fails before the fix and passes after is committed, related code paths are checked, and the change is reviewed. Without it, “done” means whatever the developer felt at the time — which is why fixes that “worked on my machine” come back as reopened tickets a sprint later.

The point of a definition of done (DoD) is to move the finish line from an opinion to a standard. Acceptance criteria tell you what to build for one ticket; the DoD tells you what finished means for every ticket. Below is a checklist you can adopt, why each item earns its place, and how to make the standard stick instead of becoming boilerplate nobody reads.

What should be in a definition of done for a bug fix?

A good DoD is short enough to actually run every time and strict enough to catch the ways fixes fail. These six items cover the common failure modes without turning every one-line fix into a ceremony:

  1. Root cause identified, not just the symptom. You can describe why the bug happened in one sentence. If the fix is “add a null check” but you can't say why the value was null, you've likely patched a symptom and the real defect is still live somewhere upstream.
  2. Fix verified against the original reproduction. Someone re-ran the exact steps from the ticket and confirmed the bug is gone — not “the code looks right,” but the actual repro no longer reproduces.
  3. A regression test that fails before and passes after. The test must fail on the pre-fix code and pass on the fixed code. A test that passes both ways proves nothing. This is the item that stops the bug from silently returning.
  4. Related code paths checked. The same root cause usually has siblings. If a date parser broke on one screen, check every screen that parses dates the same way before you close it.
  5. Change reviewed by someone else. A second reader catches the fix that introduces a new bug — the most common reason a “fixed” ticket reopens as a different failure.
  6. Ticket updated with the resolution. The commit or PR link, the root cause in plain language, and a note on anything intentionally left out of scope. Future-you triaging a similar bug will thank present-you.

Keep the list at this altitude. A DoD with twenty checkboxes gets skimmed; one with six gets run.

Why 'it works now' isn't done

The most expensive gap in most teams' DoD is verification-by-eyeball. A developer reads the diff, sees the logic is now correct, and marks the ticket done without ever re-running the failing case. Two things go wrong with this:

  • The fix addresses a different case than the one that broke. The reported bug and the code you changed can look like the same thing while being subtly different inputs. Re-running the original reproduction is the only cheap way to confirm you fixed this bug, not a nearby one.
  • Nothing stops it from coming back. A fix with no test survives exactly until the next refactor touches that code. Then it regresses — and now it's an escaped defect a user finds, not a caught one. Converting the bug into a regression test that sticks is what makes the fix permanent instead of temporary.

The rule of thumb: a bug is a claim that some behavior is wrong. A fix is only “done” when you can demonstrate — with a repeatable test, not a memory — that the behavior is now right and will stay right.

How to make the definition of done actually get used

A DoD written in a wiki page nobody opens changes nothing. The trick is to put the checklist where the work already happens so running it is the path of least resistance.

  1. Put it in the pull-request template. Add the checklist as literal checkboxes in your PR description template. A reviewer looking at empty boxes is far more likely to ask “where's the regression test?” than a reviewer working from memory.
  2. Put it in the bug ticket's close transition. Many trackers let you require fields or a comment when moving a ticket to Closed. Use that transition to prompt for the root cause and the test link, so closing without them takes deliberate effort.
  3. Make exceptions explicit, not silent. Some fixes genuinely don't warrant a test — a copy change, a one-off environment fix. Allow the exception, but require a one-line reason in the ticket. “No test: pure copy change” is a decision; a blank checkbox is a skipped step.
  4. Review your reopen rate against the DoD. If bugs keep reopening, the reopens tell you which checklist item is being skipped. Track it the same way you'd track any other quality signal — see how to reduce your bug reopen rate for the full loop.

A copy-paste checklist for your bug template

Drop this into your PR or ticket template and adapt the wording to your stack:

  • [ ] Root cause identified and stated in one sentence
  • [ ] Original reproduction steps re-run and confirmed fixed
  • [ ] Regression test added — fails on old code, passes on new
  • [ ] Sibling code paths with the same root cause checked
  • [ ] Change reviewed by a second person
  • [ ] Ticket updated with resolution, PR link, and any out-of-scope notes

Two of these — re-running the original reproduction and confirming a regression test fails-then-passes — depend entirely on having a faithful reproduction in the first place. That's exactly what a rich bug report gives you: when a report ships with the exact steps, the environment, and the evidence attached, verifying the fix and writing the test become mechanical instead of guesswork. Tools like Klavity Snap capture that state at report time — the URL, console, and network context — so the person closing the ticket is verifying against reality, not a paraphrase. A definition of done is only as strong as the reproduction it verifies against.

Key takeaways

  • Write one shared definition of done and pin it in your bug template, not in someone's head.
  • Require a regression test that fails before the fix and passes after — this is what stops reopens.
  • Re-run the original reproduction steps to verify, don't just eyeball the code diff.
  • Check sibling code paths the same root cause could affect before you close the ticket.

FAQ

What is a definition of done for a bug fix?

It's an explicit, shared checklist every bug fix must satisfy before the ticket moves to Closed — typically: root cause found, fix verified against the original reproduction steps, a regression test added, related paths checked, and the change reviewed. It turns 'done' from an opinion into a repeatable standard.

How is a definition of done different from acceptance criteria?

Acceptance criteria are specific to one ticket ('the export button should download a CSV'). A definition of done is a general standard that applies to every fix regardless of the ticket. You need both: acceptance criteria say what to build, the definition of done says what 'finished' means for all of it.

Does every bug fix need a regression test?

Nearly always for logic, data, and integration bugs — a fix with no test is a fix that can silently regress. The pragmatic exceptions are one-off environment issues and pure copy changes, but even then note in the ticket why no test was added so the decision is visible rather than skipped by default.

Catch bugs the moment a human sees them

Klavity: right-click bug reports, AI personas that review your product, and self-healing tests.

Get started free