Skip to main content

User Story Guidelines

User stories shift focus from writing detailed requirements to having conversations about what users need. A well-written user story captures the essence of a requirement in a way that facilitates discussion, estimation, and implementation.

User stories are not complete specifications - they are placeholders for conversation. The goal is to capture enough detail to discuss, estimate, and implement, not to eliminate all questions upfront.


The INVEST Principle​

The INVEST acronym provides quality criteria for effective user stories. Each story should be:

Independent​

Stories should be self-contained and deliverable without depending on other stories being completed first. Independence reduces coordination overhead and allows flexible prioritization.

Why it matters: Dependencies create bottlenecks. If Story B depends on Story A, you can't work on B until A is done, limiting parallelization and forcing strict ordering.

How to achieve it:

  • Avoid technical dependencies (e.g., "create API" as separate story from "create UI")
  • Combine tightly coupled work into a single story
  • If dependencies are unavoidable, make them explicit and prioritize accordingly

Example:

 Bad (Dependent):
Story 1: Create payment database table
Story 2: Create payment API endpoints (depends on Story 1)
Story 3: Create payment UI (depends on Story 2)

Good (Independent):
Story 1: As a customer, I want to initiate a payment so I can transfer money
(Includes database, API, and UI as a complete vertical slice)

Negotiable​

Details are negotiable until the story is being implemented. The story is not a contract but rather an invitation to collaborate on the best solution.

Why it matters: Requirements evolve as we learn. Locking in implementation details too early prevents discovering better solutions.

How to achieve it:

  • Focus on the "what" and "why", not the "how"
  • Avoid specifying implementation details in the story
  • Leave room for technical decisions during implementation
  • Welcome alternative approaches during refinement

Example:

 Bad (Not Negotiable):
As a developer, I want to implement a Redis cache with 5-minute TTL
using Spring Cache annotations so that API performance improves.

Good (Negotiable):
As a user, I want payment history to load quickly (< 1 second)
so that I can review my transactions without waiting.

(How caching is implemented - Redis, local cache, database optimization -
is negotiable and decided during technical design)

Valuable​

Every story must deliver value to a user, customer, or stakeholder. If it doesn't provide clear value, it's probably a task, not a story.

Why it matters: Value-driven work ensures the team focuses on outcomes, not outputs. Technical work without clear value is often unnecessary complexity.

How to achieve it:

  • Write from user perspective, not developer perspective
  • Articulate the benefit in the "so that" clause
  • Ask "Why does this matter?" to uncover real value
  • If no clear value exists, question whether the work is needed

Example:

 Bad (No Clear Value):
As a developer, I want to refactor the payment service
so that the code is cleaner.

Good (Clear Value):
As a customer service representative, I want payment failures
to show detailed error messages so that I can help customers
resolve issues without escalating to engineering.

Estimable​

The team must be able to estimate the size/effort of the story with reasonable confidence.

Why it matters: Estimation drives sprint planning and capacity management. If a story can't be estimated, it's too vague or too complex.

How to achieve it:

  • Provide enough detail to understand scope
  • Clarify acceptance criteria
  • Identify unknowns and create spikes if needed
  • Break down stories that are too large or complex

Example:

 Bad (Not Estimable):
As a user, I want better payment performance.

(Too vague - what does "better" mean? What part of payments? How much improvement?)

Good (Estimable):
As a user, I want payment initiation API to respond in < 500ms (P95)
so that the payment form feels responsive.

Acceptance Criteria:
- GET /api/payments/{id} responds < 500ms at P95
- POST /api/payments responds < 500ms at P95
- Measured under load of 1000 concurrent users

Small​

Stories should be small enough to complete within a sprint. Large stories are called "epics" and must be broken down.

Why it matters: Small stories are easier to understand, estimate, implement, test, and review. They reduce work-in-progress and accelerate feedback cycles.

How to achieve it:

  • Target 1-5 story points (or 1-3 days of work)
  • If larger, split along user journeys or acceptance criteria
  • Vertical slices (full stack) are better than horizontal slices (layers)

Example:

 Bad (Too Large - Epic):
As a user, I want a complete payment management system with history,
refunds, recurring payments, and fraud detection.

Good (Small, Vertical Slices):
Story 1: As a user, I want to view my payment history
Story 2: As a user, I want to request a refund for a payment
Story 3: As a user, I want to set up recurring payments
Story 4: As a user, I want to be alerted if a payment looks suspicious

Testable​

Stories must have clear acceptance criteria that can be verified through testing.

Why it matters: If you can't test it, you can't confirm it's done. Testability ensures shared understanding of "done".

How to achieve it:

  • Define explicit acceptance criteria
  • Use Given-When-Then format for clarity
  • Include edge cases and error scenarios
  • Specify success metrics

Example:

 Bad (Not Testable):
As a user, I want payments to work well.

Good (Testable):
As a user, I want to initiate a payment so that I can transfer money.

Acceptance Criteria:
- Given I have sufficient balance
When I submit a valid payment
Then the payment status shows "COMPLETED"
And my balance decreases by the payment amount

- Given I have insufficient balance
When I submit a payment
Then I see error "Insufficient funds"
And no money is transferred

- Given I enter an invalid recipient ID
When I submit a payment
Then I see error "Recipient not found"

User Story Template​

Basic Format​

As a <user persona>,
I want <goal/capability>
So that <benefit/value>.

Anatomy Breakdown​

As a <user persona>:

  • Who wants this feature? Be specific.
  • Use actual personas, not roles like "developer" or "system"
  • Examples: "bank customer", "account manager", "payment operator", "system administrator"

I want <goal/capability>:

  • What does the user want to do? Focus on the capability, not implementation.
  • Describe the user's intention, not technical details
  • Keep it concise (one sentence)

So that <benefit/value>:

  • Why do they want it? What value does it provide?
  • Articulate the business or user benefit
  • This is the most important part - it justifies the work

Extended Template​

**Story**: <Short descriptive title>

**As a** <user persona>
**I want** <goal/capability>
**So that** <benefit/value>

**Acceptance Criteria**:

1. **Given** <precondition>
**When** <action>
**Then** <expected result>

2. **Given** <precondition>
**When** <action>
**Then** <expected result>

**Additional Context**:
- <Any relevant background, constraints, or clarifications>

**Dependencies**:
- <Links to other stories, external dependencies>

**Definition of Done**:
- <Any story-specific DoD items beyond standard team DoD>

Writing Effective Acceptance Criteria​

Acceptance criteria define the boundaries of the story and specify what "done" means. They form the basis for testing and verification.

Given-When-Then Format​

This format, borrowed from Behavior-Driven Development (BDD), creates testable scenarios:

Given <initial context/state>
When <action/event occurs>
Then <expected outcome/result>

Benefits:

  • Forces concrete scenarios
  • Aligns with test automation (Cucumber, JUnit)
  • Removes ambiguity
  • Facilitates conversation

Example:

Story: As a customer, I want to view my payment history

Acceptance Criteria:

1. Given I am a logged-in customer with payment history
When I navigate to the payment history page
Then I see my 10 most recent payments listed
And each payment shows: date, recipient, amount, status

2. Given I am a logged-in customer with no payment history
When I navigate to the payment history page
Then I see message "No payments found"

3. Given I am a logged-in customer with more than 10 payments
When I scroll to the bottom of the payment list
Then I see a "Load More" button
When I click "Load More"
Then 10 more payments are displayed

4. Given I am not logged in
When I try to access payment history
Then I am redirected to the login page

Checklist Format​

For stories with multiple requirements that don't follow a scenario pattern:

Story: As a developer, I want automated deployment to staging

Acceptance Criteria:

- [ ] Pipeline automatically deploys to staging after merge to develop
- [ ] Deployment includes database migrations
- [ ] Deployment uses zero-downtime strategy (blue-green)
- [ ] Deployment notification sent to #deployments Slack channel
- [ ] Rollback capability available via GitLab UI
- [ ] Staging environment matches production configuration

Edge Cases and Error Scenarios​

Don't forget unhappy paths:

Story: As a user, I want to initiate a payment

Acceptance Criteria:

Happy Path:
- Given valid payment details
When I submit payment
Then payment status is "COMPLETED"

Edge Cases:
- Given payment amount is exactly $0.01
When I submit payment
Then payment succeeds (boundary test)

- Given payment amount has > 2 decimal places ($10.567)
When I submit payment
Then amount is rounded to $10.57

Error Scenarios:
- Given insufficient account balance
When I submit payment
Then I see error "Insufficient funds"
And payment status is "FAILED"

- Given network timeout during processing
When payment is submitted
Then payment shows "PENDING" status
And retry mechanism attempts payment again

Common User Story Patterns​

Feature Story​

**Story**: View Payment History

**As a** bank customer
**I want to** view my recent payment transactions
**So that** I can track my spending and verify payments went through

**Acceptance Criteria**:

1. Given I am logged in
When I navigate to payment history
Then I see my 10 most recent payments

2. And each payment shows:
- Payment date
- Recipient name
- Amount
- Status (completed, pending, failed)

3. Given I have more than 10 payments
When I scroll to bottom
Then I see "Load More" button

Technical Story (Spike)​

**Spike**: Investigate payment retry mechanisms

**As a** development team
**We want to** research retry strategies for failed payments
**So that** we can make an informed decision on implementation approach

**Acceptance Criteria**:

- Research 3 retry approaches (Spring Retry, Resilience4j, custom)
- Document pros/cons of each approach
- Provide recommendation with justification
- Create follow-up implementation story

**Timebox**: 4 hours
**Output**: Technical design document in Confluence

Bug Fix Story​

**Story**: Fix duplicate payment processing

**As a** customer
**I want** payments to be processed exactly once
**So that** I'm not charged multiple times for the same payment

**Bug Description**:
Users report being charged twice when clicking "Pay" multiple times
quickly. Root cause: No idempotency check on payment endpoint.

**Acceptance Criteria**:

1. Given I submit a payment request
When I submit the same payment again (same idempotency key)
Then the second request returns the original payment result
And no duplicate charge occurs

2. Given I submit a payment
When the payment is still processing
And I submit again with same idempotency key
Then I receive HTTP 409 Conflict
And message "Payment already in progress"

**Root Cause**: Missing idempotency key validation
**Fix**: Add idempotency middleware using request header

Task Breakdown​

Once a story is ready for implementation, decompose it into technical tasks. Tasks are implementation steps, not user stories.

Horizontal vs Vertical Slicing​

Vertical Slice (Recommended):

  • Delivers a complete user feature through all layers
  • Database → API → UI all implemented together
  • Produces working, testable feature
  • Example: "Implement payment history feature" (includes all layers)

Horizontal Slice (Avoid):

  • Implements one technical layer across multiple features
  • Creates dependencies between stories
  • Doesn't produce working feature until all slices complete
  • Example: "Create all database tables" (no user value)

Sample Task Breakdown​

Story: As a customer, I want to view my payment history

Tasks:

Backend:
- [ ] Design database schema for payment_history table
- [ ] Create Flyway migration script
- [ ] Implement PaymentHistoryRepository (JPA)
- [ ] Implement PaymentHistoryService (business logic)
- [ ] Create PaymentHistoryController (REST endpoint)
- [ ] Add input validation and error handling
- [ ] Write unit tests (PaymentHistoryServiceTest)
- [ ] Write integration tests (PaymentHistoryIntegrationTest)
- [ ] Update OpenAPI specification

Frontend:
- [ ] Create PaymentHistory component (React)
- [ ] Implement API client for payment history endpoint
- [ ] Add loading, error, and empty states
- [ ] Implement pagination (Load More button)
- [ ] Add unit tests (PaymentHistory.test.tsx)
- [ ] Add integration tests (E2E with Cypress)

Documentation:
- [ ] Update API documentation
- [ ] Update user guide
- [ ] Add inline code comments for complex logic

DevOps:
- [ ] Configure monitoring/alerts for new endpoint
- [ ] Update deployment scripts if needed

Task Guidelines​

Each task should:

Be estimatable: Ideally < 1 day of work Be independently testable: Can verify task completion Have clear acceptance criteria: Know when it's done Be assigned to one person: Clear ownership Be trackable: Can update status (To Do, In Progress, Done)


User Story Examples​

Example 1: Payment Initiation​

**Story**: Initiate Payment

**As a** bank customer
**I want to** initiate a payment to another account
**So that** I can transfer money quickly and securely

**Acceptance Criteria**:

1. Given I am logged in with sufficient balance
When I enter valid payment details (recipient, amount, description)
And I click "Submit Payment"
Then payment status shows "PROCESSING"
And I receive confirmation email within 1 minute
And my balance decreases by payment amount

2. Given I am logged in with insufficient balance
When I try to submit a payment
Then I see error "Insufficient funds"
And the payment button is disabled

3. Given I enter an invalid recipient account
When I try to submit
Then I see error "Invalid recipient account"
And payment is not initiated

4. Given payment processing takes > 10 seconds
When I'm waiting for confirmation
Then I see a loading indicator
And a message "Processing payment, please wait"

**Non-Functional Requirements**:
- Payment submission responds < 500ms (P95)
- Idempotency: Duplicate submissions within 5 minutes return original result
- Audit: All payment attempts logged with user ID, timestamp, amount
- Security: Amount and account details validated server-side

**Dependencies**:
- Account balance API available
- Recipient validation service available

**Estimate**: 5 story points

Example 2: Payment History​

**Story**: View Payment History

**As a** bank customer
**I want to** view my recent payment transactions
**So that** I can track my spending and verify completed payments

**Acceptance Criteria**:

1. Given I am logged in
When I navigate to "Payment History"
Then I see my 10 most recent payments

2. And each payment displays:
- Date and time (format: DD/MM/YYYY HH:MM)
- Recipient name
- Amount (format: $X,XXX.XX)
- Status: Completed, Pending, Failed

3. Given I have more than 10 payments
When I scroll to the bottom
Then I see "Load More" button
When I click "Load More"
Then 10 more payments appear

4. Given I have no payment history
When I navigate to "Payment History"
Then I see "No payments found. Initiate a payment to see it here."

5. Given I filter by status "Failed"
When I apply filter
Then I see only failed payments

**Performance**:
- Page loads < 1 second
- "Load More" pagination < 500ms

**Security**:
- Users can only see their own payments
- No sensitive data in URLs

**Estimate**: 3 story points

Example 3: Spike Story​

**Spike**: Investigate Payment Gateway Integration Options

**As a** development team
**We want to** evaluate payment gateway providers (Stripe, Square, Adyen)
**So that** we can select the best fit for our payment processing needs

**Acceptance Criteria**:

- Compare 3 providers on:
- Transaction fees
- API ease of integration
- PCI-DSS compliance support
- Geographic coverage
- Fraud detection capabilities
- Developer experience

- Document findings in Confluence
- Provide recommendation with justification
- Estimate effort for integration (story points)

**Timebox**: 8 hours
**Output**: Technical comparison document + recommendation
**Follow-up**: Create implementation stories for chosen provider

**Estimate**: 3 story points

Anti-Patterns to Avoid​

Technical Task Disguised as Story​

As a developer, I want to create a payment database table
so that we can store payment data.

Problem: Written from developer perspective, not user perspective. No user value.

Fix: Combine with user-facing story or frame as a technical task.

Too Vague​

As a user, I want better payment performance.

Problem: Not estimable, not testable. What is "better"?

Fix: Be specific about what aspect of performance and how much improvement.

As a user, I want payment history to load in < 1 second
so that I can quickly review my transactions.

Implementation Details in Story​

As a developer, I want to implement a Redis cache with 5-minute TTL
using Spring Cache annotations so that the API is faster.

Problem: Prescribes the "how", not the "what". Not negotiable.

Fix: Focus on user outcome, leave implementation to technical design.

As a user, I want payment history to load quickly (< 1 second)
so that I don't have to wait.

Too Large (Epic, Not Story)​

As a user, I want a complete payment management system with history,
refunds, scheduling, and fraud detection.

Problem: Too large to complete in one sprint. Not small.

Fix: Break into multiple stories, each delivering value independently.

No Acceptance Criteria​

As a user, I want to see payment history.

Problem: Not testable. No shared understanding of "done".

Fix: Add explicit, testable acceptance criteria.


Further Reading​


Summary​

Key Takeaways:

  1. INVEST Principle: Independent, Negotiable, Valuable, Estimable, Small, Testable
  2. User Perspective: Write from user's point of view, not developer's
  3. Value Focus: Every story must deliver clear value
  4. Acceptance Criteria: Use Given-When-Then format for testability
  5. Small Stories: Target 1-5 story points, complete within a sprint
  6. Vertical Slices: Implement full features through all layers, not horizontal layers
  7. Negotiable Details: Specify "what" and "why", not "how"
  8. Conversation Starter: Stories are placeholders for discussion, not complete specs
  9. Task Breakdown: Decompose stories into concrete technical tasks
  10. Avoid Anti-Patterns: No technical tasks disguised as stories, no implementation details