Definition of Done
Done means the checklist passed.
Done means the checklist passed. Coverage, lint, type-check, format, dup-check, AC, a11y: written down, machine-checked, signed off. Judgement is too lenient on a Friday afternoon. The list is the gate and the gate does not flex.
Opinion
Every team I have worked with that didn't have a written Definition of Done shipped half a feature and called it done at the same rate and the rate did not fall with seniority. The most senior engineer on a Friday afternoon shipped a half-typed change with the same conviction as the most junior on Monday. Gawande's point about ICU central lines is the same point about feature merges: the experts know all the steps, the experts skip steps when they're tired and the checklist they hate at first is the checklist that saves them later.1
I want to plant a flag on this: the DoD is the back half of P2 Spec-First Execution. The AC at the start is the brief. The AC at the end is the gate. If the AC was written verifiably per AI4 Verifiable Specs, the DoD has something machine-readable to verify against. If not, the DoD becomes judgement, which is the failure mode the row is built to displace. The DoD without a verifiable AC is theatre. The AC without a DoD is aspiration.
The row earns its weight at the agent layer. An LLM left unchecked will declare itself done at the moment the code compiles. That is the prior, and no amount of CLAUDE.md scolding changes the prior. The fix is not to ask the agent to be more careful. The fix is to give the agent a list it has to tick.10 Reflexion, Self-Refine, CRITIC: all the same shape. The checklist is the external signal the model corrects against.2 Without it the agent grades itself. With it, the agent has something it cannot fake.
Copy a note and link
Grab this short comment and drop it into a PR comment or an LLM chat to prompt the right change.
Done means the checklist passed. Coverage, lint, type-check, format, dup-check, AC verified, a11y verified: every row machine-checked, every row signed off. Judgement is too lenient on a Friday afternoon. The list is the gate and the gate doesn't flex. /tenet/definition-of-done/P3
AI eyes only
Rule: done means the checklist passed. Define done as a single command.
Reject: declaring done because the build is green. Reject: declaring done before lint, type- check, tests, format, duplication, coverage all pass. Reject: skipping the script “to save time”.
Generate: run npm run quality (or the project equivalent) before handing back. Paste the result. If the script fails, fix and re-run. Done is the script's exit code, not the agent's opinion.
Diagnostic: cite the exit code of the quality script in every handover. No exit code, no done.
Why?
- A written checklist is more reliable than personal judgement. Senior engineers on Friday afternoons skip the same steps as junior engineers on Monday mornings. The list is the corrective.
- The AC is the bookend. P2 Spec-First Execution writes the AC at the start; P3 verifies it at the end. The two rows compose. Without P3, the AC is aspiration.
- Total coverage is one row on the list rather than a tenet of its own. Promoting it inflated the canon. Absorbing it sharpens the list and matches Fowler and Marick on coverage as diagnostic, not target.
- The DoD is the agent's grader. The model declares itself done when the build compiles; the list is what stops it. CRITIC and the evaluator-optimizer pattern both ground the agent in something machine-checkable.
- PRs merge faster when the reviewer can read down the DoD instead of reverse-engineering the intent. Each row of the list is a row the reviewer doesn't have to think about.
- DORA / Accelerate names the DoD as one of the capabilities that predicts delivery-performance. The list isn't bureaucracy. It's a measured driver of throughput.
- The DoD is what “done” means on this team. New starters know what to verify; existing members can't lower the bar without a conversation. The floor moves up over time, not down.
Origins
The agile-era origin is Scrum. Ken Schwaber and Jeff Sutherland's Scrum Guide has used the term “Definition of Done” since at least its November 2009 edition. The current edition treats the DoD as “a formal description of the state of the Increment when it meets the quality measures required for the product.”5 Mike Cohn's Succeeding with Agile (Addison-Wesley, 2010) ch.10 is the practitioner playbook for assembling one.6 Robert C. Martin's The Clean Coder ch.3 (“Saying Yes”) and ch.8 (“Testing Strategies”) hold the discipline-first flavour: the checklist defines what a professional commitment means.7
The older intellectual root is Atul Gawande's Checklist Manifesto (Metropolitan Books, 2009).1 Gawande's case is built on aviation pilot checklists and Peter Pronovost's ICU central-line checklist (Johns Hopkins, 2001): experts on hard problems beat their own judgement when they read from a list. Gawande's claim transfers directly to engineering: the senior engineer on a Friday afternoon is the ICU doctor at the end of a shift. The checklist is the corrective.
The DORA / Forsgren research programme makes the DoD a measured driver of delivery performance. Accelerate (IT Revolution, 2018) names the 24 capabilities of high performers. The DoD checklist sits inside several: continuous testing, test automation, deployment automation.8 The 2024 DORA report continues to list these as predictors. The Microsoft Engineering Playbook codifies the same shape for an industrial team and is the most-cited operational template on GitHub.9
The total-coverage row in the previous canon merges into the DoD here. Brian Marick's coverage taxonomy and Martin Fowler's “Test Coverage” bliki entry both argue the same point: coverage is a metric rather than a target.4 Promoting it to a tenet inflated the list. Absorbing it into the DoD as one row with named exclusions (.types.ts, .spec.ts, .stories.tsx, .config.*, __mocks__/) is the move P4 Continuous Quality Feedback and the canonical literature both prefer.
Quotes
Good checklists, on the other hand, are precise. They are efficient, to the point, and easy to use even in the most difficult situations. They do not try to spell out everything—a checklist cannot fly a plane. Instead, they provide reminders of only the most critical and important steps …
The Definition of Done is a formal description of the state of the Increment when it meets the quality measures required for the product. The moment a Product Backlog item meets the Definition of Done, an Increment is born.
Professionals don't make commitments unless they know they can achieve them. It's really as simple as that.
Test coverage is a useful tool for finding untested parts of a codebase. Test coverage is of little use as a numeric statement of how good your tests are.
Evidence
20 external sources, ranked by author authority. The first five are the canon; expand to see the rest, including the qualifiers and the named opposers. Each links out to its primary source.
- 01The Checklist ManifestoSupportsThe intellectual root. Aviation pilot checklists and Pronovost’s ICU central-line checklist. Experts on hard problems beat their own judgement when they read from a list.
- 02The agile-era origin of the term. The DoD as a formal description of the state of the Increment when it meets the quality measures required for the product.
- 03Succeeding with AgileSupportsCh.10 is the practitioner playbook for assembling a DoD. Walks through team-level vs release-level definitions and the difference each makes.
- 04The Clean CoderSupportsCh.2 “Saying Yes” and ch.8 “Testing Strategies”. The checklist defines what a professional commitment means. The discipline-first flavour.
- 05Names the 24 capabilities of high-performing teams. The DoD sits inside continuous testing, test automation, and deployment automation. An empirical case for the row.
- 06The most-cited operational template for engineering DoDs on GitHub. Codifies the row-by-row checklist shape for industrial teams.
- 07Continues to list test automation, deployment automation, and continuous testing as drivers of delivery performance. The DoD is the per-change enforcement of these.
- 08Continuous DeliverySupportsThe deployment-pipeline idea. Each stage of the pipeline is a row on a machine-readable DoD. The manual checklist becomes the human-readable parallel.
- 09Lean EnterpriseSupportsThe team-level DoD scaled up to enterprise delivery. Useful as the bridge between team-level checklists and organisational-level governance.
- 10How Google Tests SoftwareSupportsGoogle’s testing playbook. The “100% with explicit exclusions” idea for coverage. The source of the named-exclusion convention the DoD uses for coverage.
- 11“Test Coverage” bliki entryQualifiesCoverage targets are a smell. Coverage is a diagnostic, not a goal. The qualifier inside the DoD’s coverage row.
- 12The Craft of Software TestingQualifiesMarick’s coverage taxonomy. Coverage is a metric, not a target. The DoD treats coverage as the diagnostic with named exclusions.
- 13The lean-throughput steelman. Every checklist row is overhead. Batch-size discipline does more for delivery throughput than list discipline.
- 14The Poppendiecks’ qualification: every checklist row is a queue, every queue is delay. The DoD must be revisited regularly to remove dead rows.
- 15Checklists are bureaucracy. The time spent on the list is time not spent on the design. The broadest opposing view. The reply argues the DoD must be pruned, not abandoned.
- 16Building Effective AgentsSupportsNames the evaluator-optimizer pattern. The agent generates, an external check evaluates. The DoD is the canonical evaluator.
- 17“Verifiability”SupportsThree conditions a task must satisfy to be agent-optimisable: resettable, efficient, rewardable. The DoD must satisfy them or the agent grades itself.
- 18The Michigan Keystone ICU study Gawande draws on, led by Pronovost of Johns Hopkins. Empirical case that a five-row checklist cut catheter-related bloodstream infections by up to 66% across 103 ICUs, taking the median rate to zero within three months. The medical analogue for the engineering DoD.
- 19Empirical case for grounding the agent in machine-checkable feedback. HumanEval pass@1 lifts from ~80% to 91%. The DoD is the canonical machine-checkable feedback in code work.
- 20Tool-interactive critiquing. The verification signal must come from running tools (tests, linters, type-checkers) exactly the rows the DoD enumerates.
Twenty sources, three stances. The supporters are Gawande, Schwaber & Sutherland, Cohn, Martin's Clean Coder, and Forsgren: the checklist canon. The qualifiers further down push the line that coverage and DoD rows are diagnostics, not goals. The opposers argue checklists are bureaucracy. The steelman the case has to address.
Enforcement
Apply these rules in eslint.config.mjs. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| vitest --coverage | vitest --coverage | the coverage row of the DoD. Configure thresholds.lines / branches / functions to fail the script when coverage drops below the team floor. |
| tsc --noEmit | TypeScript compiler | the type-check row. Strict mode plus noUncheckedIndexedAccess plus exactOptionalPropertyTypes catch the silent contract failures. |
| eslint . | ESLint core | the lint row. Every error in the config is a row on the DoD; warnings are the rows the team chose not to enforce yet. |
| jscpd . | jscpd | the dup-check row. Threshold 0 in this repo; non-zero is a refactoring task before merge. |
| prettier --check . | Prettier | the format row. Format failures are auto-fixable but never auto-merged. |
| husky pre-push | Husky pre-push | the gate. Runs the quality script on every push; the merge button doesn’t accept a red pre-push. |
| lint-staged | lint-staged | the per-commit subset. Runs the DoD on the staged files only so the loop stays under a second. |
| no-warning-comments | ESLint core | TODO / FIXME / WIP markers: the unfinished-work signal that wasn’t triaged before merge. |
eslint.config.mjsconfiguration snippet
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
export default defineConfig({
files: ['**/*.{ts,tsx}'],
rules: {
'no-warning-comments': ['error', { terms: ['todo', 'fixme', 'wip'], location: 'anywhere' }],
'no-restricted-syntax': ['error', {
selector: "CallExpression[callee.name='it'][arguments.length=1]",
message: 'Tests without an implementation block are pending, finish before merge.',
}],
}
});Apply these rules in eslint.config.mjs. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| vitest --coverage | vitest --coverage | the coverage row of the DoD. Configure thresholds.lines / branches / functions to fail the script when coverage drops below the team floor. |
| tsc --noEmit | TypeScript compiler | the type-check row. Strict mode plus noUncheckedIndexedAccess plus exactOptionalPropertyTypes catch the silent contract failures. |
| eslint . | ESLint core | the lint row. Every error in the config is a row on the DoD; warnings are the rows the team chose not to enforce yet. |
| jscpd . | jscpd | the dup-check row. Threshold 0 in this repo; non-zero is a refactoring task before merge. |
| prettier --check . | Prettier | the format row. Format failures are auto-fixable but never auto-merged. |
| husky pre-push | Husky pre-push | the gate. Runs the quality script on every push; the merge button doesn’t accept a red pre-push. |
| lint-staged | lint-staged | the per-commit subset. Runs the DoD on the staged files only so the loop stays under a second. |
| no-warning-comments | ESLint core | TODO / FIXME / WIP markers: the unfinished-work signal that wasn’t triaged before merge. |
eslint.config.mjsconfiguration snippet
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
export default defineConfig({
files: ['**/*.{ts,tsx}'],
rules: {
'no-warning-comments': ['error', { terms: ['todo', 'fixme', 'wip'], location: 'anywhere' }],
'no-restricted-syntax': ['error', {
selector: "CallExpression[callee.name='it'][arguments.length=1]",
message: 'Tests without an implementation block are pending, finish before merge.',
}],
}
});Apply these rules in package.json. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| vitest --coverage | vitest --coverage | the coverage row of the DoD with the same configuration as the TypeScript stack. |
| eslint . | ESLint core | the lint row. Without a type-checker, lint carries more weight; sonar rules add a second layer. |
| jscpd . | jscpd | the dup-check row. |
| prettier --check . | Prettier | the format row. |
| sonar-scanner | SonarQube | the sonar row. Catches code smells the lint config missed; useful in JavaScript where the type-checker isn’t in the chain. |
| husky pre-push | Husky pre-push | the gate. Runs the quality script on every push. |
package.jsonconfiguration snippet
{
"scripts": {
"quality": "npm run lint && npm run sonar && npm run type-check && npm run format:check && npm run dup-check && npm run test:coverage",
"lint": "eslint .",
"type-check": "tsc --noEmit",
"format:check": "prettier --check .",
"dup-check": "jscpd .",
"test:coverage": "vitest run --coverage --coverage.thresholds.autoUpdate=false"
}
}Apply these rules in pyproject.toml. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| fail_under | coverage.py | coverage drift below the team floor. Turns coverage into a gate, not a metric to admire. |
| branch = true | coverage.py | branch coverage gaps: both arms of every if; required for honest coverage. |
| mypy --strict | mypy | the type-check row. Catches the silent contract failures the linter can’t see. |
| ruff check . --select FIX,TD,ERA | Ruff | TODO / FIXME / commented-out code that wasn’t triaged. Unfinished-work signal in the DoD. |
| ruff format --check . | Ruff | the format row. |
| pre-commit run --hook-stage pre-push | pre-commit | the gate. Runs the DoD on every push; the merge button doesn’t accept a red pre-push. |
pyproject.tomlconfiguration snippet
[tool.coverage.run]
branch = true
omit = [
"*/__init__.py",
"*/conftest.py",
"*/*_test.py",
]
[tool.coverage.report]
fail_under = 90
show_missing = true
skip_covered = true
[tool.ruff.lint]
select = ["FIX", "TD", "ERA"]Apply these rules in pom.xml. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| jacoco:check (COVEREDRATIO ≥ 0.90) | JaCoCo | coverage drift below the team floor. The build fails when the rule is violated; the gate doesn’t flex. |
| checkstyle:check | Checkstyle | the lint row. Style violations promoted to errors so the gate enforces them. |
| spotbugs:check | SpotBugs | bytecode-level bug patterns the source linter can’t see; the second layer of the DoD’s lint row. |
| sonar:sonar | SonarQube | the sonar row. Quality-gate failure stops the merge; one umbrella over coverage, complexity, smells, and security. |
| pmd:check | PMD | complexity caps and the unused-code row. |
| spring-javaformat | spring-javaformat | the format row. Format failures are auto-fixable but never auto-merged. |
pom.xmlconfiguration snippet
<?xml version="1.0"?>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>jacoco-check</id>
<goals><goal>check</goal></goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.90</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>Apply these rules in .php-cs-fixer.dist.php. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| --coverage-clover | PHPUnit (coverage) | the coverage row. Clover XML output piped into the CI gate; thresholds enforced by the CI tool. |
| phpstan analyse --level max | PHPStan | the static-analysis row. Catches type errors PHP can’t express in syntax; second-layer to the linter. |
| php-cs-fixer fix --dry-run | PHP-CS-Fixer | the format row. Format failures are auto-fixable but never auto-merged. |
| phpcs --standard=PSR12 | PHP_CodeSniffer | the lint row. Style violations promoted to errors so the gate enforces them. |
| phpunit --fail-on-risky | PHPUnit (coverage) | tests PHPUnit flags as risky: assertions missing, output produced, or test isolation broken. Closes a class of silent passing tests. |
| captainhook --hook=pre-push | CaptainHook | the gate. Runs the DoD on every push; the merge button doesn’t accept a red pre-push. |
.php-cs-fixer.dist.phpconfiguration snippet
<?php
return (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'no_unused_imports' => true,
'no_empty_phpdoc' => true,
'php_unit_test_class_requires_covers' => true,
])
->setFinder(
(new PhpCsFixer\Finder())->in(__DIR__ . '/src')
);AI rules
.cursor/rules/p3-definition-of-done.mdc--- alwaysApply: true --- ## Prickles P3: Definition of Done Don't call work done until every row of the checklist passes: coverage, lint, type-check, format, dup-check, AC verification and accessibility, each machine-checked and signed off. The list is the gate and never a guideline. The moment a row is treated as advisory, the row is dead. Revisit the list every quarter. Remove rows that haven't caught anything. Add rows that catch failures the team is paying for. The AC at the end is the AC at the start (P2 Spec-First). Without P2's AC, the DoD has nothing to verify against.
Repo layout, CI, and ESLint wiring for these paths live on /implementation, not repeated on every tenet.
Counter-argument
The strongest steelman is the lean-throughput reading: every checklist row is a unit of overhead and the team that releases fastest is the team that has stripped its DoD to the bone. Donald Reinertsen's Principles of Product Development Flow argues that batch-size discipline does more for delivery throughput than list discipline.3 Marick's coverage taxonomy is the qualifier inside the row: target-driven coverage produces local optima.4 Ousterhout's critique is broadest: checklists are a bureaucratic answer to a design problem. The time spent on the list is time not spent on the design.
Counter-argument retort
Reinertsen is right about lean throughput when the DoD has become a backlog of compliance items the team can't justify item-by-item.3 The reply is to keep the list short, every row justified, every row tied to a measurable failure mode. The six-row npm run quality script in this repo is the working example: lint, sonar, type-check, format, dup-check, test+coverage, plus the AC verification on the PR. Each row catches a class of failure that has cost the team an hour in the past quarter. The list is short because the list is empirical.
Marick's coverage argument qualifies the row rather than refuting it. Coverage as a target produces local optima: the engineer adds a meaningless test to the file with low coverage. The suite grows. Nothing is checked. Coverage as a diagnostic surfaces the file with no tests at all, which is a different question. The DoD treats coverage as the diagnostic, with named exclusions for files that are already covered by the surrounding code ( .types.ts, .config.*) and a floor (90%) that the team has chosen because the cost of dropping below it is measurable.
Ousterhout's critique, that checklists are bureaucracy, is true for the DoD that has grown without pruning. The fix is not to drop the DoD. It is to revisit the DoD every quarter and remove the rows that haven't caught anything. A short DoD is a working DoD. A long DoD is theatre. The discipline survives Ousterhout. The bureaucratic growth doesn't.
In production work, the DoD is the row that lets the team merge to main on a Friday afternoon without holding the breath of every reviewer. P2 writes the AC; P4 runs the gates as the change is being written; P3 is the final read of the list before the merge button is pressed. The three rows compose. Collapsing them loses one of the three failure modes each catches.
Notes
- [1]Atul Gawande: The Checklist Manifesto: How to Get Things Right (Metropolitan Books, 2009). Built on aviation pilot checklists and Peter Pronovost’s ICU central-line checklist. Experts on hard problems beat their own judgement when they read from a list.
- [2]Noah Shinn et al.: “Reflexion: Language Agents with Verbal Reinforcement Learning”, NeurIPS 2023. The agent grounded in machine-checkable feedback improves. The agent grading itself does not. Generalised by Madaan (Self-Refine) and Gou (CRITIC).
- [3]Donald G. Reinertsen: The Principles of Product Development Flow (Celeritas, 2009). The lean-throughput steelman: every checklist row is overhead, and batch-size discipline does more for delivery throughput than list discipline.
- [4]Martin Fowler: “Test Coverage” bliki entry (martinfowler.com, 2012). Coverage targets are a smell. Coverage is a diagnostic rather than a goal. Pairs with Brian Marick’s coverage taxonomy as the qualifier inside the DoD’s coverage row.
- [5]Ken Schwaber & Jeff Sutherland: The Scrum Guide (current edition, scrumguides.org). “Definition of Done” as a formal description of the state of the Increment when it meets the quality measures required for the product. The agile-era origin of the term.
- [6]Mike Cohn: Succeeding with Agile: Software Development Using Scrum (Addison-Wesley, 2010), ch.10. The practitioner playbook for assembling a DoD. Useful as the operational counterweight to the Scrum Guide’s definition.
- [7]Robert C. Martin: The Clean Coder: A Code of Conduct for Professional Programmers (Pearson, 2011), ch.2 “Saying Yes” and ch.8 “Testing Strategies”. The checklist defines what a professional commitment means.
- [8]Forsgren, Humble & Kim: Accelerate: The Science of Lean Software and DevOps (IT Revolution, 2018). Names the 24 capabilities of high-performing teams. The DoD checklist sits inside continuous testing, test automation, and deployment automation.
- [9]Microsoft Engineering Fundamentals Playbook: microsoft.github.io/code-with-engineering-playbook. The most-cited operational template for engineering DoDs on GitHub. Codifies the row-by-row checklist shape for industrial teams.
- [10]Anthropic: Building Effective Agents (December 2024). Names the evaluator-optimizer pattern and calls it particularly effective “when we have clear evaluation criteria”. The agent generates, an external check evaluates. The DoD is the canonical evaluator.