Nguyen Le PhongNguyen Le Phong

Writing Good Acceptance Criteria

A practical guide to writing acceptance criteria as shared evidence for done behavior: clear examples, important edge cases, reviewable scope, and a calmer handoff between product, engineering, QA, and stakeholders.

The ticket looks ready until someone asks one small question in refinement: what should happen if the customer already used the discount code once? The room pauses. The user story says the customer can apply a promotion at checkout. The design has a neat input box. The API task has a clear endpoint. But the behavior everyone imagined is not quite the same behavior.

This is where acceptance criteria earn their keep. They are not a decoration under a user story, and they are not a private checklist for QA at the end. Good acceptance criteria are shared evidence for what done means. They make the expected behavior visible before the team spends several days building different versions of the same idea.

A useful criterion describes observable behavior. It does not say the checkout should work properly, because properly lives in each person's head. It says that a valid unused promotion code reduces the order total before payment is created. It says that an expired code shows a clear error and leaves the total unchanged. It says that a code already used by the same account cannot be applied again. The wording is simple, but it gives product, engineering, QA, and support the same surface to review.

The best acceptance criteria often sound almost boring. When this condition is true, the system does this visible thing. When this condition is not true, the system does something else. That structure is powerful because it turns a vague promise into examples. A developer can see the branch of logic. A tester can see the cases to verify. A product owner can see whether the rule matches the business decision. A reviewer can see whether the scope stayed inside the ticket.

Examples matter because teams rarely misunderstand the happy path. They misunderstand the edges around it. What if the user has no permission? What if the data is missing? What if the payment provider times out? What if the same button is clicked twice? What if the mobile layout has less space? Acceptance criteria do not need to predict every possible failure, but they should name the edge cases that would change the product promise or create expensive rework later.

There is a balance here. Too few criteria leave the team guessing. Too many criteria turn a small story into a contract nobody wants to read. The useful middle is to cover the behavior that proves the story is complete, the important negative cases, and the boundaries of scope. If a criterion describes a nice future improvement, it probably belongs in a follow-up ticket. If it describes a behavior without which the feature would be misleading or unsafe, it belongs in the current story.

I like writing criteria in everyday language first, then tightening them only as much as needed. A line such as paid users can download invoices may be a good start, but it still hides questions. Which invoices? From which date range? In which format? What should happen if an invoice is still generating? The goal is not to make the text legalistic. The goal is to remove the kind of ambiguity that would force someone to interrupt the work halfway through.

Given/When/Then can help when the behavior needs a small scenario. Given a customer has one unpaid invoice, when they open billing, then the invoice appears with a pay action. Given the invoice is already paid, when they open billing, then the invoice appears as paid and the pay action is hidden. The format is not the point. The point is that the example is reviewable. People can disagree with it while the work is still cheap to change.

Good acceptance criteria also protect review scope. A pull request becomes easier to review when the reviewer can map the diff back to a small set of promised behaviors. Did the code implement the valid code path? Did it reject expired codes? Did it keep the total unchanged after failure? If the PR contains work that no criterion mentions, the team can ask whether the scope changed on purpose. If the criteria mention behavior no code covers, the gap is visible before release.

For QA, acceptance criteria are not the whole test plan, but they are a useful spine. QA may still explore browser differences, accessibility, performance, data setup, and strange combinations the story did not list. But clear criteria keep exploration anchored. They answer the first question: what evidence would convince us that the product behavior is present? From there, testing can look for ways that evidence might be incomplete.

The small habit I trust most is to read acceptance criteria out loud before the team commits to the work. Not in a ceremonial way. Just slowly enough that someone can say: this rule is unclear, this edge case is missing, this belongs in another ticket, or this example does not match how users behave. That conversation is sometimes the real value. The written criteria are the artifact left behind after the team has aligned its understanding.

Acceptance criteria do not make delivery perfect. They make misunderstanding easier to catch while it is still quiet. The next time a ticket feels ready, try asking what evidence would make the team comfortable calling it done. The answer does not need to be long. It needs to be clear enough that different people can look at the same behavior and recognize the same promise.

你觉得这篇文章如何?