Skip to content

Instantly share code, notes, and snippets.

@Hrissimir
Last active March 27, 2026 00:54
Show Gist options
  • Select an option

  • Save Hrissimir/c2ccee4bab6bb1b0acfa02ee538da2a9 to your computer and use it in GitHub Desktop.

Select an option

Save Hrissimir/c2ccee4bab6bb1b0acfa02ee538da2a9 to your computer and use it in GitHub Desktop.
Documentation-Driven Development — A Contributor's Handbook

Documentation-Driven Development — A Contributor's Handbook


"The palest ink is better than the best memory." — Chinese proverb

"If it isn't documented, it doesn't exist." — The first rule of the fightclub this project


This document is pure gold!

source: https://gitlab.com/-/snippets/5973012

author: Matthias Schuster (correct me if I'm wrong)

NOTES:

Save as Guide.md at the repo root for GitHub/GitLab to surface it automatically on the repository homepage and on every new PR and issue page

The author's checklist in Section 6 can be copied verbatim into .github/PULL_REQUEST_TEMPLATE.md or .gitlab/merge_request_templates/default.md so it appears pre-filled on every new PR

The templates in Section 9 can each be extracted into their own files under docs/templates/ and referenced by path, if you prefer to keep CONTRIBUTING.md shorter

The only concrete technology reference removed is the rendering pipeline — Section 3.7 now instructs contributors to consult docs/toolchain.md, which you maintain separately and can update when your stack changes without touching this governance document

Table of Contents

  1. A Letter to New Contributors
  2. The Philosophy in Full
  3. Core Concepts You Must Understand
  4. Your Workflow, Step by Step
  5. What Every PR Must Contain
  6. The PR Acceptance Policy
  7. How to Be a Good Reviewer
  8. The Cost and the Payoff
  9. Document Templates
  10. The Living Glossary
  11. Frequently Asked Questions

1. A Letter to New Contributors

Welcome. You are about to contribute to a project that takes documentation seriously — not as a courtesy to future developers, not as a box to check before shipping, but as a discipline that drives the work itself.

This means something specific: you will write before you code.
You will describe what you intend to build before you build it.
You will explain why you made a decision in the same commit that enacts it.
You will draw the structure before you lay the foundation.

This may feel unfamiliar. In many projects, documentation is an afterthought.

Written under deadline pressure, immediately inaccurate, and abandoned within a release cycle. That is not what happens here, and it is not what we are asking of you.

What we are asking is a different relationship between thinking and building. Writing is thinking. A feature you cannot describe clearly is a feature you do not yet understand. An architecture diagram that contradicts the running system is an architecture that no one is governing. A decision recorded only in someone's memory is a decision waiting to be undone by the next person who encounters it.

The workflow described in this document will cost you time at the start of every piece of work. It will save you — and everyone who comes after you — a multiple of that time before the work is done. This is not an article of faith. It is a pattern observed consistently across decades of software engineering. The earlier a mistake is caught, the cheaper it is to fix. Documentation-driven development moves the discovery of mistakes to the earliest possible moment: before implementation begins.

Read this document fully before you open a code editor. Then keep it close.


2. The Philosophy in Full

The document is the first deliverable

On this project, the first thing you produce for any non-trivial change is a document — a specification, a diagram, a decision record, or an RFC. This is not a warm-up exercise. It is the work. The code that follows is an expression of that document in a form the machine can execute.

This sequence is non-negotiable:

↓
Describe the change (spec / RFC)
↓
Record the decisions (ADRs)
↓
Draw the structure (diagrams)
↓
Review and agree
↓
Write tests aligned to the spec
↓
Implement
↓
Verify implementation matches spec
↓
Merge

Steps are not optional. They are not skipped because a feature is "small." They may be brief — a two-paragraph spec for a minor change, a two-line ADR for a trivial choice — but they are always present.

Documentation is a contract, not a caption

There is a fundamental difference between documentation written before implementation and documentation written after it.

Documentation written before implementation is a contract: it describes intended behaviour, expresses the author's understanding, invites disagreement before the cost of disagreement is high, and commits the team to a shared model. When the implementation deviates from this document, that is a signal that either the document was wrong (revise it) or the implementation is wrong (fix it). Either way, you learn something before shipping.

Documentation written after implementation is a caption: it describes what was built. It is accurate on the day it is written and begins drifting immediately. It cannot catch design errors because design is already fixed. It rarely asks "is this the right thing to do" because the thing is already done.

We write contracts, not captions.

Implicit knowledge is a liability

Every piece of knowledge that exists only in someone's head is a liability to this project. That person will leave. They will forget. They will be unavailable at the critical moment. They will remember the decision but not the reasoning, and the reasoning is almost always what matters.

We treat undocumented knowledge the same way a finance team treats an undeclared transaction: it may exist, but it cannot be trusted, verified, audited, or relied upon. The default assumption for anything not recorded in docs/ is: it does not exist as project knowledge.

The code answers "what." The docs answer "why."

A sufficiently skilled engineer can read any codebase and reconstruct what it does. What they cannot reconstruct — not from the code, not from the tests, not from the git log — is why it does it that way. Why PostgreSQL and not SQLite? Why this state machine and not that one? Why was the original design abandoned? Why is this seemingly arbitrary limit set to 4096?

The answer to every "why" lives in docs/decisions/. If it is not there, the answer is lost. When lost answers are needed — and they always eventually are — engineers are forced to either guess, or reverse-engineer intent from implementation, or make a different decision and repeat the mistake the original decision was made to avoid.

ADRs are the project's institutional memory. They compound in value over time.


3. Core Concepts You Must Understand

3.1 The Ubiquitous Language

One of the most powerful ideas in software design is this: the words used in the code should be the same words used in the documentation, the discussions, the issue tracker, and the conversations with domain experts. No synonyms. No abbreviations. No "in the code we call it X but in the docs we call it Y."

This shared vocabulary is called the ubiquitous language. It is documented in docs/architecture/glossary.md.

What this means for you:

  • Before naming anything — a type, a function, a module, a file — check the glossary. Use the established term.
  • If you need a new concept that does not yet exist in the glossary, add it before you use it in code. The glossary entry is part of your spec.
  • Never use synonyms for glossary terms in documentation or commit messages. If the glossary says Transaction, do not write payment, charge, or event in its place.
  • If you believe an existing term is wrong, open an RFC to rename it. A rename that touches only docs is far cheaper than a rename that touches both docs and code.

The ubiquitous language is a living agreement. Guard it carefully.

3.2 Bounded Contexts

The system is not a monolith of meaning. Different parts of the codebase model different parts of the real world, and those models do not need to agree with each other in every detail — they need to agree within their own domain and communicate across boundaries through explicit contracts.

A bounded context is a region of the system in which a particular model and a particular language are consistent and authoritative. The word Account might mean something subtly different in the billing context and the authentication context. That is not a bug. It is a recognition that forcing one model to serve all purposes produces a model that serves none well.

What this means for you:

  • Every docs/architecture/contexts/ file describes one bounded context: its responsibilities, its language, its boundaries, and its contracts with neighbouring contexts.
  • Before adding a concept, check which bounded context owns it.
  • If a change crosses a context boundary, it requires explicit documentation of how the contexts communicate (via events, via API, via shared kernel).
  • Adding a new bounded context is an architectural change and requires an RFC.

3.3 The Document Hierarchy

Documentation in this project is organised into four layers. Each answers a different question. Each has a different audience and lifecycle.

┌──────────────────────────────────────────────────────────────────────┐
│ LAYER 1 — Architecture Decisions (ADRs)                              │
│ Question answered: WHY?                                              │
│ Audience: All contributors, present and future                       │
│ Location: docs/decisions/ADR-NNN-*.md                                │
│ Lifecycle: Written once, never deleted, only superseded              │
├──────────────────────────────────────────────────────────────────────┤
│ LAYER 2 — Architecture Documentation                                 │
│ Question answered: WHAT does the system look like?                   │
│ Audience: Engineers, architects, technical reviewers                 │
│ Location: docs/architecture/                                         │
│ Lifecycle: Updated whenever structure, boundaries, or flows change   │
├──────────────────────────────────────────────────────────────────────┤
│ LAYER 3 — Feature and Module Specifications                          │
│ Question answered: WHAT does this component do from the outside?     │
│ Audience: Implementors, reviewers, testers                           │
│ Location: docs/specs/SPEC-NNN-*.md                                   │
│ Lifecycle: Written before implementation, updated with the PR        │
├──────────────────────────────────────────────────────────────────────┤
│ LAYER 4 — Inline Documentation                                       │
│ Question answered: HOW does this specific code work?                 │
│ Audience: Engineers reading the source                               │
│ Location: In source files (comments, docstrings, annotations)        │
│ Lifecycle: Updated atomically with every code change it describes    │
└──────────────────────────────────────────────────────────────────────┘

Every PR touches at least one layer. Most touch at least two. A PR that touches zero layers of documentation is incomplete by definition.

3.4 Architecture Decision Records

An ADR is a short, structured, permanently committed document that records a single architectural or design decision: the context that made it necessary, the decision itself, the alternatives that were considered and rejected, and the consequences — good and bad — that follow from it.

ADRs are the answer to the question every engineer eventually asks while reading code: "Why on earth does it work like this?" Without ADRs, that question has no reliable answer. With them, the answer is always one file away.

Properties of ADRs:

  • Immutable once accepted. An ADR is never edited to change its meaning after acceptance. If a decision is reversed, a new ADR is written, and the original is updated only to set its status to Superseded by ADR-NNN. The historical record is preserved intact.
  • Permanently committed. ADRs are never deleted. The fact that a decision was made and later reversed is itself valuable knowledge.
  • Sequential and numbered. ADR-001, ADR-002, and so on. Numbers are never reused.
  • Small and focused. One ADR = one decision. If you are tempted to write an ADR that makes multiple decisions, split it.
  • Written at decision time. An ADR written after the fact describes what was done, not what was decided. Write it when the decision is fresh.

When to write an ADR:

Write an ADR when you make a decision that:

  • Has consequences that are hard or expensive to reverse later
  • Involves a meaningful tradeoff between two or more reasonable alternatives
  • Would surprise a competent engineer encountering the code for the first time
  • Concerns the adoption of a library, framework, protocol, or data format
  • Changes a convention that was previously established and documented
  • Resolves a question raised and left open in an earlier RFC or spec

When in doubt, write the ADR. The cost is ten to twenty minutes. The benefit compounds for the lifetime of the project.

3.5 Specifications

A specification (spec) describes a feature or module from the outside — its observable behaviour, its inputs, its outputs, its constraints, its error conditions, and its edge cases. It does not describe implementation.

Think of a spec as a detailed, prose-form test suite. A reader should be able to verify whether an implementation is correct by comparing it to the spec, without reading the implementation's source code. If the spec is not detailed enough for that, it is not complete.

Specs are written before implementation begins. They are the primary artefact for design review. Feedback on a spec is architectural feedback — it shapes what gets built. Feedback on an implementation is code review — it shapes how it was built. The former is almost always more valuable.

A spec may evolve during implementation. If you discover during coding that the spec was wrong or incomplete, update the spec as part of the same PR. The spec and the implementation must always agree. If they disagree, one of them is wrong and it is your job to resolve the conflict explicitly.

3.6 Requests for Comments (RFCs)

An RFC is a proposal for a significant change, submitted as a pull request against docs/rfcs/ and reviewed before any implementation work begins.

RFCs are appropriate when a change is:

  • Architecturally significant (affects component boundaries, data models, or public contracts)
  • Highly uncertain (the right approach is not yet clear)
  • Cross-cutting (affects multiple bounded contexts or teams)
  • Irreversible or expensive to reverse
  • Likely to be controversial

An RFC is not appropriate for routine feature work. Use the spec template for that. The distinction is scale and risk.

The RFC process:

  1. Author writes an RFC using the template in Section 9.3
  2. RFC is opened as a pull request. No implementation PR should be opened while the RFC is under review.
  3. Team reviews, comments, and requests changes over a defined discussion period (typically one to two weeks)
  4. RFC is either accepted, rejected, or withdrawn. Decision is recorded in the document's status field.
  5. Only after acceptance does an implementation PR open.
  6. The implementation PR links to the accepted RFC. The RFC links back to the implementation PR.

An RFC that is rejected is not a failure. It is a record of a path considered and not taken, which is exactly as valuable as a path taken.

3.7 Diagrams as Code

All architecture diagrams in this project are maintained as plain text source files committed to docs/architecture/diagrams/. No binary diagram files are committed, and no screenshots of external tools.

The rationale is not aesthetic. It is practical:

  • Plain text produces meaningful diffs in pull request review. You can see exactly what changed in a diagram between versions.
  • Text diagrams are reviewable on the same platform as the rest of the PR. A diagram that cannot be reviewed is a diagram that drifts.
  • Text diagrams can be regenerated at any time from a clean checkout. They are reproducible and tooling-independent.
  • Text diagrams can be kept in version control alongside the code they describe, and tagged alongside releases.

Diagram conventions:

Diagrams follow the C4 model hierarchy for architecture visualisation:

Level 1 — Context: The system in its environment. Shows the system as a black box, surrounded by users and external systems it interacts with. Every project must maintain a current Level 1 diagram.

Level 2 — Container: The deployable units (services, databases, queues, front-ends) that make up the system, and the communication between them. Must be updated whenever a container is added, removed, or renamed.

Level 3 — Component: The internal structure of a single container: its modules, their responsibilities, and the interfaces between them. Required for any container you introduce or significantly modify.

Level 4 — Code: Class and function level detail. Not hand-maintained. Generated from source by tooling if needed.

For sequence diagrams (runtime behaviour), use a notation that clearly shows actors, messages, and ordering. Document every non-trivial flow that involves more than two components.

Diagrams are rendered locally using the project's agreed toolchain, documented in docs/toolchain.md. Rendered outputs are not committed; they are generated as part of the documentation build.


4. Your Workflow, Step by Step

The following sequence applies to all non-trivial contributions. "Non-trivial" means anything beyond a typo fix, a formatting correction, or a one-line patch to an already-specified behaviour. When in doubt, treat your change as non-trivial.

Step 0 — Orient yourself

Before designing anything, read:

  • docs/architecture/README.md — the system overview
  • docs/architecture/contexts/ — the bounded contexts and their boundaries
  • docs/architecture/glossary.md — the ubiquitous language
  • Any existing specs or ADRs related to the area you are changing

Do not start designing until you understand the existing structure. A change that ignores existing boundaries will be rejected in review regardless of its technical quality.

Step 1 — Stake out the scope

Write one paragraph answering three questions:

  1. What problem am I solving?
  2. Which bounded context(s) does this touch?
  3. What is explicitly out of scope for this change?

This paragraph does not need to be submitted anywhere. It is for you. If you cannot write it clearly, you are not ready to design yet.

Step 2 — Write the RFC (if applicable)

If this change is architecturally significant, open an RFC first. See Section 3.6 for criteria. Do not open an implementation PR until the RFC is accepted.

Step 3 — Write the specification

Create a spec file in docs/specs/ using the template in Section 9.1. Describe the feature from the outside. Write it as if it already exists and works exactly as intended. A reader who has never seen the codebase should understand from the spec alone what the feature does, what it accepts, what it returns, and how it behaves under error conditions.

Do not describe the implementation. Do not mention data structures, algorithms, or internal module names unless they are part of the public contract.

Submit your spec for review before writing significant implementation. This is the cheapest moment for feedback.

Step 4 — Update the architecture diagrams

If your change affects:

  • Component or container boundaries
  • Data flows between components
  • Deployment topology
  • Any public interface

...update the relevant diagrams in docs/architecture/diagrams/. If a new diagram is needed (e.g. a new component), create it.

Diagrams are reviewed in the PR alongside code. An outdated diagram is a failing review item.

Step 5 — Record your decisions

As you design and implement, capture every non-obvious decision as an ADR. You will make more decisions than you expect. Write the ADR at the moment of decision, not at the end of the PR. The reasoning is freshest now.

Step 6 — Update the glossary

If you are introducing new domain concepts — new types, new bounded context terms, new names for system entities — add them to docs/architecture/glossary.md before using them in code.

Step 7 — Write tests against the spec

Your spec is now the authoritative source of truth for this change. Write your tests to verify the behaviour described in the spec. Each test case should be traceable to a specific sentence or assertion in the spec. If a test case has no basis in the spec, either the spec is incomplete (update it) or the test is testing an implementation detail (reconsider it).

Step 8 — Implement

Write the code. The implementation fulfils the spec. The spec does not describe the implementation. The flow of authority goes:

spec → tests → implementation

If the implementation suggests the spec was wrong, update the spec. If the implementation suggests the tests were wrong, update the tests. Never silently diverge from the spec without recording the divergence.

Step 9 — Update inline documentation

Every function, type, module, and interface you touch must have accurate, current inline documentation before the PR is opened. "Accurate" means it describes what the code does now, not what it did before your change.

Step 10 — Self-review against the checklist

Before marking your PR as ready for review, run through the checklist in Section 5. Every unchecked item is a reason the PR will not be merged.


5. What Every PR Must Contain

The following lists define what "complete" means for a pull request on this project. Reviewers are expected to verify these items before approving. Authors are expected to satisfy them before requesting review.

Always required

  • A link to the specification (new or updated) for everything this PR changes. The spec lives in docs/specs/. If the change is so small that a full spec is disproportionate, a clear description in the PR body that covers behaviour, inputs, outputs, and edge cases is sufficient — but this is the exception, not the rule.

  • Updated inline documentation for every function, type, module, and interface touched by this PR. Documentation that describes the previous behaviour is worse than no documentation.

  • A CHANGELOG.md entry under [Unreleased] that describes the change in terms a reader who has not seen this PR can understand.

Required when applicable

  • One or more ADRs for any non-obvious decisions made during this PR. If you made a decision and briefly wondered whether to do it a different way, you made an ADR-worthy decision.

  • Updated architecture diagrams if this PR changes component boundaries, data flows, deployment topology, or public interfaces.

  • An updated or new bounded context description if this PR affects context boundaries or introduces a new context.

  • A glossary update if this PR introduces new domain terminology.

  • A reference to the accepted RFC if this PR implements a change that was subject to the RFC process.

Never acceptable

  • A PR whose documentation consists entirely of "see the code"
  • A PR that changes behaviour without updating the spec that describes it
  • Inline comments marked // TODO: document later or equivalent
  • A PR that introduces undocumented exceptions to the behaviour described in an existing spec
  • Documentation promised in a follow-up PR as a condition of merging this one (see Section 6)

6. The PR Acceptance Policy

A pull request that does not meet the documentation requirements in Section 5 will not be merged. This policy has no exceptions.

This applies to all contributors, all maintainers, all timelines, and all urgency levels. "We need to ship this today" is not an exception. A PR that ships undocumented is a debt that compounds daily and is almost never repaid. We do not incur it.

What reviewers must do

Reviewers are not just checking whether the code is correct. They are checking whether the change is complete. A change without its documentation is not complete, regardless of how good the code is.

When a reviewer encounters a PR that is missing required documentation, they must request changes — not approve and comment, not merge and create a follow-up issue. Request changes. Block the merge. The author must complete the documentation before the PR can advance.

This is not hostile. It is the highest form of technical respect: treating your colleague's future self, and every future contributor to this codebase, as worthy of the information they will need.

The "follow-up PR" rule

"I'll document it in a follow-up PR" is not acceptable as a condition for merging.

The reasons are structural, not punitive:

  1. Follow-up documentation PRs describe code that was already merged. They describe what was built, not what was intended. They are captions, not contracts. They provide none of the design-review value of pre-merge documentation.

  2. Follow-up documentation PRs are almost never opened. The urgency that justified skipping documentation does not disappear after merging; it simply transfers to the next urgent thing. The documentation gap becomes permanent.

  3. Allowing follow-up documentation creates an incentive to rush merges. If "we'll document later" is accepted once, it becomes the norm. The entire discipline unravels.

Follow-up documentation PRs are permitted only in the following cases:

  • Expanding examples, tutorials, or usage guides that do not affect correctness guarantees
  • Correcting minor factual errors in documentation that does not contradict the current implementation
  • Adding documentation for pre-existing, explicitly acknowledged legacy code, tracked as a specific item in docs/tech-debt.md with a ticket number and a committed deadline

If you believe your situation requires an exception, discuss it with a maintainer before opening the PR, not after.

The author's pre-submission checklist

Complete this before marking your PR as ready for review:

### Documentation
[] Spec is written or updated for all changed behaviour
[] All touched functions, types, and modules have accurate inline docs
[] CHANGELOG.md entry is present and describes the change clearly

### Decisions
[] ADRs are written for all non-obvious decisions in this PR
[] Glossary is updated if new terms are introduced

### Architecture
[] Diagrams are updated if structure or interfaces have changed
[] Bounded context descriptions are current

### Consistency
[] I have re-read the spec and verified the implementation matches it
[] I have re-read the diagrams and verified they match the implementation
[] No inline comments say "document later" or "TODO: explain this"

7. How to Be a Good Reviewer

Code review on this project is documentation review as much as it is code review. These two activities are inseparable.

Reviewing the documentation

When you open a PR to review, read the spec first — before reading the code. Ask yourself:

  • Is the problem statement clear? Do I understand what this feature does and why it exists?
  • Are the edge cases and error conditions explicitly addressed?
  • Does the spec use the ubiquitous language correctly?
  • Is anything promised by the spec absent from the implementation?
  • Is anything in the implementation absent from the spec?

A spec that requires you to read the code to understand it is an incomplete spec. Request changes.

Reviewing the diagrams

Open the updated diagrams before reviewing the code structure. Ask:

  • Do the diagrams reflect what the code will look like after this PR merges?
  • Are new components shown? Are removed components gone?
  • Are interface changes reflected in the diagram?

A diagram that does not match the implementation is a failing item. Request changes.

Reviewing the ADRs

For each ADR in the PR, ask:

  • Is the context accurate and complete?
  • Were the real alternatives considered, or only obviously inferior ones?
  • Are the consequences — especially the negative ones — honestly stated?
  • Is this a decision that should have been an RFC instead?

Reviewing the inline documentation

For every function or type modified in the PR, verify:

  • Does the documentation describe current behaviour, not previous behaviour?
  • Would a reader understand what the function does without reading its body?
  • Are error conditions and edge cases documented?

A note on tone

Documentation review can feel more personal than code review because it concerns understanding, not just correctness. Be direct, be specific, and be respectful. "This section of the spec doesn't address what happens when the input is empty — please add that" is a precise, constructive request. "This spec is unclear" is not.

The goal is always a complete, accurate, maintainable artefact — not a judgement of the author.


8. The Cost and the Payoff

Documentation-first development is slower at the beginning of every task. This is true and should not be obscured. Writing a spec before implementing takes time. Writing an ADR takes time. Updating diagrams takes time.

Here is what that time buys:

Fewer wrong implementations

A spec that is reviewed before implementation catches design errors at the cheapest possible moment. Fixing a misunderstanding in a two-paragraph spec takes minutes. Fixing a misunderstanding discovered after three weeks of implementation takes days or weeks and may require architectural surgery.

Faster code review

A PR with a clear spec requires the reviewer to check: does the implementation match the spec? This is a faster and more reliable question than: does the implementation do the right thing? Without a spec, the reviewer must reconstruct the author's intent from the code itself, which is slower and more error-prone.

Faster onboarding

A new contributor who can read docs/architecture/ and docs/decisions/ and docs/specs/ and understand the system's structure, history, and rationale before touching a line of code is productive much faster than one who has to reverse-engineer all of that from the codebase.

Durable institutional knowledge

Engineers leave projects. They go on holiday. They get sick. They move to other teams. Every piece of knowledge that exists only in someone's memory leaves with them. Documentation-driven development ensures that the project's knowledge lives in the repository, not in the team.

The compound effect

The value of a well-maintained doc corpus increases non-linearly with time. A two-year-old ADR that explains why a critical subsystem was built the way it was can save weeks of investigation when that subsystem needs to change. A three-year-old spec that describes an edge case no one remembers discussing can prevent a regression that would otherwise have gone undetected until production.

These benefits are invisible until they are needed. When they are needed, they are invaluable.


9. Document Templates

Copy these templates as the starting point for new documents. Remove all instructional text (in square brackets) before submitting.


9.1 Feature Specification Template

File: docs/specs/SPEC-NNN-short-descriptive-title.md

# SPEC-NNN: Short Descriptive Title

| Field        | Value                                      |
|--------------|--------------------------------------------|
| Status       | Draft / In Review / Accepted / Implemented |
| Author       | Your Name                                  |
| Date         | YYYY-MM-DD                                 |
| Bounded Context | [Which context does this belong to?]    |
| Related ADRs | ADR-NNN, ADR-NNN                           |
| Related RFC  | RFC-NNN (if applicable)                    |

***

## Problem Statement

[What is the problem being solved? Who experiences it, and in what context?
Write for a reader who understands the domain but has not thought about this
specific problem. Be concrete. Avoid implementation language.]

## Goals

[Numbered list of things this feature must do. Each item should be
verifiable: a reader should be able to look at an implementation and
determine whether a goal has been achieved.]

1.
2.
3.

## Non-Goals

[Explicit list of things this feature does NOT do and will NOT do in this
iteration. Non-goals prevent scope creep and make the feature's boundaries
explicit. If a non-goal is likely to be misunderstood, explain why it is
out of scope.]

- This spec does not cover:
- This spec does not address:

## Behaviour

[Describe the feature from the outside. Inputs, outputs, constraints,
invariants, error conditions, and edge cases. Structure as a sequence of
clear assertions about observable behaviour. Write in the present tense
as if the feature already exists.

Use subsections if the feature has multiple distinct modes or flows.

Do not describe implementation internals. If you find yourself writing
about data structures, algorithms, or internal module boundaries,
you are writing implementation notes, not a specification. Move those
to inline documentation in the code.]

### Normal Flow

### Error Conditions

| Condition | Expected Behaviour |
|-----------|-------------------|
| | |

### Edge Cases

## Examples

[Concrete examples of inputs and expected outputs. These should be
precise enough to serve as the basis for test cases.]

## Open Questions

[Questions that must be resolved before or during implementation.
Each question must have an assigned owner and a target resolution date.
This section is cleared (moved to resolved or closed) before the spec
is marked Accepted.]

| Question | Owner | Target Date | Status |
|----------|-------|-------------|--------|
| | | | Open |

***

*This spec is owned by the author listed above. Questions and change
requests should be directed to them in the PR discussion.*

9.2 Architecture Decision Record Template

File: docs/decisions/ADR-NNN-short-title-in-kebab-case.md

# ADR-NNN: Short Title

| Field   | Value                                                      |
|---------|------------------------------------------------------------|
| Status  | Proposed / Accepted / Deprecated / Superseded by ADR-NNN  |
| Date    | YYYY-MM-DD                                                 |
| Author  | Your Name                                                  |
| Context | [Bounded context or system area this decision applies to]  |

***

## Context

[Describe the situation that required this decision. What forces are at
play? What constraints exist — technical, organisational, temporal?
What is the cost of doing nothing or deferring?

Write this section in neutral, factual terms. This is not the place to
justify the decision. It is the place to establish why a decision was
necessary at all.]

## Decision

[State the decision in one to three sentences. Be direct and unambiguous.
Use active voice: "We will use X" not "X was determined to be preferable."

Do not explain the reasoning here. That belongs in the next section.]

## Reasoning

[Why is this the right decision given the context above? What evidence,
analysis, or experience supports it? This section is the most important
part of the ADR — it is why this document exists.]

## Alternatives Considered

[For each alternative you seriously considered, describe it and explain
why it was not chosen. Do not dismiss alternatives with one word.
"Too slow" or "too complex" is not an explanation. Be specific about
what made the alternative unsuitable for this context.]

| Alternative | Why Not Chosen |
|-------------|---------------|
| | |
| | |

## Consequences

### Benefits

[What becomes easier, faster, safer, or clearer as a result of this
decision?]

-

### Drawbacks and Tradeoffs

[What becomes harder, slower, or more constrained? What are we giving up?
What is the known cost of this decision?]

-

### Risks

[What could go wrong? What assumptions underpin this decision, and what
happens if those assumptions prove incorrect?]

-

## Review Conditions

[Optional. If this decision should be revisited after a certain milestone,
a time period, or a change in circumstances, state that here.]

## References

- [Link to related spec, RFC, ticket, or external resource]

9.3 RFC Template

File: docs/rfcs/RFC-NNN-short-title-in-kebab-case.md

# RFC-NNN: Short Title

| Field      | Value                                            |
|------------|--------------------------------------------------|
| Status     | Draft / In Review / Accepted / Rejected / Withdrawn |
| Author     | Your Name                                        |
| Date       | YYYY-MM-DD                                       |
| Discussion | [Link to PR]                                     |
| Replaces   | RFC-NNN (if applicable)                          |

***

## Summary

[One paragraph. What are you proposing and why? A reader should understand
the core idea after this paragraph without reading further.]

## Motivation

[What problem is this RFC solving? Why is the current state of the system
insufficient? Why is this the right time to address it?

Be specific. "This would be an improvement" is not motivation.
"This is blocking X because of Y, and the cost of the current approach
is Z per week" is motivation.]

## Detailed Design

[The complete technical proposal. This section must be detailed enough
that an engineer could implement from it without asking clarifying questions.

Include, as applicable:
- Interface definitions (function signatures, type definitions, contracts)
- Data model changes
- Sequence diagrams for runtime behaviour
- Migration strategy for existing data or behaviour
- Impact on bounded contexts and their boundaries
- Impact on the ubiquitous language / glossary

Use subsections freely. This section should be the longest in the RFC.]

## Drawbacks

[An honest accounting of what this RFC costs. Complexity introduced.
Performance implications. Migration effort. Maintenance burden.
What does the team give up by accepting this proposal?

Do not minimise drawbacks. A reviewer who finds an undisclosed drawback
will trust the RFC less. Disclosing drawbacks demonstrates that you have
thought the proposal through fully.]

## Alternatives

[What other approaches were considered? For each, explain what it would
look like and why it was not chosen as the primary proposal. Include the
option of doing nothing.]

| Alternative | Summary | Why Not Chosen |
|-------------|---------|----------------|
| Do nothing | | |
| | | |

## Unresolved Questions

[What questions are intentionally deferred to implementation? What must
be discovered during building and fed back into this RFC or into ADRs?

This is not a list of things you haven't thought about. It is a list of
things you have thought about and explicitly chosen to defer, with a
justification for the deferral.]

## Implementation Plan

[An outline of the implementation sequence if accepted. Major milestones,
order of operations, and dependencies between phases. This does not need
to be a full project plan — a numbered list of logical steps is sufficient.]

1.
2.
3.

9.4 Diagram Header Convention

All diagram source files must begin with a standard header comment:

@startuml (or equivalent opening for your chosen format)
' Title: [Human-readable title]
' Level: [C4 Level 1 / 2 / 3 | Sequence | Flow | State | ERD]
' Context: [Bounded context(s) this diagram describes]
' Updated: YYYY-MM-DD
' PR: [Link to the PR that last updated this diagram]
' Owned by: [Name or team]

This header ensures that any diagram file found in isolation — in a generated artefact, an exported PDF, or a downloaded archive — retains its provenance and context.


10. The Living Glossary

The glossary at docs/architecture/glossary.md defines all terms that are part of the project's ubiquitous language. Its format is:

## TermName

**Bounded Context:** [Which context owns this term]
**Status:** Active / Deprecated / Proposed

[One paragraph definition. Written precisely enough that two engineers
reading it will apply the term consistently. If the term means something
different in different contexts, document each context's usage separately.]

**Not to be confused with:** [Related terms that are distinct]
**See also:** [Related ADRs or specs]

Terms are listed alphabetically. New terms are added by PR, reviewed like any other documentation change. Deprecating a term requires an ADR explaining why and what replaces it.

Using a term that is not in the glossary in code or documentation is a review-blocking item.


11. Frequently Asked Questions

"This is a small change. Do I really need a full spec?"

If the change is truly small — a one-line fix to a correctly specified behaviour — you do not need a full spec. You do need an accurate description of what changed and why, either in the PR description or as an update to the relevant existing spec. "Small" is about scope, not about importance. A small change that alters an external interface is not exempt from documentation requirements.

"The spec I wrote turned out to be wrong during implementation. What do I do?"

Update the spec. This is not a problem — it is the system working correctly. You discovered a design error before shipping, not after. Update the spec to reflect what you learned, note the change in the PR description, and if the change was significant, consider whether an ADR is warranted to explain why the original design was wrong and what the correct model turned out to be.

"I'm fixing a bug in behaviour that was never specified. Do I write a spec for the old behaviour or the new behaviour?"

Write a spec for the correct behaviour — that is, the behaviour after your fix. Note in the spec that this formalises previously undocumented behaviour, and reference any relevant bug report. If the bug existed because of an architectural misunderstanding, write an ADR for the underlying decision that produced the correct model.

"I'm working on legacy code that has no documentation at all. Where do I start?"

Start with the area you are changing. You do not need to document the entire legacy codebase before opening a PR. You need to document the specific behaviour you are changing. Add the legacy code area to docs/tech-debt.md as an acknowledged gap, and contribute one documented component at a time. Over multiple PRs, the coverage grows.

"How much detail is enough for an ADR?"

Enough that a competent engineer reading it in two years, without access to anyone who was present for the decision, can understand what was decided, why, and what it would mean to change it. If you would need to ask you a question after reading the ADR, it needs more detail.

"A reviewer is asking me to document something that feels obvious."

Write the documentation. What is obvious to you today, given full context, is not obvious to someone encountering this code for the first time next year. The test of whether documentation is necessary is not whether you need it. It is whether anyone else ever might.

"I disagree with this policy."

Open an RFC. The documentation standards described in this document are themselves subject to the RFC process. If you have a substantive argument for a different approach, make it in writing, invite discussion, and let the team deliberate. That is exactly the process this document describes.


This document is itself subject to the standards it defines. If you find a section that is unclear, incomplete, inconsistent, or incorrect — open a PR to improve it. The specification of our process deserves the same care as the specification of our software.

Last meaningful revision: see git log docs/CONTRIBUTING.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment