How to Write Acceptance Criteria that Actually Work
Acceptance criteria are promises we make to ourselves about how a feature should behave. Unfortunately most of those promises are impossible to keep.
Ask a PM what acceptance criteria are for and they'll say "so engineers know when a ticket is done."
Ask an engineer the same question and they'll say "so I know what I'm supposed to build."
Both answers are completely right. Which means when acceptance criteria go wrong — and they go wrong all the time — they let both of those people down at once. The engineer ships something that technically meets the criteria, but doesn't actually do what was wanted. The PM approves it and the customer comes back two weeks later and tells you it still doesn't work.
The acceptance criteria existed. They just didn't work.
Let's talk about how to fix that.
What bad acceptance criteria actually look like
Acceptance criteria don't always fail spectacularly. They can look fine when you write them. It's when someone tries to build against them that you see the problems.
This is what bad criteria look like when they come time to build.
The adjective trap
- —"The page should load faster."
- —"The form should be easier to complete."
- —"The dashboard should feel more intuitive."
These all sound like acceptance criteria. They're not. Faster than what? Easier for who? More intuitive according to which measure?
Any engineer reading "the page should load faster" will optimise something. They'll likely optimise the right metric. But until someone specifies what done looks like there's no way to actually know when the ticket is ready to ship.
The description of a feature masquerading as a criterion
"Users can export reports as PDF"
This isn't an acceptance criterion, this is just a description of a feature. It tells you what to build. It doesn't tell you what done looks like.
Does the PDF need to match precisely the formatting they see on screen? Does it need to work on mobile? Does it need to generate under five seconds? Does it need to export all data or just allow filtered views?
The smallest details can affect whether an engineer thinks they've met the criteria. If your acceptance criterion just describes what feature you're building, it's leaving every judgement call up to the engineer. That's fine for some decisions. It's not fine for all of them.
Acceptance criteria that fail to mention the user assume every user is the same. Engineers will build for the average case. Your customer might be an edge case.
What good acceptance criteria actually look like
Good acceptance criteria aren't fun to write. They're boring, except to lawyers.
Here's what they are:
- —Specific
- —Unambiguous
- —Testable
- —Clear pass/fail conditions
Here's what they aren't:
- —Wordy expressions of intent
- —They don't include adjectives without clearly defined terms. ("Faster" is an adjective. "Faster than 2 seconds" is an adjective with a defined term.)
- —They don't just describe features. They describe outcomes.
- —They tell you who the user is, and in what context they're using your product.
Given / When / Then
Writing acceptance criteria is basically just one big framework:
- —Given — the initial conditions. What's the starting state? Who is the user? What are they looking at?
- —When — what happens? What action does the user take?
- —Then — what's the outcome? What should occur as a result of the action? What is the new, measurable state of the world?
Contrast these:
- —Bad: "The export should be fast." — Good: "Given a user with a report containing up to 500 rows of data, when they click Export PDF, then the PDF should generate and download within three seconds."
- —Bad: "Users should be able to manage their team." — Good: "Given a workspace owner, when they navigate to Team Settings, then they should be able to add a new member by email, assign that new member one of three roles (viewer/editor/admin), and remove that member — with those changes taking effect immediately and without a page refresh."
- —Bad: "The onboarding should be clearer." — Good: "Given a new user who has just completed signup for the first time, when they first arrive at their dashboard, then we should show them a persistent, three step onboarding checklist until they complete all three steps. Each step should link directly to that feature."
Pay attention to how much longer the good ones are. That's ok — length is not the problem. Vagueness is.
Before you ship a ticket, ask five questions
Before any ticket ever enters your sprint, take it through this checklist. If you can't answer yes to all of these questions, your criteria aren't done.
- —Can I test this without asking a human? You should never have to ask your PM "does this count as passing this criterion?" If you find yourself doing that your criteria aren't objective enough. Anybody should be able to look at something that implements these criteria and know whether it passed or failed.
- —Does this need a number? If so, does it have one? Pretty much any criterion that involves speed, size, amount, frequency, or any kind of threshold should have a number. Slow isn't a number. Under-two-seconds is.
- —Does it specify who the user is? If the behavior changes based on who the user is, your criteria need to establish who you're talking about. Free user or paying customer? New or returning? Owner or viewer?
- —Does it describe the outcome instead of the feature? "Users can do X" describes a feature. "When user Y does X in situation Z the result should be Q" describes an outcome. Feature-level criteria mean you'll never know when you're done.
- —Does it describe how the feature should behave under failure conditions? Happy-path criteria are easy to write. They're also incomplete. What happens if the action fails? What do you show the user? What state should the system be in?
There's one more reason criteria fail even if they're well-written
Acceptance criteria fail when they're written for the wrong problem.
Here's an example:
- —Customer: "My file uploads are really slow. It would be great if I could see a progress bar."
- —PM writes criteria for showing a progress bar.
- —Engineer implements perfect progress bar, ships.
- —Customer returns two weeks later asking why their uploads are still so slow. "I thought the progress bar would help, but it didn't."
The criteria were perfect. The engineer shipped exactly what was requested. Too bad it wasn't what was needed.
This almost always happens when someone writes acceptance criteria before understanding the problem the customer is actually trying to solve. The customer doesn't give you solutions, they give you problems.
If you accept the customer's solution as fact you might build something perfect and still build the wrong thing.
Connecting acceptance criteria to customer outcomes
Acceptance criteria solve one problem: how will we know this is done?
But there's a more fundamental question that those answers rely on: how will we know this actually solved the customer's problem?
The answer to those two questions is not always the same. It's entirely possible for a ticket to be done — all the criteria are met, the engineer signed off, and the PM approved — and not actually fix whatever problem the customer wanted fixed.
The teams who care about this connection learn something every sprint. They start to understand which problems they chronically underspecify. They get really good at writing acceptance criteria because they have actual data about when their criteria worked and when it didn't.
The teams who don't care just ship against the criteria and assume the customer knows best.
TL;DR
Good acceptance criteria are:
- —Specific
- —Numeric where numbers are needed
- —Testable by anyone
- —Cover failure states
- —Describe the correct problem
They're testable because they use numbers where needed. They know which user to build for and in what context. They cover failure cases as well as happy paths. And above all they are written after you understand the problem, not after you've decided what the solution will be.
Don't fix broken acceptance criteria. Spot them before they happen. The trick? Every ticket before it ships, ask yourself if every acceptance criterion could be tested by just handing it to an engineer.
If you can't answer yes to that question, your criteria aren't done yet.
Meet Specc
Specc is built to catch exactly these problems — it structures raw feedback into tickets, flags ambiguous criteria before they reach engineering, and helps you close the loop when you ship.
Give it a free try at speccapp.com
Try Specc
Stop guessing. Start shipping the right thing.
Paste your raw feedback and Specc turns it into a developer-ready ticket with acceptance criteria, priority signals, and flagged ambiguities.
Get started free →