The short answer
A product specification explains the outcome, the behavioural contract, the constraints, and the verification criteria for a change. Six sections, and each one earns its place by helping somebody make, implement, or test a decision.
- Six sections, none of them optional
- Non-goals stated as plainly as goals
- Branches and failures written down, not implied
- Acceptance cases named so tickets can cite them
On this page · 6 sections
Outcome and scope
Start with the result, not the interface.
State the current problem, who runs into it, what should become possible, and how you will know the change helped. Then write the non-goals, so adjacent good ideas do not quietly join the commitment.
- Problem — what is difficult or impossible today?
- Outcome — what observable result should change?
- Audience — which users or systems are affected?
- Non-goals — what is explicitly outside this revision?
Roles and permissions
A role is a set of capabilities. A persona is a story about a person.
Name every actor that can start, approve, observe, or be affected by the behaviour — including the system itself and anonymous visitors.
For anything sensitive, write both halves: who is allowed to act, and who must not be able to. The second half is the one that gets discovered in a penetration test if you leave it out.
Primary flow and decision branches
Write the happy path first, then branch at the exact condition.
Start with the smallest successful sequence from trigger to outcome. Add branches at the point where a condition changes what happens, and give each branch both a condition and a destination. Alternatives buried in a notes section at the end do not get implemented.
A complete flow covers:
- Trigger and preconditions.
- Ordered user and system steps.
- Decision conditions and where each branch lands.
- Failure, retry, cancellation, and recovery behaviour.
- Notifications and downstream side effects.
Data and state
The product-level data contract. Not a database dump.
List the entities the feature reads or changes. For new fields, record the type, whether it is required, the allowed values, the default, and its relationships. Then state the lifecycle: which transitions are legal, how long the data is kept, and whether an action can be undone.
| Record | Example |
|---|---|
| Field and type | expiresAt — datetime, required |
| Allowed values | status — pending | active | revoked |
| Relationship | belongs to one User; a User has many |
| Lifecycle | revoked is terminal; no path back to active |
| Retention | deleted 24 hours after expiry |
| Reversibility | revocation cannot be undone by the user |
Copying the whole schema helps nobody. Include what a reviewer needs to validate behaviour and what an implementer needs to avoid an incompatible assumption.
Acceptance and edge cases
Prove the contract. Do not restate the happy path in new words.
Give each case a stable name so tickets and automated tests can point back at it. A case that only describes success is a description, not a test.
Cover the cases that actually break in production:
- A user without permission attempts the action.
- Input is invalid, missing, or hostile.
- The same request arrives twice.
- A dependency times out mid-flow.
- Two actors change the same state at once.
Decisions, risks, and approval
Open questions need an owner. Approved revisions need a name.
Record each unresolved question with the person who owns it and the decision that is actually needed. Capture the risks that could change scope or architecture, and leave general project management outside the product contract.
Before you call the spec ready
- The outcome and the non-goals are unambiguous.
- Every actor has explicit capabilities and restrictions.
- The primary flow includes decisions, failures, and recovery.
- Data fields and state transitions are typed at the product-contract level.
- Acceptance cases cover the important negative paths.
- Open questions have owners; approved decisions have a revision history.