top of page

Definition of Done vs Acceptance Criteria

  • Shawn West
  • Mar 17
  • 10 min read

Updated: Aug 10

The story was called "Export billing report to CSV." It had a clean set of acceptance criteria: the button appears for admins, the file downloads, the columns match the on-screen table, empty ranges produce an empty file with headers. A reviewer read them, checked each one by hand, approved the pull request. The story moved to Done. It shipped that afternoon.

Three days later a customer with 40,000 invoices clicked Export and their browser tab hung for ninety seconds, then died. The export had never been tested against a realistic data volume. Nobody had decided it should be — because the check that would have caught it, "this endpoint responds within an acceptable time under production-scale data," lived in neither list. It wasn't in the story's acceptance criteria, because whoever wrote them was thinking about correctness, not scale. And it wasn't in the team's Definition of Done, because the team had never written a Definition of Done at all. The check fell into the space between two lists, and the space between two lists is where completion quietly stops meaning anything.

This is the failure that confusing Definition of Done with acceptance criteria produces. Not a dramatic outage — a slow erosion where "done" describes a checkbox someone ticked rather than a bar the work actually cleared. The two concepts sound like synonyms. They are not. They answer different questions, they live at different levels, and the moment you treat them as one thing, checks start slipping through the gap or piling up as boilerplate nobody reads. This article is about how to keep them separate on purpose, and how to sort any candidate check to the level where it will actually be enforced.

Two questions, not one

Acceptance criteria answer: is this specific story doing what it was asked to do? They are written per story, by whoever specified it, and they describe behavior a stakeholder cares about. "Users with two or more workspaces see a switcher." "Switching preserves the current URL context." When the acceptance criteria pass, the feature does what the story promised.

Definition of Done answers a different question: has this work met the standard every piece of work on this team must meet, regardless of what it does? It is written once, by the team, and it applies to every story identically. "Code reviewed by someone who didn't write it." "Unit tests added and the suite is green." "No new accessibility regressions." None of those mention what the story is about, because they are true for all of them.

A story is done when both are satisfied — when it does the specific thing it was asked to do (acceptance criteria) and it clears the team's universal bar (Definition of Done). Miss the distinction and you get one of two failure shapes. Either the baseline checks get copied into every story's acceptance criteria, where they bloat the list until reviewers skim past them and the baseline stops being enforced — or there's no baseline at all, and every author re-decides what "good enough" means, which is how the CSV export shipped.

Audit you can run today: open your last five closed stories. For each, find where "code was reviewed" is recorded. If it's written into the acceptance criteria of all five, you're maintaining the same sentence in five places — boilerplate. If it's written nowhere and you're trusting memory, you have no baseline. Either way, you've found the gap this article closes.

The sorting question

There is exactly one question that sorts any candidate check to the right level:

Does this apply to every story, or only to this one?

If the honest answer is "every story" — every story needs code review, every story needs a green test suite, every story needs to not break the keyboard-navigation that already works — it belongs in the Definition of Done. If the answer is "only this story" — only the workspace-switcher story needs a visible switcher, only the export story needs matching columns — it belongs in the acceptance criteria.

The question sounds trivial until you apply it to a genuinely ambiguous check. Take performance. "The workspace switch completes in under 500 milliseconds" — is that a Definition of Done item or an acceptance criterion? Run the sorting question. Does every story have a 500ms budget? No. A nightly batch job doesn't. A settings page doesn't. So the specific threshold is an acceptance criterion for this story. But "no story introduces a performance regression against its own stated budget" — that applies to every story, so that framing is a Definition of Done item. The threshold is story-specific; the discipline of having and honoring a threshold is universal. Same word, two levels, and the sorting question is what tells them apart.

Take accessibility, which trips up most teams. "No new accessibility regressions" applies to every story — it's a Definition of Done item. "The workspace switcher is reachable and operable by keyboard" applies only to the story that builds the switcher — it's an acceptance criterion. If you collapse these into one line, you either lose the baseline (nobody guards the existing a11y) or you lose the specific (nobody checks the new control). You need both, at both levels.

Audit you can run today: take the three checks your team argues about most — usually performance, accessibility, and security. For each, write the universal version and the story-specific version as two separate sentences. If you can't, you haven't finished thinking about the check.

A developed example: "User can switch between workspaces"

(Developed example — composite scenario.)

Here is the full separation done on one real story, end to end, so you can copy the move rather than the conclusion.

The story: "As a user who belongs to more than one workspace, I can switch between them without logging out." A mid-sized SaaS team picked this up in a sprint. Here's how the two lists came out.

Acceptance criteria (this story only):

  • A workspace switcher is visible to any user who belongs to two or more workspaces.

  • Users with exactly one workspace do not see the switcher.

  • Selecting a workspace preserves the current URL context where an equivalent resource exists in the target workspace, and falls back to that workspace's home when it doesn't.

  • The currently active workspace is clearly indicated in the switcher.

  • The switcher is operable by keyboard alone, including opening it, moving between options, and selecting one.

Definition of Done (every story on this team):

  • Code reviewed and approved by someone other than the author.

  • Unit tests added or updated; full suite green.

  • Integration tests for touched paths passing.

  • No new accessibility regressions across the app.

  • No new high or critical security findings.

  • User-facing docs updated if behavior changed.

  • Deployed to staging and smoke-checked.

Now the part that matters — the check that falls in the gap. During refinement someone raised it: "When a user switches workspaces, does the new session actually scope their permissions to that workspace, or do they keep the permissions they had in the previous one?" That is a real, load-bearing check. And notice where it wants to live. It's not naturally an acceptance criterion, because the story was written about navigation — being able to move between workspaces — not about authorization. And it's not obviously a Definition of Done item either, because "permissions re-scope on context switch" doesn't apply to a story about CSV columns.

Left alone, this check ships nowhere — exactly the CSV-export failure, except this one leaks data across tenant boundaries instead of hanging a browser. So you resolve it by relocating it to the level that will enforce it. The permission-scoping check is specific to workspace switching, so it becomes a new acceptance criterion on this story: "After switching, the user's effective permissions match their role in the target workspace, not the source workspace." One sentence, added to the right list, and the check now has an owner and a place.

But the lesson generalizes, and that's the second move. The team asked the sorting question about it in reverse: is "context switches must re-establish the correct authorization scope" true only here, or true anywhere the app changes a security context? It's true anywhere. So a universal version was promoted into the Definition of Done: "Any change of security or tenant context re-establishes authorization from scratch; no permission carries across the boundary." The specific check guards this story. The promoted principle guards every future story that changes context — including ones nobody has written yet. That is the whole discipline in one motion: the specific instance lands in acceptance criteria, and if it reveals a rule that's universal, the rule is lifted into the Definition of Done.

Audit you can run today: take your current in-progress story and ask one question of it out loud in refinement — "what check does this story need that isn't obviously about the feature?" The CSV export died for want of that question. It costs thirty seconds.

Sorting the common checks

Here's the sorting question applied to the checks teams argue about most. Read the middle column as the answer, and the right column as how you know.

Check

DoD (every story) or AC (this story)?

The tell

Unit tests added and suite passing

DoD

Every story that touches code needs it; nothing about the feature changes the answer

Code reviewed by a second person

DoD

Universal quality bar; independent of what the story does

"User with 2+ workspaces sees a switcher"

AC

Names a behavior only this story produces

No new accessibility regressions

DoD

Guards the whole app on every change

"Switcher is keyboard-operable"

AC

A specific new control this story introduces

Endpoint responds under 500ms at scale

AC

The threshold is per story; a settings page and a batch job have different budgets

No performance regression vs the story's budget

DoD

The discipline of honoring a stated budget is universal

Legal/compliance review completed

AC (usually)

Only stories touching regulated data need it; forcing it on all bloats the baseline

Deployed to staging and smoke-checked

DoD

Every story ships the same way

Two rows to sit with. Performance appears twice on purpose — the threshold is a story-specific criterion, the discipline is a universal one, and separating them lets a team enforce speed without pretending every story has the same budget. And compliance review is an acceptance criterion, not a Definition of Done item for most teams. Put "compliance review completed" in the Definition of Done and a one-line copy tweak can't close until someone signs off on a review it never needed — so within two sprints the team is closing stories with that box unticked "because it obviously doesn't apply." The moment a Definition of Done item gets routinely waved through, the whole list loses authority; people learn it's negotiable. Keep the baseline to things genuinely true for every story, and route the conditional checks to the stories that trigger them.

Audit you can run today: take your current Definition of Done and run every line through the sorting question. Any item where the honest answer is "well, not every story" is miscategorized — move it to a template acceptance criterion that gets pulled in only when the condition applies.

Building and maintaining the two lists

The Definition of Done is a team artifact, so it's built by the team, not decreed. Get the people who do the work in a room, propose each candidate item, and pass it through one filter: is this true for every story, and will we actually block a story that misses it? If the answer to either half is no, it doesn't go in. An aspiration nobody enforces isn't a Definition of Done item; it's decoration that teaches the team to ignore the list. Then make it visible — in the pull-request template, the ticket, wherever the work happens — because a Definition of Done that lives in a wiki nobody opens is enforced by memory, and memory is what failed the CSV export.

Acceptance criteria are built per story during refinement, by whoever specifies it, ideally in a form the team already shares — Given/When/Then keeps them testable and unambiguous. The refinement conversation is also where the gap check surfaces, if someone asks the question: what does this story need that isn't obviously about the feature?

Both lists rot if left alone. A Definition of Done that hasn't changed in two years is almost certainly not describing how the team actually works — it's describing how they worked before the new test framework, the new compliance requirement, the class of bug that keeps escaping review. Update it when a new capability becomes standard, when a defect pattern repeats, or when a check you keep adding to individual stories turns out to be universal (that's the signal to promote it, as the permission-scoping check was promoted). Verify both lists where the work is actually inspected — pull-request review and merge — not at sprint's end, because "done" checked at sprint review is checked too late to fix anything. This is the same enforcement logic behind quality gates that actually gate: a bar only holds if something blocks on it at the moment of passage.

Audit you can run today: find the last check you added to an individual story's acceptance criteria. Ask whether you've now added the same check to three or more stories. If you have, it's not story-specific — it's a Definition of Done item wearing a disguise, and it belongs in the baseline where you'll stop re-typing it.

Where this connects

The separation you've just drawn has neighbors. Acceptance criteria are what a user-acceptance test exercises when it's run well — the stakeholder confirming the story did the specific thing they asked for. The Definition of Done is closer to the reason we test at all: the standing guarantee that every unit of work cleared the same bar, so that "done" is a claim about quality rather than about effort. Confuse the two lists and both disciplines inherit the confusion — the UAT tests baseline items it shouldn't, or the quality guarantee has holes shaped exactly like the checks that fell in the gap.

The discipline reduces to one repeatable move. Every time a check comes up — in refinement, in review, in a postmortem — ask whether it applies to every story or only this one. Universal checks go to the Definition of Done, where they're enforced once for everyone. Specific checks go to the story's acceptance criteria, where they're owned by the person who wrote them. And when a specific check turns out to hide a universal rule, promote the rule. Do that consistently and the space between the two lists — the space where a shipped-and-hung CSV export lives — closes.

Sources

  • The "Export billing report" opening and the "User can switch between workspaces" walkthrough are illustrative composite scenarios, not accounts of specific named clients. The 500ms threshold and the 40,000-invoice figure are illustrative, chosen to make the mechanism concrete.

  • Definition of Done as a shared team agreement, and acceptance criteria as story-level conditions of satisfaction, follow standard Scrum and agile-requirements usage (Scrum Guide; common Given/When/Then acceptance-criteria practice).

Keep learning. This article is part of the Software Testing Foundations path in the ShiftQuality Learning Center.

bottom of page