Why this matters
As a Business Analyst, you ensure features are usable and safe in the real world, not just on the happy path. Edge cases and negative scenarios protect users, reduce production bugs, and clarify boundaries so engineers and QA know exactly what to build and test.
- Reduce rework by defining limits (e.g., max upload size, timeout behavior).
- Prevent data loss (e.g., failed payment should not create an order).
- Improve transparency with clear error messages and consistent outcomes.
Concept explained simply
Think of every user story as three paths:
- Happy path: everything goes right.
- Negative path: user/system/input is wrong or fails.
- Edge path: valid but extreme or boundary conditions.
Mental model: The 3-B fences
To find non-happy paths, scan for three fences:
- Boundary: first/last, min/max, empty/full, zero/negative, shortest/longest, slow/fast.
- Bad: invalid input, unsupported format, expired token, permission denied, dependency failure.
- Break: interrupted flow like timeouts, network drop, app refresh, concurrency conflicts.
Turn each fence into acceptance criteria: the system responds clearly, protects data, and keeps the user unblocked.
Who this is for
- Business Analysts defining user stories and acceptance criteria.
- Product Owners clarifying scope and quality gates.
- QA/Developers needing explicit negative and boundary expectations.
Prerequisites
- Basic user story format: As a [role], I want [need], so that [benefit].
- Understanding of acceptance criteria using Given/When/Then (Gherkin) or bullet-style criteria.
- Familiarity with the feature domain (e.g., authentication, payments, uploads).
How to discover edge cases quickly
- Input spectrum: empty, null, whitespace-only, too short/long, invalid characters, duplicates.
- Timing: expired tokens, midnight/daylight saving, end-of-month/year, session timeout.
- System limits: rate limits, max payload, pagination edges (0 items, last page).
- Permissions: unauthorized, forbidden, wrong role, disabled account.
- Dependencies: payment declines, API 500, slow response, partial failure.
- Concurrency: two users editing, rapid double-submit, retry after failure.
- Devices/Network: slow/unstable connection, offline/online transitions.
Edge Case Checklist (quick scan)
- Have we covered min, max, and empty states?
- Do we define behavior for invalid formats and unsupported types?
- What happens on timeouts, network drops, or refresh?
- Are unauthorized/forbidden cases explicit?
- Is data integrity preserved on failure?
- Are error messages user-friendly and actionable?
Worked examples
Example 1: Password reset (negative + edge)
User story: As a user, I want to reset my password so I can regain access if I forget it.
Acceptance criteria
- Given a reset link is older than 60 minutes, When the user clicks it, Then the user sees "Link expired" and can request a new link; no password is changed.
- Given a reset link has been used once, When the user clicks it again, Then the user sees "Link already used" and can request a new link; no password is changed.
- Given the user submits a new password shorter than 8 characters, When they click Save, Then the system shows validation error and disables Save until valid.
- Given a network interruption occurs after clicking Save, When the request fails, Then the system shows a retry option and the old password remains active.
- Given more than 5 reset requests from the same account within 10 minutes, When another request is made, Then the system shows a rate limit message and sends no email.
Example 2: File upload (negative + edge)
User story: As a user, I want to upload a profile picture so my account looks personal.
Acceptance criteria
- Given the user selects a file larger than 5 MB, When attempting to upload, Then the system blocks upload and shows "Max size 5 MB".
- Given the user selects an unsupported type (e.g., .exe), When uploading, Then the system blocks upload with a clear supported types list; no file is stored.
- Given the user uploads an image with extremely wide dimensions (> 4000px), When upload completes, Then the system auto-scales or rejects with guidance; no broken UI.
- Given the upload is interrupted (network drop), When the connection resumes, Then the system shows an error and allows retry; no partial file remains.
- Given duplicate uploads (same file clicked twice quickly), When requests arrive, Then only one image is saved; deduplicate to prevent duplicates.
Example 3: Checkout payment (negative + edge)
User story: As a shopper, I want to pay securely so I can place my order.
Acceptance criteria
- Given the payment is declined by the provider, When the user tries to pay, Then the order is not created, the cart remains intact, and the user sees the decline reason in plain language.
- Given 3-D Secure challenge is canceled by the user, When returning to checkout, Then no order is created and the user can retry payment.
- Given billing address does not match card, When processing payment, Then payment fails with a clear error and suggests verifying billing details.
- Given API timeout (> 30s) while processing, When timeout occurs, Then the system shows an unknown status message and checks payment status via callback; it does not double-charge or create a duplicate order.
- Given the user double-clicks Pay, When two requests are sent, Then idempotency ensures only one charge and one order are created.
How to write great negative/edge acceptance criteria
- Be explicit about state: what must not change on failure (e.g., no order created).
- Define user feedback: exact, human-readable messages or message intent.
- Protect integrity: idempotency, deduplication, rollback or no-commit behavior.
- Allow recovery: retry, back button, request a new link, re-upload, contact support.
- Quantify limits: size, time, count, attempts, rate limit windows.
Template snippets (copy/paste)
- Given [invalid input/expired token/exceeded limit], When [action], Then [clear message], and [no data change] and [recovery path].
- Given [concurrency/double-submit], When [requests arrive], Then [idempotent outcome] and [single record/charge only].
- Given [dependency failure/timeout], When [call fails], Then [inform user], [log for monitoring], and [allow retry].
Exercises
Complete the exercise below. You can compare with the solution in the toggle. In the Quick Test, progress is available to everyone; only logged-in users will see saved progress.
Exercise 1: Profile update form
Story: As a user, I want to update my profile (name, email, phone) so my account is accurate.
- List at least 6 acceptance criteria covering negative and edge cases.
- Include: invalid email/phone formats, empty required fields, rate limits, and concurrency (two tabs).
- State what must not change on each failure.
Show example solution
- Given the email is missing @ or domain, When saving, Then show "Enter a valid email" and do not update profile.
- Given phone contains letters, When saving, Then show validation error with allowed formats; do not update profile.
- Given name is blank or whitespace, When saving, Then show "Name is required"; do not update profile.
- Given more than 5 save attempts in 1 minute, When saving, Then show rate limit message and block further saves for 60 seconds; no partial update.
- Given the profile was changed in another tab, When saving, Then show a conflict message and allow user to review and merge; no silent overwrite.
- Given network timeout, When saving, Then show retry with spinner stop; profile remains unchanged until a successful save confirmation.
Common mistakes and self-check
- Vague outcomes: "Show error" without stating data state or recovery option. Self-check: Can QA verify what stays unchanged?
- Missing limits: Not defining max size/time/attempts. Self-check: Are numbers present?
- Ignoring dependencies: No plan for timeouts/500s. Self-check: What happens if the API fails?
- One-path thinking: Only happy path. Self-check: List at least 3 negatives and 3 edges per story.
- Ambiguous messages: Technical jargon. Self-check: Would a non-technical user understand?
Quick self-audit
- For each field, do I have invalid, empty, and boundary criteria?
- Are timeouts, retries, and double-submit handled?
- Is data integrity preserved on all failures?
- Is the recovery path clear?
Practical projects
- Authentication pack: Write acceptance criteria for login, lockout after failed attempts, session timeout, and remember-me expiration.
- Import wizard: Define negative/edge criteria for CSV import (empty rows, wrong delimiter, >10k rows, partial failures, rollback rules).
- Comments feature: Cover profanity filter, rate limit, edit window, deleted user comments, and pagination edges (0 items, last page).
Mini tasks (time-boxed)
- In 10 minutes, list 12 edge/negative cases for a search bar.
- Convert 5 of them into Given/When/Then statements.
- Review with the checklist and refine messages and data states.
Learning path
- Start: Review user story basics and the Given/When/Then format.
- Next: Use the 3-B fences to enumerate negative/edge cases.
- Apply: Write acceptance criteria for your current feature.
- Validate: Peer review with QA and a developer; refine limits and recovery flows.
- Assess: Take the quick test below; revisit any weak spots.
Next steps
- Use the checklist on your next PRD or story ticket.
- Pilot a 15-minute "edge-case storm" in grooming sessions.
- Track defects that escape; add missing scenarios back into your templates.
Mini challenge
Story: As a user, I want to change my email, so I continue receiving notifications.
- Draft 5 acceptance criteria for negative/edge cases: invalid formats, already-used email, confirmation link expired, no access to old email, and double-click on Save.
- State the exact user message and what must not change on failure.