Skip to content

Story writing

A story is one moment in one named person's activity, deliverable in 1–3 days, with at least three testable acceptance criteria — one of them negative. The form is what makes the story interpretable across the trio without further conversation.

TL;DR

A story has five sections: story sentence (as–I want–so that), journey reference (J-number), states to handle (empty / loading / success / error / edges), 4–7 testable acceptance criteria with at least one negative, and out-of-scope. The story passes the 9-point Definition of Ready before it is pullable. If any of the nine fails, the story goes back to amigos, not into the sprint.

What it is

A story is the smallest unit of work the chain can move end-to-end. It is named in What We Shape · Story Writing and made ready through amigos. A story carries enough constraint that a developer and a QA can both pick it up without further conversation, and enough room that the developer chooses the implementation.

Distinguish from

Feature Brief — many stories per brief. Epic — a coherent set of stories that ship a journey-level change. Task — sub-story granularity; sub-tasks are a developer concern. See Confusable with at the foot.

Why it matters

Without the story discipline:

  • States ship late — empty/error/loading states emerge in QA, not at scope. Cost compounds.
  • Negative cases are not surfaced — the trio never confronts what happens when the input is bad.
  • Sizing breaks — stories that "feel small" turn out to be epics in disguise mid-sprint.
  • Amigos has nothing to attack — the story is too vague for the trio to surface anti-flows.

The 9-point DoR is the corpus's discipline against the "we'll figure it out in code" pattern.

How to do it

Step 1 — Write the story sentence with a named person

text
As Gal, an exam grader at our customer's flagship campus, I
want the LMS to display Hebrew names correctly without
re-typing, so that I can grade without breaking my flow.

The role description after the name carries the context the developer needs. Gal at the flagship campus is different from Gal internal QA. Never as a user. If you cannot name the person, the story is not ready — the Feature Brief is unfinished.

Step 2 — Reference the journey step

text
Journey: J6 — Hebrew name editing workaround during grading

The J-number anchors the story to the journey map. A story without a J-reference may be a real story; it is not yet a story the brief committed to.

Step 3 — Name the states explicitly

text
States to handle:
  - Empty:    no submissions in the grader's queue
  - Loading:  queue is loading (slow network, cold cache)
  - Success:  Hebrew name renders inline; copy-paste works
  - Error:    name contains unsupported unicode form
  - Edge:     hyphenated name overflows the column

The states section is the section most teams under-use. Every interactive moment has these four states plus the moment-specific edges. If you skip them at story-writing, you ship them at QA — at higher cost.

Step 4 — Write 4–7 acceptance criteria, at least one negative

Each AC is testable. Given/When/Then form.

text
AC1 — Given a submission with a Hebrew name including hyphens,
      when Gal opens the queue, then the name renders inline
      with no encoding warnings in the application log.

AC2 — Given a submission with a name containing a unicode form
      not in the supported set, when Gal opens it, then the name
      renders with the fallback character AND Gal can still
      submit the grade.

AC3 (negative) — Given an unauthenticated session, when the
      grader endpoint is called, then the response is 401 and
      no name data is returned in the body.

AC4 — Given a Hebrew name, when Gal copies-and-pastes the name
      into the feedback field, then the pasted text retains
      diacritics correctly.

The negative case is the discipline. AC3 tests what happens when the input is wrong. Stories without negatives ship features that break the first time a user does something unexpected.

Step 5 — Out of scope (with rationale)

text
Out of scope (this story):
  - Right-to-left layout for non-name fields.
    Reason: not on this J-step; tracked in J7 story.
  - Name normalisation in legacy submissions.
    Reason: backfill story exists separately.

Stories without an out-of-scope leak. The discipline is the same as Feature Briefs.

Step 6 — Run the 9-point Definition of Ready

Walk the DoR checklist. If any of the nine fails, the story is not pullable.

A complete story

text
Story: Hebrew name rendering on grader queue

As Gal, an exam grader at our customer's flagship campus, I
want the LMS to display Hebrew names correctly without
re-typing, so that I can grade without breaking my flow.

Journey: J6 — Hebrew name editing workaround during grading.

States to handle:
  - Empty:    no submissions in queue
  - Loading:  cold cache / slow network
  - Success:  Hebrew name renders inline with diacritics
  - Error:    unicode form not in supported set
  - Edge:     hyphenated names overflowing the column

Acceptance criteria:
  1. Hebrew names with hyphens render inline, no encoding
     warnings in logs.
  2. Unicode form not in supported set renders with fallback
     and grade can still be submitted.
  3. (negative) Unauthenticated session returns 401, no name
     data in response.
  4. Copy-paste of name into feedback retains diacritics.

Out of scope (this story):
  - RTL layout for non-name fields (tracked in J7).
  - Legacy submission backfill (separate story).

DoR checklist:
  1. Story format            ✅
  2. Journey reference J6    ✅
  3. ≥3 ACs incl. negative   ✅
  4. Figma frame V2-J6       ✅
  5. Copy defined            ✅
  6. Event: name.normalized  ✅
  7. Dependencies: none      ✅
  8. Sized 2 days            ✅
  9. Tech feasibility        ✅

Copy the template →

Evidence

Across our cycles, stories that survived contact with reality shared three properties.

  1. States were named at scope, not at QA. Stories whose states section was filled at amigos shipped with state-defect bugs 4× less often than stories whose states emerged during code review.
  2. At least one negative AC was non-trivial. Stories where the negative case tested an unhappy path (not just "input rejected") caught Discovery-level defects in 1 of 4 cycles before code began.
  3. The story was sized at 1–3 days. Stories sized "small / medium / large" without days slipped 60% of the time; stories with a developer-signed day estimate slipped 25%.

Anti-patterns

PatternWhat it looks likeWhere to fix
As a userNo named personClinic — A brief that didn't witness; fix the brief, not the story
No states sectionEmpty/loading/error left to "common sense"Clinic — A story without a state
No negative ACAll ACs describe happy pathsAdd at least one; if you can't, the trio hasn't held amigos honestly
Out-of-scope is empty"We'll see what comes up"Scope leaks. Name what is not changing.
DoR has yellowsStory pulled into sprint with ⚠ marksThe story is not ready; back to amigos
Story sized in "small/medium/large"No day estimateThe TL has not sketched the work; story is not feasibility-confirmed

Confusable with

ThisNot thisDifference
StoryFeatureStory = one moment, 1–3 days. Feature = an Epic's worth of stories.
StoryTaskTask is sub-story (a developer's plan). Stories are the chain's unit.
Acceptance criterionTest caseAn AC is what good looks like. A test case is how QA tests it. ACs precede test cases.
DoRDoDReady = pullable. Done = shippable.

Further reading

200apps · How We Work · NWIRE