User Stories
Turn a fuzzy epic, spec, or feature into a backlog of small, testable, independently shippable user stories. Each story names a user, a capability, and an outcome, carries acceptance criteria a developer and tester can verify, and is sliced as a thin vertical end-to-end path — never a horizontal technical layer. Where a story is too big, split it with a proven pattern rather than guessing.
Grounded in: User Story Mapping — Jeff Patton: vertical slices and story mapping, with INVEST and Given/When/Then.
Go deeper (The Product Channel): Guide to Product Requirements Documents
When to use this
- An epic or spec is too large to estimate or build, and needs decomposing.
- You're handed a PRD and need a ready-to-groom backlog out of it.
- A single story keeps growing or hides several decisions and should be split.
- A story exists but its acceptance criteria are vague, missing, or untestable.
- Sprint planning needs stories small enough to fit a sprint with clear "done."
Before you start (gather these)
- The epic / spec / feature you're decomposing — the scope boundary.
- The user or persona who benefits (and any distinct user types involved).
- The outcome / goal: what changes for them when this ships.
- If pointed at a PRD or doc, read it first and pull the above from it.
- If scope, persona, or outcome is unclear, ask before slicing — don't invent them.
Principles
- Story form: As a [user/persona], I want [capability], so that [outcome]. The "so that" is non-negotiable — it's the user value and the reason to build.
- INVEST every story:
- Independent — minimal ordering dependencies on other stories.
- Negotiable — a conversation, not a frozen contract of implementation detail.
- Valuable — delivers observable value to a user or the business.
- Estimable — the team understands it well enough to size it.
- Small — fits comfortably in a sprint; ideally a few days.
- Testable — you can write the acceptance criteria that prove it's done.
- Acceptance criteria as Given/When/Then — Given [context], When [action], Then [observable result]. Cover the happy path AND edge cases (empty, invalid, unauthorized, limits, failures).
- Vertical slices over horizontal — each story is a thin end-to-end path that delivers value (UI + logic + data for one capability), not a layer ("build the API," "build the schema") that delivers nothing alone. Exception for quality-bar / model-accuracy work: a story that proves a probabilistic capability hits its bar against an eval set (detection/scoring verified offline, no UI yet) is a legitimate first vertical slice — the verifiable accuracy is the value.
- Acceptance criteria for probabilistic / ML / quality-bar features — Given/When/Then is deterministic, but ML behavior is only correct ~85% of the time, so don't write AC as if each case must pass. Instead make the criteria threshold-based and measured against a fixed eval set / golden dataset: define a labeled dataset, set precision/recall (or F1, accuracy) targets, and write AC like "Given the golden eval set of N labeled examples, when the model runs, then precision ≥ 0.90 and recall ≥ 0.80." Specify the dataset, the metric, and the threshold so the AC is still pass/fail — just measured statistically rather than per-instance.
Splitting patterns (when a story is too big)
- Workflow steps — slice along the steps of a process. "Checkout" → enter address → choose shipping → pay → confirm.
- Business-rule variations — one story per rule. Discount: percentage off, then BOGO, then loyalty tier.
- Happy vs. edge path — ship the happy path first, edges as follow-ups. "Pay with valid card" before "handle declined card."
- Simple vs. complex — do the basic case, defer the hard one. "Search by exact name" before "fuzzy + filters search."
- Data / interface variations — split by input type, format, or channel. "Upload CSV" before "upload Excel and JSON."
- CRUD operations — split create / read / update / delete. "View saved reports" can ship before "edit" and "delete."
- Defer performance — make it work, then make it fast. "Generate report (any speed)" before "generate report in <2s."
Process
- Identify the user(s) and the epic's outcome — who benefits and what changes for them. List distinct personas if more than one.
- Slice into vertical stories — break the outcome into thin end-to-end capabilities, each delivering value on its own. Reach for a splitting pattern when a slice is still chunky.
- Write each in story form — As a / I want / so that. Confirm the "so that" states real value.
- Add Given/When/Then acceptance criteria — at least one happy-path scenario plus the edge cases that matter (invalid, empty, unauthorized, limits, failure).
- Check INVEST and bound the slice — run each story through the six letters; flag and fix anything that fails. State what's explicitly out of scope for the story (deferred work, things handled elsewhere) so the boundary is unambiguous.
- Split anything too big — if a story fails Small or Estimable, or hides multiple decisions, apply a splitting pattern and re-check.
- Sequence — order by dependency and value; surface the thinnest slice that proves the epic end-to-end first.
Output template
Copy-paste per story:
### [Story title]
As a [user/persona],
I want [capability],
so that [outcome].
**Acceptance criteria**
- Given [context], when [action], then [observable result].
- Given [edge context], when [action], then [result].
- Given [error/limit context], when [action], then [result].
**Notes / edge cases**
- [assumptions, open questions, data or rule details]
**Out of scope**
- [explicitly not in this story — deferred or handled elsewhere]
Suggested build order
- [story] — thinnest end-to-end slice; proves the path.
- [story] — [why next: unblocks X / highest value]
- [story] — ...
Worked example (the split that matters)
An epic arrives as "bulk CSV export." The lazy split is by layer — backend story, frontend story, QA story — which produces three stories none of which ship value alone.
The useful split is by thin vertical slice, each independently shippable:
- As an ops manager, I want to export the current filtered view as CSV, so I can hand my director numbers without asking data for a pull. — one view, ≤1,000 rows, synchronous download.
- As an ops manager, I want exports over 1,000 rows to arrive by email, so a big export doesn't time out in my browser.
- As an ops manager, I want my last export's filters remembered, so the Monday report takes one click.
Story 1 alone is worth shipping. That's the test: if a story can't be released on its own and be worth something, it's a task, not a story.
Notice the "so that" clauses — each names a real outcome, not a restatement of the action. "so that I can export a CSV" is the tell that the value wasn't understood.
Avoid (anti-patterns)
- Horizontal / technical-layer stories — "build the API," "create the schema," "wire up the service." They deliver no user value alone. Slice vertically. Exception: a detection/eval-only story for an ML or quality-bar feature — verified against an eval set with no UI — is not a horizontal layer; the measured accuracy is real, demonstrable value and is a valid first slice.
- Untestable acceptance criteria — "works well," "is fast," "is intuitive." If you can't write a pass/fail check, rewrite it as Given/When/Then.
- Giant stories — one story hiding a dozen decisions, rules, or screens. If you can't estimate it, split it.
- Missing the "so that" — a capability with no stated user value. If there's no outcome, question whether to build it.
Tips
- One verb per story. "Create and edit and export" is three stories.
- If estimation triggers an argument, the story is too big or too vague — split or clarify.
- Write the edge-case criteria first sometimes; they expose hidden scope fast.
- A story you can demo in one sentence ("watch me pay with a saved card") is the right size.
- Keep acceptance criteria about observable behavior, not implementation.
- Name the persona specifically ("returning shopper," not "user") — it sharpens the slice.
1---2name: user-stories3description: Breaks an epic, spec, or feature into well-formed user stories with Given/When/Then acceptance criteria, applies INVEST, and splits stories that are too big. Use when a PM says "break this epic into stories", "write user stories", needs "acceptance criteria for X", wants to "split this story", or asks to "turn this PRD into a backlog".4---56# User Stories78Turn a fuzzy epic, spec, or feature into a backlog of small, testable, independently shippable user stories. Each story names a user, a capability, and an outcome, carries acceptance criteria a developer and tester can verify, and is sliced as a thin vertical end-to-end path — never a horizontal technical layer. Where a story is too big, split it with a proven pattern rather than guessing.910**Grounded in:** *User Story Mapping* — Jeff Patton: vertical slices and story mapping, with INVEST and Given/When/Then.11**Go deeper (The Product Channel):** [Guide to Product Requirements Documents](https://sidsaladi.substack.com/p/guide-to-product-requirements-documents)1213## When to use this14- An epic or spec is too large to estimate or build, and needs decomposing.15- You're handed a PRD and need a ready-to-groom backlog out of it.16- A single story keeps growing or hides several decisions and should be split.17- A story exists but its acceptance criteria are vague, missing, or untestable.18- Sprint planning needs stories small enough to fit a sprint with clear "done."1920## Before you start (gather these)21- The epic / spec / feature you're decomposing — the scope boundary.22- The user or persona who benefits (and any distinct user types involved).23- The outcome / goal: what changes for them when this ships.24- If pointed at a PRD or doc, read it first and pull the above from it.25- If scope, persona, or outcome is unclear, ask before slicing — don't invent them.2627## Principles28- **Story form:** As a [user/persona], I want [capability], so that [outcome]. The "so that" is non-negotiable — it's the user value and the reason to build.29- **INVEST** every story:30 - *Independent* — minimal ordering dependencies on other stories.31 - *Negotiable* — a conversation, not a frozen contract of implementation detail.32 - *Valuable* — delivers observable value to a user or the business.33 - *Estimable* — the team understands it well enough to size it.34 - *Small* — fits comfortably in a sprint; ideally a few days.35 - *Testable* — you can write the acceptance criteria that prove it's done.36- **Acceptance criteria as Given/When/Then** — Given [context], When [action], Then [observable result]. Cover the happy path AND edge cases (empty, invalid, unauthorized, limits, failures).37- **Vertical slices over horizontal** — each story is a thin end-to-end path that delivers value (UI + logic + data for one capability), not a layer ("build the API," "build the schema") that delivers nothing alone. *Exception for quality-bar / model-accuracy work:* a story that proves a probabilistic capability hits its bar against an eval set (detection/scoring verified offline, no UI yet) is a legitimate first vertical slice — the verifiable accuracy *is* the value.38- **Acceptance criteria for probabilistic / ML / quality-bar features** — Given/When/Then is deterministic, but ML behavior is only correct ~85% of the time, so don't write AC as if each case must pass. Instead make the criteria threshold-based and measured against a fixed eval set / golden dataset: define a labeled dataset, set precision/recall (or F1, accuracy) targets, and write AC like "Given the golden eval set of N labeled examples, when the model runs, then precision ≥ 0.90 and recall ≥ 0.80." Specify the dataset, the metric, and the threshold so the AC is still pass/fail — just measured statistically rather than per-instance.3940## Splitting patterns (when a story is too big)41- **Workflow steps** — slice along the steps of a process. "Checkout" → enter address → choose shipping → pay → confirm.42- **Business-rule variations** — one story per rule. Discount: percentage off, then BOGO, then loyalty tier.43- **Happy vs. edge path** — ship the happy path first, edges as follow-ups. "Pay with valid card" before "handle declined card."44- **Simple vs. complex** — do the basic case, defer the hard one. "Search by exact name" before "fuzzy + filters search."45- **Data / interface variations** — split by input type, format, or channel. "Upload CSV" before "upload Excel and JSON."46- **CRUD operations** — split create / read / update / delete. "View saved reports" can ship before "edit" and "delete."47- **Defer performance** — make it work, then make it fast. "Generate report (any speed)" before "generate report in <2s."4849## Process501. **Identify the user(s) and the epic's outcome** — who benefits and what changes for them. List distinct personas if more than one.512. **Slice into vertical stories** — break the outcome into thin end-to-end capabilities, each delivering value on its own. Reach for a splitting pattern when a slice is still chunky.523. **Write each in story form** — As a / I want / so that. Confirm the "so that" states real value.534. **Add Given/When/Then acceptance criteria** — at least one happy-path scenario plus the edge cases that matter (invalid, empty, unauthorized, limits, failure).545. **Check INVEST and bound the slice** — run each story through the six letters; flag and fix anything that fails. State what's explicitly out of scope for the story (deferred work, things handled elsewhere) so the boundary is unambiguous.556. **Split anything too big** — if a story fails *Small* or *Estimable*, or hides multiple decisions, apply a splitting pattern and re-check.567. **Sequence** — order by dependency and value; surface the thinnest slice that proves the epic end-to-end first.5758## Output template59Copy-paste per story:6061```62### [Story title]6364As a [user/persona],65I want [capability],66so that [outcome].6768**Acceptance criteria**69- Given [context], when [action], then [observable result].70- Given [edge context], when [action], then [result].71- Given [error/limit context], when [action], then [result].7273**Notes / edge cases**74- [assumptions, open questions, data or rule details]7576**Out of scope**77- [explicitly not in this story — deferred or handled elsewhere]78```7980**Suggested build order**811. [story] — thinnest end-to-end slice; proves the path.822. [story] — [why next: unblocks X / highest value]833. [story] — ...848586## Worked example (the split that matters)87An epic arrives as *"bulk CSV export."* The lazy split is by layer — backend story, frontend story, QA story — which produces three stories none of which ship value alone.8889The useful split is by **thin vertical slice**, each independently shippable:90911. *As an ops manager, I want to export the current filtered view as CSV, so I can hand my director numbers without asking data for a pull.* — one view, ≤1,000 rows, synchronous download.922. *As an ops manager, I want exports over 1,000 rows to arrive by email, so a big export doesn't time out in my browser.*933. *As an ops manager, I want my last export's filters remembered, so the Monday report takes one click.*9495Story 1 alone is worth shipping. That's the test: if a story can't be released on its own and be worth something, it's a task, not a story.9697Notice the "so that" clauses — each names a real outcome, not a restatement of the action. *"so that I can export a CSV"* is the tell that the value wasn't understood.9899## Avoid (anti-patterns)100- **Horizontal / technical-layer stories** — "build the API," "create the schema," "wire up the service." They deliver no user value alone. Slice vertically. *Exception:* a detection/eval-only story for an ML or quality-bar feature — verified against an eval set with no UI — is not a horizontal layer; the measured accuracy is real, demonstrable value and is a valid first slice.101- **Untestable acceptance criteria** — "works well," "is fast," "is intuitive." If you can't write a pass/fail check, rewrite it as Given/When/Then.102- **Giant stories** — one story hiding a dozen decisions, rules, or screens. If you can't estimate it, split it.103- **Missing the "so that"** — a capability with no stated user value. If there's no outcome, question whether to build it.104105## Tips106- One verb per story. "Create and edit and export" is three stories.107- If estimation triggers an argument, the story is too big or too vague — split or clarify.108- Write the edge-case criteria first sometimes; they expose hidden scope fast.109- A story you can demo in one sentence ("watch me pay with a saved card") is the right size.110- Keep acceptance criteria about observable behavior, not implementation.111- Name the persona specifically ("returning shopper," not "user") — it sharpens the slice.