Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 27 additions & 16 deletions docs/rules/DEV-180.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,40 @@ depends_on: ["DEV-110", "DEV-170"]

## Problem

When a Spec describes behavior that already shipped, or `docs/product/`
describes behavior that was never built, a reader cannot tell what the product
actually does today. The backlog and the documentation rot into fiction.
A spec that stays behind after its behavior ships is indistinguishable from one
that is still a plan. Readers take shipped work for unbuilt, and the stale text
collides with the next spec written for the same feature.

## Solution

Split intended behavior from shipped behavior, and move sections across as work
lands.
Keep one source of truth for what the product does, and a separate one for what
stakeholders still want. The product is the app together with its end-user docs.
The spec is the plan, and holds only what is not built yet.

1. `docs/specs/<feature>.md` holds only unimplemented behavior for a Goal.
`docs/product/<feature>.md` holds only what currently ships.
1. Each PR that delivers behavior moves the corresponding sections out of the
spec and into the appropriate file under `docs/product/`. A spec may graduate
into more than one `docs/product/` file.
1. When scope is added after the Spec merges, open a new PR against the spec
A spec states what the business needs before anyone builds it. The product docs
are part of the product, not a description of it: they carry what a user cannot
work out from the screen alone. Once behavior ships it stops being a proposal
and becomes a fact in the source code, so it leaves the spec. What a user still
needs in order to use it moves to the product docs. What the UI already makes
obvious goes nowhere, because a field visible on the screen documents itself.

1. `docs/specs/<feature>.md` holds only unimplemented behavior for a Goal. The
product docs hold only what ships today: `docs/product/<feature>.md` by
default, or the path the repo's docs README declares where it differs.
1. Each PR that delivers behavior moves the delivered sections out of the spec.
Graduate what a user cannot infer from the UI, drop the rest. A spec may
graduate into more than one file.
1. When scope is added after the spec merges, open a new PR against the spec
file and create Problem issues for the added scope.
1. When every section has graduated, keep the spec file as frontmatter only.
Never delete it, so the Goal's `# Spec` link keeps working.
1. When every section has graduated or been dropped, keep the spec file as
frontmatter only. Never delete it, so the Goal's `# Spec` link keeps working.
1. Do not close a Goal while its spec still holds behavior that shipped.

### Acceptance Criteria

- [ ] `docs/specs/` holds only unimplemented behavior
- [ ] `docs/product/` holds only shipped behavior
- [ ] Each shipping PR graduates the delivered sections from spec to
`docs/product/`
- [ ] The product docs hold only shipped behavior, and only what the UI does not
make obvious
- [ ] Each shipping PR graduates or drops the delivered sections
- [ ] A fully graduated spec file is kept as frontmatter only, not deleted
- [ ] No Goal is closed while its spec still describes shipped behavior
Loading