Every delivery dispute you have ever been in was, underneath the emails, an argument about one word. Not "quality," not "scope." The word was done. You said the milestone was done. The client said it wasn't. Both of you were, in your own frame, telling the truth. That is the whole problem. "The dashboard works" is not a fact anyone can check. It is an opinion, and two people can hold contradictory opinions about it in perfect good faith right up until one of them is holding your invoice.
The way out is not to be more persuasive in the argument. It is to write "done" so precisely, before work starts, that the argument has no room to happen. A dispute is a contest over whose definition of done wins. Whoever wrote that definition as a checkable statement controls where the ambiguity falls. Write it vaguely and the ambiguity defaults toward the party withholding money. Write it as something a third party could verify without an opinion, and the default flips to you.
This is a craft. Here is how the sentence gets built.
What makes a criterion checkable
A checkable acceptance criterion has five properties. Miss any one and you have reopened the door you were trying to close.
Observable and testable. It states an outcome a stranger could verify without judgment. "Returns HTTP 403" is observable. "Handles permissions correctly" is a judgment call, and judgment calls are where disputes breed.
A single source of truth. One named artifact is authoritative: a specific endpoint, a specific test, a specific dataset, a specific schema file. When the criterion points at orders.schema.json v2, there is no "works on my machine," because the machine is not the authority. The file is.
An explicit environment. It names where the outcome must be true. "It works" is meaningless without a location. "On staging, against the prod-mirror snapshot" is a place you can both stand in and look at the same thing.
Evidence-linked. It points at proof: a URL, a screenshot, a passing CI check, a log line. Acceptance then becomes an act of inspecting evidence, not re-litigating the work. The client looks at the linked Newman run; they do not re-run your project in their head.
Time-boxed for review. It carries a review window so "done" cannot be held open forever while you wait, unpaid, for a client who has gone quiet. More on that below, because it is the property that does the heavy lifting.
The Scrum Guide describes a Definition of Done as "a formal description of the state of the Increment when it meets the quality measures required," created up front so everyone shares one understanding of what was completed. That is the posture. Define done before the work, in writing, so it is never argued after.
Before and after
The gap between a vague criterion and a checkable one is not a matter of length. It is a matter of whether the sentence can be falsified. Here are four pairs across the kinds of work you actually ship.
| Work type | Vague ("done" as opinion) | Checkable ("done" as fact) |
|---|---|---|
| Dashboard | "The dashboard works." | "On staging, a user with role=viewer loads /dashboard in under 2s (p95 over 20 loads), sees the four KPI cards populated from the prod-mirror dataset, and receives a 403 on /admin." |
| API | "The orders API returns the right data." | "On staging, GET /api/v1/orders/{id} with a valid bearer token for the owning account returns 200 and a body validating against orders.schema.json v2; a token for a different account returns 404 (not 403, to avoid leaking existence); an unauthenticated request returns 401. Evidence: the passing Newman run in the CI artifact." |
| Data / ETL | "The nightly import loads customer data correctly." | "On staging against the prod-mirror snapshot dated 2026-07-15, the import completes with exit code 0 in under 30 minutes; row count in stg.customers equals the source count (0 dropped, reconciled in the run log); no NULLs in customer_id or email; a re-run is idempotent (second run: zero net inserts/updates). Evidence: run-log URL and reconciliation query output." |
| Auth | "Only admins can access the settings page." | "On staging: role=admin loading /settings gets 200 and sees the Billing panel; role=member gets 403 and one access_denied event is written to the audit log with their user_id; a signed-out request is redirected (302) to /login. Evidence: screenshots for all three roles plus the linked audit-log entries." |
Read the right column again and notice what each one does. It names the environment. It enumerates the negative case, not just the happy path. It states an exact status code for each condition. It requires a side effect as proof where a side effect exists (the audit-log write). And it links the evidence, so acceptance is an inspection, not a debate.
The negative cases matter more than they look. "Only admins can access settings" is satisfied by an app that lets nobody in at all. The checkable version enumerates member, admin, and signed-out because a criterion that only describes success is half a criterion. The failure modes are where clients get burned, so the failure modes are where you get precise.
Performance is the same discipline. "The search is fast" is an adjective. The checkable form replaces the adjective with percentile bounds, a load profile, a fixed data scale, and a named repeatable script: on the 1M-row prod-mirror index, /search returns first results at p95 under 400ms and p99 under 900ms over 200 requests at 20 concurrent, using the committed search-load.js, error rate 0%, k6 summary attached. "Fast" is an opinion. p95 under 400ms is a measurement.
Given / When / Then as the sentence mold
If you want a template that mechanically forces criteria to be checkable, borrow the Gherkin structure from behavior-driven development. Its three keywords map exactly onto the properties above. Given pins the environment and preconditions, what Cucumber's reference calls "the initial context of the system, the scene of the scenario." When names a single concrete action or event. Then names the expected outcome, which the reference defines as something you verify with an assertion comparing actual to expected.
That last definition is the trick. Because Then is defined as an assertable outcome, the format physically resists opinions. You cannot write "Then the dashboard feels responsive" and stay inside the grammar. The auth criterion above, in this mold, reads: Given a user with role=member on staging, When they request /settings, Then the response status is 403 and one access_denied event is written to the audit log. The And / But keywords let you chain further conditions without losing the observable shape.
You do not need to run Cucumber, install anything, or write a single step definition. The sentence shape alone disciplines the criterion. Given, When, Then, out loud, over the SoW draft, is enough to catch every opinion before it reaches the contract.
Deemed acceptance: the clause that shifts the default
Precise criteria decide who is right. A deemed-acceptance clause decides what happens when the client says nothing, which is the more common failure. Silence is how milestones die.
A deemed-acceptance clause typically states that on delivery of a milestone with its evidence, the client has a fixed window (commonly five to ten business days) to reject in writing, and any rejection must cite the specific acceptance criterion it fails and the specific way it fails. If no such written, criterion-referenced rejection arrives inside the window, the milestone is deemed accepted. This is educational framing, not legal advice, and the exact language and window belong in a contract your counsel reviews. But the mechanism is the point.
The clause flips the burden. Without it, vague dissatisfaction blocks acceptance indefinitely and the burden of proof sits on you. With it, the client must do concrete work to withhold acceptance: name a failing criterion. And here the two halves lock together. Because your criteria are checkable, a rejection is now falsifiable. "The dashboard doesn't feel right" references no criterion and is not a valid rejection under the clause. "Criterion 3 fails: the viewer received 200 on /admin, not 403" is a valid rejection, and it is also a specific, reproducible bug you can go fix and re-deliver. Either way you are out of the fog. The precision of the criteria is exactly what makes deemed acceptance both fair to the client and enforceable for you. Vague criteria with a deemed-acceptance clause is a trap for the client; checkable criteria with the same clause is a fair, symmetric default.
Acceptance is the payment trigger and the record
Two things hang off the acceptance event, and both are why the craft is worth the extra half hour per milestone.
First, acceptance releases payment. The invoice for a milestone comes due on acceptance, actual or deemed, so a well-drafted criterion is also a payment-release condition. This is why the discipline matters more here than in a purely internal backlog. When you are billing a US client in USD, "done" and "payable" are the same threshold, and how you get money to move once a milestone is accepted is its own corridor problem, covered in getting paid across the corridor.
Second, acceptance plus its evidence should land in a durable record. Months later, "was milestone 2 accepted?" should be answered by looking up a record with the URLs, screenshots, and passing checks attached, not by re-arguing the work from memory over email. Those accepted criteria, with their proof, are the most credible artifact you own. They are the substance behind the delivery record being the new portfolio: not a testimonial, but a checkable history of dones that held.
Checkable criteria, evidence links, a recorded acceptance event. That is a milestone that closes and pays without a standing dispute.
The place to write all of this down is before work starts, which is the same place you scope the build out so it cannot be nibbled for free, covered in the unpaid scoping trap. A well-formed Statement of Work is where these criteria get committed in advance, and a delivery check at the end is where the finished work gets verified against the same criteria and the same evidence. The craft is the same at both ends: write "done" as something that either happened or did not, and the argument about whose opinion wins never gets to start.