User stories and acceptance criteria
A user story says who needs what and why. Its acceptance criteria say exactly when it is done. This lesson teaches you to write both, with Given/When/Then scenarios that cover the edge cases and the non-functional requirements that sink operations software: working offline, speed on cheap handhelds, permissions and an audit trail.
By the end you can
- Write stories that are small, valuable and testable, and split big ones by workflow step or business rule
- Write acceptance criteria as Given/When/Then scenarios with concrete values
- Find edge cases with a checklist and a 25-minute example-mapping session
- Turn offline, speed, permission and audit needs into requirements someone can test
The everyday picture
You order a birthday cake: chocolate, for 20 people, Saturday. That sentence is enough for the baker to start, and it is a promise to talk about the details. At the counter you agree how you will both know it is right: ready by 4 pm, "Happy birthday Ama" on top, no nuts because one guest is allergic, and it must fit in a car boot. Those checks are the acceptance criteria. On Saturday, you and the baker can look at the cake and agree, without arguing, whether it is done.
Then there are the things nobody says out loud. The cake must survive a 40-minute drive on a hot afternoon. The card machine must work when the bakery's internet is down. Nobody asked for these, and they are exactly what ruins parties. Software calls them non-functional requirements, and in shops, warehouses and delivery vans they decide whether a feature gets used.
A story is a promise of a conversation
A user story is a small slice of value described from one user's side. The common template comes from Rachel Davies' team at Connextra: As a [role], I want [capability], so that [benefit]. Ron Jeffries described the three parts of a story in 2001 as Card, Conversation, Confirmation: a card with just enough words to identify the need, a conversation where the details are worked out, and a confirmation, the acceptance tests that show it works.
Bill Wake's INVEST checklist (2003) says a good story is Independent, Negotiable, Valuable, Estimable, Small and Testable. In practice, "small" means a few days of work for the team, and "testable" means you can already picture the scenarios.
- Weak: "As a user, I want a claims API." There is no real role, no benefit, and only a technical layer.
- Strong: "As a partner shop owner, I want to report missing cartons while the driver is still at my door, so that I get credit without phoning my sales rep."
Big stories, sometimes called epics, need splitting. Split so that every piece still gives a user something they can see and accept:
- By workflow step: report, driver confirms, finance approves, credit appears.
- By business rule: claims at the door, late claims after the driver leaves; claims up to $50, claims above $50.
- By data variation: missing cartons, damaged cartons, wrong item delivered.
- Happy path first, unhappy paths next: the normal case ships, then the failures get their own stories.
- By role: the shop owner's view first, the shop assistant's later.
Avoid splitting by technical layer (a screen story, an API story, a database story). No layer alone gives a user value, and nobody outside engineering can accept it. When the team does not know enough to size a story, add a spike: a time-boxed investigation, such as two days to check whether the ERP can create credit notes through its API.
To see the whole workflow before you split, Jeff Patton's story map (User Story Mapping, 2014) lays the user's steps left to right and the stories under each step, so you can cut a first release across all steps instead of finishing one step perfectly.
Quick check
Which is the best way to split "As a shop owner, I want to claim for bad deliveries"?
Each piece in the second option gives a shop owner something they can use and accept, and each can be tested on its own. Layers deliver nothing until all three are done. Splitting by platform builds the same thing twice and leaves the hard rules unsplit.
Acceptance criteria as Given/When/Then
Acceptance criteria turn a story into checks that the PM, the developer and the tester read the same way. The most widely used format comes from Dan North, who described it in "Introducing BDD" in 2006: Given some initial context, When an event occurs, Then ensure some outcomes. His example was a cash machine: given the account is in credit and the card is valid and the dispenser contains cash, when the customer requests cash, then the account is debited, cash is dispensed and the card is returned.
Teams that automate these checks often write them in Gherkin, the syntax used by Cucumber and similar tools, which adds a few keywords: Feature, Scenario, And, But, and Scenario Outline with an Examples table for rules that need several values. You do not need the tools to use the format. It is valuable on paper too.
Rules for scenarios that work:
- One behaviour per scenario. If you need two Whens, you have two scenarios.
- Concrete values. "3 cartons of cooking oil at $22 each", never "some items".
- Describe what the user does and sees, not which button they press. "When the driver confirms with his code" survives a redesign; "When he taps the green button" does not.
- An observable Then. Something on a screen, in a record or in a message that a tester can check. "Then the claim is processed correctly" is not a check.
- Short. Three to seven lines. Long scenarios hide several behaviours.
Scenario: Driver confirms a door claim for missing cartons
Given delivery D-5531 to Ravi's shop lists 10 cartons of cooking oil at $22.00
And driver Karim is assigned to D-5531
When Ravi reports 3 cartons missing
And Karim confirms the claim with his driver code
Then claim C-1042 for $66.00 shows "Waiting for finance" in the Partner app
And the claim records Karim as the confirming driverBoundaries are where bugs live, so test the values on each side of a rule. A Scenario Outline keeps that compact:
Scenario Outline: Driver-confirmed claims up to $50 are approved automatically
Given a driver-confirmed claim worth <amount>
When the claim reaches the server
Then its status is "<status>"
Examples:
| amount | status |
| 49.99 | Approved |
| 50.00 | Approved |
| 50.01 | Waiting for finance |Quick check
Which "Then" can a tester check?
The third names a place to look and an exact value. "Correctly" and "as expected" hide the expectation in someone's head, so the PM, the developer and the tester can each pass or fail the same build.
Finding edge cases on purpose
Edge cases are found by asking the same questions every time. Run this checklist against each story; most stories trigger four or five of the categories.
| Category | Ask | Claims example |
|---|---|---|
| Zero, one, many, too many | What if the count is 0, 1, every line, or more than allowed? | Claim 12 missing from a line of 10 |
| Repeats | What if the same action arrives twice? | "Send" tapped three times on a slow network |
| Time | What if it happens late, across midnight, or the device clock is wrong? | Claim at hour 25 of a 24-hour window |
| Interruptions | What if a call comes in, the app is closed or the battery dies? | Phone dies after the photo, before sending |
| Network | What if there is no signal, a slow signal, or it drops halfway? | Photo upload stops at 60% |
| People and permissions | What if the wrong person, or someone whose role just changed, acts? | A different driver's code is entered |
| Data | What if the record is missing, unknown or changed since? | Delivery not yet downloaded to the phone |
| Two people at once | What if two people change the same thing? | Owner and assistant both claim the same line |
| Money and units | What about rounding, partial units and currencies? | 3 broken bottles out of a carton of 12 |
The "repeats" row deserves special care in any app that retries. A request is idempotent when doing it twice has the same effect as doing it once. The usual way is for the app to create a unique ID for each claim and for the server to ignore a second claim with the same ID. Write it as a scenario, or a slow network will one day create duplicate credit notes.
To find the questions you cannot answer alone, run example mapping, described by Matt Wynne of the Cucumber project in 2015. The PM, a developer and a tester sit with a stack of coloured cards. The story goes on a yellow card, each rule (an acceptance criterion) on a blue card, examples of each rule on green cards under it, and every question nobody can answer on a red card. About 25 minutes is enough for a well-sized story. Many red cards mean the story is not ready: give each question an owner, or split the story. Too many blue cards usually means the story is too big.
Quick check
An example-mapping session ends with five red cards for one story. What should happen next?
Red cards are unanswered questions, and five of them means the team would be guessing. Building anyway turns each guess into a hidden product decision. Throwing the cards away does not make the questions go away; they come back as bugs or arguments in testing.
Non-functional requirements that bite in operations software
A non-functional requirement (NFR) says how well, or under what conditions, the system must work. Consumer apps that ignore them lose users. Operations software that ignores them keeps its users, who cannot leave, and they quietly go back to paper and chat groups while the data rots. Most failed rollouts in stores and warehouses are NFR failures: the feature worked in the office on Wi-Fi with a new phone and failed at the shop door. Four NFRs matter most, and each can be written as Given/When/Then with numbers.
Offline. Say what works without a network, for how long, and what the user sees. For claims: creating a claim, taking photos and the driver's confirmation work offline for a full day (up to 50 claims and 200 photos). The delivery record downloaded at the depot includes what confirmation needs, such as the assigned driver's code in a protected form. The screen shows how many claims are waiting to send and when the phone last synced. Some things never work offline, and the spec should say so: the finance approval above $50 and the credit note itself. The engineering side of this, from queues to conflicts, is in Offline-first apps and data sync.
Speed on cheap handhelds. "Fast" is untestable. Name the device (your slowest supported phone or the store's handheld model), the conditions (a 500-line delivery, battery saver on, a weak 3G signal), the percentile and the number. Jakob Nielsen's classic limits (1993) are a good guide: about 0.1 seconds feels instant, 1 second keeps the user's flow of thought, and past 10 seconds attention is lost. Android itself shows an "app not responding" dialog when an app does not respond to a tap or key press within 5 seconds. Test on the real device with real data volumes. A laptop emulator on office Wi-Fi tells you almost nothing about a three-year-old phone in a storeroom.
Permissions. Write down who may create, confirm, approve, edit and cancel, and require the server to enforce it. Hiding a button is a design choice, never a security control, because anyone with the API can still call it. Say what happens when a role changes during a shift, and how shared devices work: store handhelds pass between staff, so each person needs their own sign-in or PIN, or the system cannot know who did what. The security side is in Security essentials.
Audit trail. For every change to money or stock, record who (user and role), what (the action, with the value before and after), when (device time and server time), where (shop or store, device, app version) and why (reason code or note). Entries are append-only: a correction is a new entry, and nobody can edit history, administrators included. Keep entries as long as your finance policy requires, and make them searchable by support in under a minute, because "who changed this?" is asked most during disputes.
| NFR | Untestable | Testable |
|---|---|---|
| Offline | "Works offline" | "Up to 50 claims and 200 photos saved with no signal for 10 hours; all send within 5 minutes of signal returning" |
| Speed | "Fast on all devices" | "Delivery with 500 lines opens in under 1 s at p95 on our slowest supported phone, battery saver on" |
| Permissions | "Only authorised users" | "A driver calling the approve endpoint gets a refusal and the attempt is logged" |
| Audit | "All actions logged" | "Every claim change stores user, role, before and after, device and server time; entries cannot be edited" |
Ready and done
Acceptance criteria belong to one story. A Definition of Done applies to every story. The 2020 Scrum Guide calls it a formal description of the state of the work when it meets the quality measures the product requires. For an operations product, a good one includes: code reviewed (Pull requests and code review), every scenario covered by an automated or written test, tested on the slowest supported device, analytics events added (Instrumentation), behind a feature flag where risky (Shipping safely), and support notes written.
Many teams also keep a Definition of Ready, a check before a story enters a sprint: scenarios written, open questions answered or split off, designs linked, NFRs noted, and small enough for a few days. Treat it as a guide; a rigid gate can stall a team waiting for perfect stories.
When an outside company builds the feature, acceptance criteria do two more jobs. They become the user acceptance test cases, and they draw the line between a defect (the build does not do what the criteria say) and a change request (you now want something the criteria never said). Vague criteria make that line blurry, and blurry lines cost money (Working with vendors and agencies).
Do it
Write one story with its acceptance criteria and NFRs, ready for a team or a vendor to build.
-
Map the workflow and pick the slice
Write the user's steps left to right (receive delivery, check cartons, report, confirm, get credit). Under each step, list the stories. Pick the thinnest set that goes end to end for the first release.
-
Write the story card
One role, one capability, one benefit. Add the context a designer and developer need, and link the PRD.
Markdown## CLAIMS-3: Report missing cartons at the door **As a** partner shop owner **I want to** report missing cartons while the driver is still at my door **So that** I get credit without phoning my sales rep Context: owner's own budget Android phone; storeroom often has 0 to 1 bars; driver waiting, ideally under 60 s extra. PRD: Delivery claims v1.1. Out of scope here: damaged items (CLAIMS-4), late claims (CLAIMS-6). ### Acceptance criteria 1. Happy path: driver-confirmed claim, status "Waiting for finance" or "Approved" 2. Quantity cannot exceed the delivered quantity on that line 3. Works with no signal; sends when signal returns 4. A repeated send never creates a second claim 5. Only the driver assigned to the delivery can confirm 6. Every change is in the audit trail ### Non-functional - Claim screen opens in < 1 s (p95) on our slowest supported phone (2 GB RAM, Android 10), battery saver on, 500-line delivery - 50 claims and 200 photos stored offline for 10 hours without data loss - Confirm step adds < 30 s at the door (p90, measured in pilot) ### Open questions - Partial cartons (3 of 12 bottles)? Owner: DC ops lead, due 28 Nov -
Write a scenario for every end of the decision tree
Draw the decisions inside the story (as in the first diagram), then write one scenario per end. Add values on both sides of every boundary.
OutputFeature: Report missing cartons at the door Scenario: Quantity cannot exceed what was delivered Given delivery D-5531 lists 10 cartons of cooking oil When Ravi tries to report 12 cartons missing Then he sees "You can claim at most 10 cartons on this line" And no claim is created Scenario: Claim made with no signal is sent later Given Ravi's phone has no signal When Ravi reports 3 cartons missing and Karim confirms Then the claim shows "Waiting to send" with the time 10:02 And when signal returns the claim reaches the server within 5 minutes And the server records it happened at 10:02 and arrived at 10:40 Scenario: Only the assigned driver can confirm Given Karim is assigned to delivery D-5531 When a different driver enters his own code on the claim Then the claim is not confirmed And Ravi sees "This code is not for today's driver" Scenario: The audit trail records the confirmation When Karim confirms claim C-1042 Then the audit trail shows Karim, role driver, claim C-1042, status changed from "Draft" to "Confirmed", device time and server time -
Run the edge-case checklist, then example mapping
Go through the nine categories alone first. Then book 25 minutes with a developer and a tester. Rules on blue cards, examples on green, questions on red. Leave with every red card owned.
-
Turn each NFR into a number and a test
For offline, speed, permissions and audit, write the condition, the number and how it will be tested (on which device, with what data). Put the slowest supported device in the test plan by model name.
-
Agree and mark it ready
The developer and tester read the story, change what they need, and agree. If a vendor builds it, send the agreed version as the reference for the next delivery and for UAT.
Bring it to your team
Ask these when you review a story with engineering and QA.
- Can a tester pass or fail every "Then" here without asking me what I meant?
- What happens if this request arrives twice? Where is that written?
- Which parts work with no signal, for how long, and what does the user see while waiting?
- What is our slowest supported device, and have we written the timing for it?
- Who is allowed to do each action, and does the server check it, or only the screen?
- If finance asks "who changed this and when?", can support answer in a minute?
Red flags
- Stories named after layers: "claims API", "claims table".
- Acceptance criteria with "correctly", "properly", "as expected" or "user-friendly".
- Speed requirements with no device, no percentile or no data size.
- Permissions written as "hide the button".
- An audit log that administrators can edit or delete.
Practice
Turn weak criteria into five scenarios
Story: "As store staff, I want to record a price check on the handheld, so that wrong shelf prices get fixed the same day." The current acceptance criteria say: "Works offline. Fast. Only authorised users. Logged." Facts: stores use a shared handheld model with 2 GB of memory; Wi-Fi is weak at the back of most stores; only staff with the "price checker" role may record checks; a supervisor must approve any correction above $5. Write five Given/When/Then scenarios: the happy path, offline, speed, permissions and audit.
- Each scenario has one behaviour, concrete values and an observable Then
- The offline scenario says what the user sees and when the data is sent
- The speed scenario names the device, the condition and a number
- The permission scenario is enforced by the server and covers the $5 approval rule
- The audit scenario records who, what (before and after), when and where
Show a model answer
Scenario: Staff records a wrong shelf price
Given the system price of Basmati rice 5 kg is $9.40
When Lina scans the shelf label and enters the shelf price $9.90
Then the check is saved as "Mismatch: shelf $9.90, system $9.40"
And a label reprint task appears for aisle 4
Scenario: Price check recorded with no Wi-Fi
Given the handheld has no network
When Lina records a check
Then it shows "1 check waiting to send"
And it is sent within 5 minutes of the network returning
Scenario: Scan result appears quickly on the store handheld
Given the store handheld model with battery saver on and 20,000 prices stored
When Lina scans a shelf label
Then the system price appears in under 300 ms for 95% of scans
Scenario: Correction above $5 needs a supervisor
Given Lina has the "price checker" role but not "supervisor"
When she submits a correction of $6.00 through the app or the API
Then the server marks it "Waiting for supervisor"
And the price is not changed
Scenario: Every check is in the audit trail
When Lina records a check on handheld H-17 in store 112
Then the audit trail stores Lina, role, article, shelf and system price,
device time, server time, store 112, handheld H-17 and app versionFind the edge cases in a payment story
Story: "As a partner shop owner, I want to pay my invoice with a mobile wallet (such as M-Pesa, UPI or GCash) from the Partner app, so that I don't have to hand cash to the driver." Use the nine-category checklist to list eight edge cases from at least five categories. Then write acceptance criteria (Given/When/Then) for the two you think are riskiest, and say why.
- Eight distinct edge cases from at least five categories
- At least one repeat or idempotency case and one network case
- The two scenarios are concrete, observable and testable
- The reason for "riskiest" mentions money lost, double charges or trust
Show a model answer
Edge cases. Repeats: owner taps Pay twice; the wallet sends its success message twice. Network: the wallet charges the owner but the app loses signal before it hears back. Time: payment made one minute after the invoice due time; the wallet's confirmation arrives an hour later. Many: paying three invoices at once; partial payment of one invoice. Permissions: a shop assistant tries to pay from the owner's account. Data: the invoice was changed by a credit note after the owner opened it. Money: a wallet fee changes the amount received.
Riskiest two. Charged but the app never hears back, and double payment, because both lose a shop owner's money and trust, and both happen on weak networks every day.
Scenario: Charge confirmed by the wallet after the app lost signal
Given Ravi paid invoice INV-881 for $412.00 and the app lost signal
When the wallet confirms the payment to Mangrove's server
Then INV-881 shows "Paid" the next time the app opens
And Ravi gets an SMS receipt within 2 minutes of the confirmation
Scenario: A second payment attempt for a paid invoice is refused
Given INV-881 is "Paid"
When the app sends another payment request for INV-881
Then no new wallet charge is started
And Ravi sees "This invoice is already paid"Check yourself
The finance approval story passes every scenario in testing. After launch, approvers say each claim takes about 6 seconds to open with its photo. What was missing from the story?
Speed was never written as a testable requirement, so nobody tested it. The story sentence was fine. More happy paths would still not measure time. A longer UAT might have noticed the slowness, but without a number nobody could say whether 6 seconds fails.
Which permission requirement can actually be tested?
The third names the role, the action, both ways in, and an observable result enforced on the server. "Authorised" is undefined. Hiding a button leaves the API open. "Carefully" describes behaviour no system can check.
Store handhelds are shared by several staff during a shift. Finance needs to know who confirmed each line of a goods received note. Which requirement works?
Only a personal sign-in ties an action to a person, and the audit entry then has who, where and when. A device ID says which handheld, not who held it. The manager's name is wrong for most lines. Typed initials are optional, easy to fake and impossible to check.
Flashcards
Key takeaways
- A story is one user's slice of value and a promise of a conversation. Split by workflow step, rule or data, never by layer.
- Write acceptance criteria as Given/When/Then with concrete values, one behaviour each, and a Then a tester can check.
- Find edge cases with the same checklist every time, and make repeated requests idempotent.
- In operations software, offline, speed on the slowest device, server-enforced permissions and an append-only audit trail decide adoption. Write each with numbers.
- With a vendor, acceptance criteria become the UAT tests and the line between a defect and a change request.
Sources and further reading
- Introducing BDD, Dan North, 2006
- INVEST in Good Stories, and SMART Tasks, Bill Wake, 2003
- Essential XP: Card, Conversation, Confirmation, Ron Jeffries, 2001
- Introducing Example Mapping, Matt Wynne, Cucumber, 2015
- Response Times: The 3 Important Limits, Jakob Nielsen, Nielsen Norman Group, 1993
- ANRs, Android Developers documentation
- Jeff Patton, User Story Mapping, O'Reilly, 2014