Why this matters: “we have a lot of features but nobody in the team knows all the corner cases” is not a memory problem and it is not a people problem. It is a knowledge externalisation problem, and it is completely fixable. This is the fifth root cause from Why We Miss Deadlines.
Knowledge decays on a predictable schedule
Right after you build a feature, you know everything about it. Every edge case, every reason you chose one approach over another, every weird case the stakeholder mentioned once.
Then:
- After one month you remember the shape but not the exceptions.
- After six months you remember it exists.
- After a year, or after that person leaves, the code is the only remaining record, and code tells you what the system does, never what it was supposed to do. Those two are different exactly where the bugs are.
This is completely normal and expecting otherwise is unreasonable. The mistake is designing a process that assumes people remember. Once you accept that memory fails, the answer is obvious: write the behaviour down at the moment you know it, which is during the Story, not later.
What this costs you when you do not
It shows up as things nobody attributes to documentation:
- Estimates are guesses, because nobody knows what a change will touch.
- Every change to module X has to wait for the one person who built it.
- Onboarding takes months, and mostly consists of interrupting that same person.
- Nobody dares to refactor, so the code gets worse, so changes get slower.
- The same bug gets fixed twice, because the first fix was undone by someone who did not know why the odd looking code was there.
- When someone leaves, a part of the product becomes effectively unowned legacy code overnight.
That last one is the real risk in a startup. One resignation should not turn a working feature into an archaeological dig.
The feature one pager
The unit of documentation I would use is one page per Feature, not per Story and not per file. Feature level is right because it matches how people ask questions: “how does checkout work”, not “what does PaymentController do”.
# Feature: Stock movement on sales
**Owner:** <name> **Last reviewed:** 2026-09-18
**Status:** live **Epic:** Inventory tracking
## What it does
When a sale is invoiced, stock decreases for each line item at the invoice date.
Drafts do not affect stock. Cancelling an invoice reverses the movement.
## Why it exists
Businesses were overselling because stock only updated at month end.
## Expected behaviour
- Stock decreases when an invoice moves from draft to saved.
- Backdated invoices apply the movement at the invoice date, not today.
- Cancellation creates a reversing movement, it never deletes the original.
- Items marked "allow negative stock" can go below zero with a warning.
## Edge cases we know about
- Multi location sales take stock from the location on the invoice header.
- Stock at a past date is computed from movements, never from a stored snapshot.
- An item deactivated after a sale still shows in movement history.
- Imports of opening stock bypass this path entirely and post directly.
## What it depends on
Invoice service, item master, ledger posting for inventory value.
## How it is tested
Test cases TC-INV-010 to TC-INV-024. Automated: TC-INV-014, 015, 020.
## Known gaps
Serial number tracking is not implemented. Reservation for quotations is not implemented.
## Decisions worth remembering
2026-04: chose movement based calculation over a stored quantity column,
because backdated entries were corrupting the stored value. Slower reads,
but correct. Do not "optimise" this back into a column without solving that.
That takes twenty minutes to write when the knowledge is fresh, and it answers, for the next engineer: what should this do, what breaks if I change it, what is already tested, who do I ask.
The Decisions worth remembering section is the one people skip and the one that is most valuable later. It is a lightweight form of an Architecture Decision Record: the decision, the reason, and the thing you should not undo without understanding why. Without it, somebody “cleans up” that odd design in a year and reintroduces the bug it was built to solve.
Where to keep it
Close to the code, in the repository, in markdown. Not in a wiki nobody opens and not in a shared drive.
The reason is purely practical: documentation that lives next to the code gets updated in the same pull request as the code, gets reviewed alongside it, and gets versioned with it. Documentation in a separate system gets updated never, because it requires a second deliberate act after the work feels finished.
This also means that the person reviewing the change can see whether the documentation still matches, which is the only enforcement mechanism that consistently works.
Ownership is a name, not a team
Every Feature has one named owner. Not “the backend team”. A person.
The owner is not the only one allowed to touch it. The owner is the person who:
- keeps the one pager honest,
- is the default reviewer for changes to it,
- knows the open gaps and the fragile parts,
- and hands it over explicitly when they move on.
The value is that “who knows about X” stops being a guessing game, and handover becomes an event rather than an accident. When someone leaves, ownership transfer is a task on a list, including the conversation where the knowledge actually moves.
If a Feature has no owner and nobody wants it, that is useful information too. It usually means a feature nobody uses, which is a candidate for removal, and deleting a feature is a legitimate and underused form of maintenance.
How to build this without a documentation project
Do not launch an initiative. It will die in week three. Two rules instead:
New work documents itself. Any new Feature gets its one pager as part of Definition of Done. Twenty minutes per Feature, permanently.
Old work gets documented one per sprint. Pick the feature that is breaking most often, or the one only one person understands, and give it a page. Same slice used for test coverage in Test Case Strategy, and it works well to do both on the same feature at the same time, because writing the behaviour down and writing the test cases are almost the same activity.
Twelve sprints later, roughly half your product is documented, and the half you did first is the half that actually matters.
Keeping it from rotting
Documentation that is wrong is worse than documentation that is missing, because people trust it. Three defences:
Update it in the same pull request as the behaviour change. If the code changed and the page did not, the review is not finished.
Date it and review it. “Last reviewed” on every page. Anything older than six months on an actively changing feature gets a quick check by its owner.
Prefer executable truth where you can. A test case cannot silently drift, because when the behaviour changes the test fails. Prose can drift forever. So put the precise behaviour in test cases, and keep the prose for the things tests cannot express: why it exists, what the trade offs were, what is deliberately not supported. This is the Specification by Example idea, that the tests themselves are the living documentation, and the prose is the part that explains why.
The test for whether it is working
Pick any feature. Ask someone who did not build it:
“What is this supposed to do, what are its edge cases, how do I know it works, and who owns it?”
If they can answer in five minutes using written material, you have a maintainable product. If the honest answer is “ask Ravi, and hope he remembers”, you have a product that is one resignation away from a problem.
That is the difference between having built a lot of features and having a product. It is the same distinction as in Why We Miss Deadlines: shipping is not the finish line, and it matters even more when AI lets you ship faster than you can understand, which is Building with AI.