ap.Abhishek Patel

Search

Search notes, essays, projects, and the library. Use arrow keys to choose and Enter to open.

← Notes

How to Write Acceptance Criteria: Defining Expected Behaviour Before Development Starts

Created · Updated · 10 min read

Why this matters: this is the single cheapest fix available to a struggling team. Acceptance criteria are what end the argument at the demo, and they are where your test cases come from. If I could only change one thing about how we work, it would be this one. That claim is argued in Why We Miss Deadlines.

What they are

Acceptance criteria are the specific, checkable conditions a single User Story must satisfy to be accepted. They belong to that one Story and nothing else.

They answer exactly one question:

How will we know this specific Story works correctly?

Not “is the code good” and not “did we follow our process”. Those are Definition of Done. Acceptance criteria are only about whether this behaviour is right.

What happens without them

Without written criteria the expected behaviour lives in somebody’s memory of a conversation. Then the demo happens:

Stakeholder: “That is not what I expected.”

Developer: “That is exactly what we discussed.”

Both of them believe they are right, and both of them probably are, because two people left the same conversation with different pictures in their heads. There is no artifact to check against, so the loudest or most senior person wins, the Story reopens, the sprint overflows, and everyone quietly loses a little trust in the process.

Written criteria do not make stakeholders agree. They make disagreement happen early, on a document, before the code is written, when changing your mind is free.

Two formats, both fine

Given / When / Then

Borrowed from BDD. Best for behaviour that depends on state.

Given a registered user with a verified email

When they request a password reset

Then a reset email is sent to that address

And the reset link is valid for 30 minutes

The three parts are: Given the starting state, When the action, Then the observable outcome. The discipline of filling all three is what forces you to think about preconditions, which is where most missed edge cases hide.

Checklist

Best for rules, constraints and validation, where a full Given/When/Then would be noise.

- A used reset token cannot be reused.

- The response is identical whether or not the email exists in our system.

- Password must be at least 8 characters and not one of the last 3 used.

- Rate limit: maximum 5 reset requests per email per hour.

Mix both in one Story. Use Given/When/Then for the main flows, a checklist for the rules. Nobody gets points for format purity.

What makes a criterion good

It is observable from outside. “The service layer validates input” is not testable by a tester. “Submitting a negative quantity shows the error ‘Quantity must be greater than zero’ and nothing is saved” is.

It is binary. Pass or fail, no judgement call. “The page loads fast” is an argument waiting to happen. “The list of 1,000 invoices renders within 2 seconds on a normal connection” is a test.

It describes behaviour, not implementation. “Store the token in Redis with a TTL” is a design decision that belongs to developers. “The link stops working after 30 minutes” is the behaviour you actually care about. Write the second, let the team choose the first.

It includes the unhappy paths. This is where teams are weakest. For every criterion describing success, ask: what if it is empty, too long, duplicated, already deleted, in a different currency, in a different timezone, submitted twice, or attempted by a user without permission? Most production bugs live in exactly those questions, and most of them cost nothing to think of in advance.

It says what is out of scope. An explicit “not in this Story” line prevents the demo from turning into a scope negotiation. This is the cheapest scope control device there is, and it is why Feedback and Scope Control leans on it.

How many is right

Roughly three to eight. Fewer than three usually means you have not thought about edge cases. More than about eight is a strong signal the Story is too big and should be split using the patterns in Writing User Stories.

Do not write a hundred criteria describing every field validation in a giant form. Group them: “all mandatory fields show a field level error when empty” plus a table of the fields is one criterion, not fifteen.

Who writes them and when

The Product Owner owns them, but they must not be written alone in a room. The good version is written during refinement with a developer and a tester present, because each role spots different gaps. That is the Three Amigos idea from Writing User Stories.

Timing matters: before development starts, not after. Microsoft’s own guidance for Azure DevOps says to define acceptance criteria before implementation so the team and stakeholders share the same expectation. Criteria written after the code are not criteria, they are a description of whatever got built.

Having criteria is also one of the required items in Definition of Ready. That is the mechanism that stops undefined Stories entering a sprint.

They are your test cases

This is the connection that fixes the “we have features but no test cases” problem.

Every acceptance criterion maps to at least one test case. If a Story has six criteria, that Story generates six test cases minimum, and they were effectively written during refinement, before coding, at zero extra cost. The tester’s job becomes turning them into executable tests and adding the deeper edge cases, rather than reverse engineering intent from a finished screen.

So the chain is:

flowchart LR
    A["Story"] --> B["Acceptance Criteria"]
    B --> C["Test Cases"]
    C --> D["Regression suite"]
    D --> E["Living documentation"]

Skip the second box and every box after it is missing too. That is the entire mechanism behind our missing coverage, and the full version is in Test Case Strategy. The same written criteria are what later make Feature Inventory and Ownership possible, because in eight months the criteria are the only honest record of what the feature was supposed to do.

Acceptance Criteria versus Definition of Done

Constantly confused, completely different.

Acceptance Criteria Definition of Done
Scope One Story Every Story, always
Question Is this behaviour correct? Is the work professionally complete?
Written by PO with dev and tester, per Story The whole team, once, improved over time
Example “Reset link expires after 30 minutes” “Code reviewed, tests pass, no critical defects, docs updated”
Changes Every Story Rarely

A Story can satisfy every acceptance criterion and still not be Done, because nobody reviewed the code or wrote the tests. And a Story can satisfy your entire Definition of Done checklist while doing the wrong thing, because the criteria were wrong. You need both.

Worked examples

Password reset

Story: As a user who forgot my password, I want to reset it by email, so that I can get back into my account without contacting support.

1. Given a registered email, when reset is requested, then an email with a unique link is sent within 1 minute.

2. Given an unregistered email, then the on screen response is identical to case 1 and no email is sent.

   (Reason: we must not reveal whether an account exists.)

3. The reset link expires 30 minutes after it is generated.

4. A link that has already been used cannot be used again.

5. The new password must satisfy the password policy and cannot match the previous password.

6. On successful reset, all existing sessions for that user are invalidated.

7. Maximum 5 reset requests per email per hour, after which further requests are silently ignored.

Out of scope: reset by SMS, admin initiated reset, password expiry policy.

Criterion 2 and criterion 6 are the interesting ones. Neither is obvious, both are security relevant, and neither would have appeared if the Story went straight from a one line request into development. That is what fifteen minutes of refinement buys.

Invoice PDF export

Story: As an accountant, I want to export an invoice as PDF, so that I can email it to a client who does not use the portal.

1. Given a saved invoice, when I click Export PDF, then a PDF downloads named INV-<number>.pdf.

2. The PDF shows: company logo, company address, client name and address, invoice number,

   invoice date, due date, line items with quantity, rate, tax and amount, subtotal, tax total,

   grand total, and payment terms.

3. Amounts display in the invoice's currency with the correct symbol and 2 decimal places.

4. An invoice with more than 20 line items paginates, repeating the header row on each page.

5. A draft invoice exports with a visible DRAFT watermark.

6. A cancelled invoice cannot be exported and the button is disabled with a tooltip explaining why.

Out of scope: Excel export, CSV export, emailing the PDF directly, bulk export of many invoices.

Now, when the stakeholder sees the demo and asks for Excel and email, there is no debate about whether it was “supposed to be included”. It is written down as out of scope, it becomes new backlog items, and the current Story gets to finish. That is exactly the move described in Feedback and Scope Control, and it is the difference between a 5 point Story that finishes and a 5 point Story that silently becomes 20.

A technical enabler

Even enabler work can have criteria, and it should.

Story: Move reporting queries to a read replica so that heavy reports stop slowing down invoice entry.

1. All read only report queries execute against the replica.

2. Write operations continue to use the primary.

3. If the replica is unreachable, reports fall back to the primary and a warning is logged.

4. Replica lag above 10 seconds is visible on the monitoring dashboard.

5. Invoice save time under a concurrent heavy report load stays under 800ms at the 95th percentile.

Criterion 5 is the whole reason the Story exists, and writing it as a number is what makes the work verifiable rather than a matter of opinion. That number is also a natural service level indicator, which is where SLI SLO and Error Budget picks up.

Common mistakes

  • Writing them after the code. Then they just describe what was built, including the bugs.
  • Restating the Story. “The user can export a PDF” adds nothing. Criteria must add detail the Story does not have.
  • Only happy paths. If there are no error cases listed, they were not considered, and they will appear as production bugs.
  • Hidden design decisions. Specifying tables, endpoints and libraries removes the team’s ability to find a better solution.
  • Vague words. “Fast”, “user friendly”, “properly”, “as discussed”. Every one of these is a future argument. Replace with a number or an example.
  • Treating them as fixed forever. If refinement or development reveals a criterion is wrong, change it deliberately and tell the Product Owner. Changing criteria is fine. Changing them silently is not.

This connects to

Why Startups Keep Missing Deadlines: The Real Cause Behind Sprint Delays, Missing Test Cases and Forgotten Features

Sit in any startup review meeting and the same questions come back every month:

Note

How to Write a User Story That Fits in One Sprint: INVEST, Splitting Patterns and Examples

The most useful thing I ever read about User Stories is Ron Jeffries' idea of the three Cs: the Card (the short sentence), the Conversation (the discussion it triggers), and the Confirmation (the acceptance criteria you agree on). The written sentence is not a specifi

Note

Definition of Done: How to Stop Calling Work Finished When Testing Is Still Pending

The Definition of Done is a single shared checklist that applies to all work, describing what must be true before anything can be called complete.

Note

Scope Creep in Sprints: How to Classify Stakeholder Feedback Instead of Absorbing It

A Story was agreed: export invoice as PDF, 5 points, acceptance criteria written.

Note

Definition of Ready: The Checklist That Decides Whether a Story Can Enter a Sprint

The Definition of Ready is a short checklist a Story must pass before it is allowed into a sprint.

Note

Why Features End Up With No Test Cases, and How to Make That Structurally Impossible

That is the part worth understanding. There was never a meeting where we chose to ship untested features. What happened is that testing sat outside the definition of finished, so under pressure it was the thing that quietly did not happen, two hundred separate times.

Note

Why Nobody Remembers How a Feature Works: Documenting Behaviour, Edge Cases and Ownership

Right after you build a feature, you know everything about it. Every edge case, every reason you chose one approach over another, every weird case the stakeholder mentioned once.

Note

SLI, SLO and Error Budgets: Measuring Whether Your Product Is Actually Healthy in Production

A feature can pass every test case and still be failing customers, because tests run in a controlled environment on clean data with one user at a time.

Note

Referenced by

Epic vs Feature vs User Story vs Task: How to Break Work Down and Decide Which Is Which

Four levels, and each one answers a different question:

Note

Building Software With AI: Why Faster Code Makes Specification and Review the Real Bottleneck

AI did not create our planning problems. It removed the thing that was accidentally hiding them.

Note

The Sprint Cycle Explained: Backlog Refinement, Sprint Planning, Daily, Review and Retrospective

If you take one thing from this note: the quality of your sprint was decided before it started.

Note

Agile and Azure DevOps Terminology Cheat Sheet: Every Term Explained in One Line

Acceptance criteria are per Story and change constantly. Definition of Done applies to everything and changes rarely. Definition of Ready is a team practice, not an official Scrum artifact, while Definition of Done is.

Note

Product Delivery Playbook: Planning, Estimating and Maintaining Software as It Grows

These are my notes on why software teams keep missing deadlines, keep losing track of what their own product does, and keep ending up with a hundred features and almost no test cases. I wrote them for myself first, after watching the same failure repeat sprint after sprint and re

Note

Ready to Use Templates for Epics, Features, Stories, Bugs, Test Cases and Sprint Goals

Written as a business objective, per [[Epic vs Feature vs User Story vs Task|Work Breakdown Hierarchy]].

Note

How to Set Up Azure DevOps for Agile Delivery: Work Items, Fields, Tags, Queries and Test Plans

A warning first: the tool does not create the discipline. A perfect Azure DevOps configuration with no [[How to Write Acceptance Criteria|Acceptance Criteria]] and no [[Definition of Done, The Gate Out of a Sprint|Definition of Done]] produces a beautifully organised record o

Note

How to Roll Out an Agile Delivery Process in 90 Days, One Change at a Time

One change at a time. A team can absorb roughly one new habit per two weeks. Introduce six at once and you get six half followed rituals and a team that concludes "process does not work here", which then blocks the next attempt for a year.

Note