Writing acceptance criteria that actually work

The quality of what an agent builds is capped by the quality of your definition of done. This is the highest-leverage fifteen minutes in this documentation.

The one test that matters

Could this criterion fail? If no observable outcome could violate it, it’s not a criterion; it’s a mood. “Improve the onboarding flow” cannot fail. “A new user reaches the board in ≤ 3 screens without entering a password” can. Falsifiable is the whole game; everything below is technique.

Anatomy of a working criterion

Three parts, most of the time:

  1. An observable behavior: what happens, from outside the code. “Requesting a magic link for an unknown email shows the same confirmation as a known one.”
  2. A boundary: the number, limit, or edge. “Link expires after 15 minutes.” “60 requests/min per IP.”
  3. A failure mode: what happens when things go wrong. “An expired link shows a resend screen, not an error page.”

Weak criteria usually have the behavior and skip the other two. The boundary and the failure mode are exactly where an unguided agent guesses, so they’re exactly what you specify.

Worked examples

Too vague: “Handle rate limiting properly.”
Working: “Unauthenticated requests to /api/* beyond 60/min per IP return 429 with a Retry-After header; authenticated requests are exempt.”

Too vague: “Users should get notified about failed payments.”
Working: “On a failed charge, the account owner gets one email within 5 minutes, containing the failure reason and a retry link. Three consecutive failures flag the account past_due.”

Too vague (and too big): “Migrate the app to the new design system.”
This one isn’t a criterion problem; it’s a scoping problem. If you can’t write one criterion that covers the task, it’s several tasks. Split first, then specify.

Calibrate ceremony to complexity

Not everything deserves three criteria. A trivial chore (“bump dependency, tests stay green”) needs one line or none. The rule: anything you’d hand to an agent gets at least one falsifiable criterion, because the criterion is doing the code review’s first pass for you.

Let Masaro draft, keep the pen

Brainstorm-generated tasks arrive with drafted criteria. Treat drafts the way you treat agent code: useful, fast, and yours to tighten. The five seconds you spend sharpening “shows an error” into “shows an inline error and preserves input” pays for itself at review time.

Smells

  • “Properly,” “correctly,” “gracefully”: placeholders where a boundary should be.
  • Implementation, not behavior: “use Redis for rate limiting” is a constraint (fine: put it in the description or project memory); the criterion is the observable 429.
  • The invisible criterion: one you couldn’t check in under two minutes at review. If verification is expensive, the agent’s claim of “met” is unfalsifiable in practice.

Next: Reviewing agent work