Code says how. Documents say why, what for, and how to start. This post covers three kinds of writing developers do all the time: design docs (think before you build), Architecture Decision Records (remember why you chose something), and READMEs (help a stranger get started in five minutes). It also gives a map for all other docs (Diátaxis), a short style guide, and text-based diagrams. Every template is copy-ready. It follows the Estimating post in Part E.

Series map: 01 Text Encoding → … → 21 Estimating → 22 Design Docs (this post) → 23 Money Basics → 24 Desk Health. Part E · Growth outside code.


Learning Outcome

  • Explain what a design doc is for, and when to skip it.
  • Write a design doc with context, goals and non-goals, proposal, alternatives, risks and rollout.
  • Record a decision as an ADR in Michael Nygard's format.
  • Learn from real RFC processes: Rust RFCs and Python PEPs.
  • Structure a README so a new user succeeds in minutes.
  • Sort documentation into Diátaxis's four types.
  • Write in plain language and draw diagrams as text with Mermaid and the C4 model.

Chapter 1 — Why write design docs

A design doc is a short document written before coding that describes the plan and, most of all, the trade-offs. Changing a paragraph is cheaper than changing a service.

Malte Ubl's article "Design Docs at Google" (2020) describes the practice there:

  • Design docs are informal documents. Authors write them before coding starts, to record the high-level plan and key design decisions, with a focus on trade-offs.
  • Common sections: context and scope, goals and non-goals, the actual design (including system-context diagrams, APIs, data storage), alternatives considered, and cross-cutting concerns such as security, privacy and observability.
  • Length: about 10-20 pages for a larger project; "mini design docs" of 1-3 pages for smaller, incremental work.
  • A lifecycle: write and iterate quickly, get a wider review, update the doc during implementation when reality differs, then keep it as a record.
  • Skip it when the solution is obvious and there are no real trade-offs to discuss, or when fast prototyping matters more.

What it buys you: colleagues find the flaw before you write 3,000 lines; everyone reads the same plan; non-goals stop the scope from growing in secret; six months later the "why" is still written down; and the breakdown and risks feed straight into an estimate (see Dev Essentials 21 · Estimating).

Terminology

Term Meaning
Design doc A plan for a change, with trade-offs, written before building
Non-goal Something the project will deliberately not do
ADR Architecture Decision Record — one short file per significant decision
RFC "Request for Comments" — a public proposal process, e.g. Rust RFCs
PEP Python Enhancement Proposal — Python's design-document process
Diátaxis A framework that splits docs into tutorials, how-to guides, reference, explanation
C4 model Simon Brown's four zoom levels for architecture diagrams

Chapter 2 — The design doc template

Typical sections, in reading order:

Section Answers Tip
Summary What are you proposing, in 3 sentences? Conclusion first — many readers stop here
Context What exists today, and what is the problem? Facts only, no solution yet
Goals / non-goals What counts as success? What is out of scope? Measurable goals; explicit non-goals
Proposal How will it work? Diagram, API shape, data model
Alternatives considered What else did you look at, and why not? At least one real alternative, plus "do nothing"
Trade-offs What do you give up? Be honest; reviewers will find them anyway
Risks What could go wrong? Likelihood, impact, mitigation
Rollout / migration How do you ship it safely and undo it? Feature flag, phases, rollback plan
Open questions What is still undecided? Name who decides, and by when

Copy-ready template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# Design: <short name>

| | |
|---|---|
| Author(s) | <names> |
| Status | Draft / In review / Approved / Implemented / Superseded |
| Last updated | YYYY-MM-DD |
| Reviewers | <names> |

## Summary
<3 sentences: the problem, the proposal, the main trade-off.>

## Context
<What exists today. Numbers if you have them. Link to tickets and incidents.>

## Goals
- <Measurable: "p95 search latency under 300 ms at 200 req/s">

## Non-goals
- <Out of scope, on purpose: "No multi-language search in this phase">

## Proposal
### Overview
<Diagram + one paragraph.>
### API
<Endpoints or function signatures.>
### Data
<Schema, storage, retention.>
### Cross-cutting concerns
<Security, privacy, observability, cost.>

## Alternatives considered
| Option | Pros | Cons | Why not |
|--------|------|------|---------|
| Do nothing | | | |
| <Option B> | | | |

## Trade-offs
<What we give up and why it is acceptable.>

## Risks
| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|

## Rollout and migration
1. <Phase 1 behind a feature flag>
2. <Backfill / dual-write>
3. <Switch reads, then remove old path>
Rollback: <how to undo each step>

## Open questions
- <Question> — owner: <name>, decide by: <date>

Common pitfall: a design doc that describes only the chosen solution. Without alternatives and trade-offs it is a manual, not a design doc, and reviewers cannot tell if it is the best option.

Trick: state goals as numbers where you can. "Fast search" cannot be tested; "p95 under 300 ms" can.


Chapter 3 — ADRs: one decision, one file

A design doc covers a project. An Architecture Decision Record covers one decision. Michael Nygard proposed the format in "Documenting Architecture Decisions" (2011):

  • Title — a short noun phrase, numbered: "ADR 7: Use PostgreSQL for billing records".
  • Status — proposed, accepted, deprecated, or superseded (with a link to the replacement).
  • Context — the forces at play, described as neutral facts.
  • Decision — in active voice, starting with "We will…".
  • Consequences — everything that follows, good and bad.

Other points from the same article:

  • Keep ADRs in the repository, for example doc/arch/adr-NNN.md, numbered in order. Numbers are never reused.
  • Each ADR is one or two pages, in full sentences.
  • When a decision is reversed, do not delete the old ADR. Mark it superseded and keep it — the history is the point.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# ADR NNN: <Decision as a short noun phrase>

Status: Proposed | Accepted | Deprecated | Superseded by ADR-MMM
Date: YYYY-MM-DD

## Context
<The situation and the forces at play: requirements, constraints, team skills,
deadlines, costs. Neutral facts, no decision yet.>


## Decision
We will <do X>.

## Consequences
- Good: <what becomes easier>
- Bad: <what becomes harder, new risks, new work>
- Follow-up: <tasks this decision creates>

Trick: write the ADR when you make the decision, not later. It takes 20 minutes then; it takes a day of archaeology in a year.


Chapter 4 — Real-world RFC processes

Big open-source projects run design docs in public. They are free lessons in how to argue for a change.

Rust RFCs. Substantial changes to Rust go through an RFC in the rust-lang/rfcs repository. The template's sections are: Summary, Motivation, Guide-level explanation, Reference-level explanation, Drawbacks, Rationale and alternatives, Prior art, Unresolved questions, Future possibilities. Note the two explanation levels: one for users ("how would I teach this?"), one for implementers.

Python PEPs. PEP 1 defines a PEP as a design document that gives information to the Python community or describes a new feature, process or environment change. There are three types: Standards Track, Informational and Process. The Python Steering Council makes the final decision, or a PEP-Delegate it approves. A PEP's sections include Abstract, Motivation, Rationale, Specification, Backwards Compatibility, Security Implications, How to Teach This, Reference Implementation, Rejected Ideas and Open Issues.

Both map onto the Chapter 2 template: Motivation → context and goals; Specification / reference-level explanation → proposal; Rationale, Rejected Ideas, alternatives → alternatives considered; Drawbacks, Backwards Compatibility → trade-offs and risks; Open Issues / Unresolved questions → open questions.

Trick: before writing your first design doc, read one accepted PEP and one accepted Rust RFC end to end. Notice how much space goes to alternatives and drawbacks.


Chapter 5 — READMEs

A README is the front door of a repository. GitHub's docs say a README typically covers what the project does, why it is useful, how to get started, where to get help, and who maintains it. GitHub looks for the file in .github/, then the repository root, then docs/.

A structure that works:

  1. Name + one-line description — what it is.
  2. Why — the problem it solves, in 2-3 sentences.
  3. Quickstart — the shortest path to a working result. Copy-paste commands.
  4. Usage — common tasks with examples.
  5. Configuration — every option, its default, and an example.
  6. Troubleshooting — the top 3-5 errors and their fixes.
  7. Contributing — how to set up, test and send changes.
  8. License.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# <project-name>

<One line: what it is and who it is for.>

## Why
<The problem, in 2-3 sentences. What makes this tool different.>

## Quickstart
Requires <runtime + version>.

~~~bash
git clone <repo-url>
cd <project-name>
<install command>
<run command>
~~~


Expected output:

~~~text
<what success looks like>
~~~


## Usage
<The 3-5 most common tasks, each with a command and its output.>

## Configuration
| Setting | Default | Description |
|---------|---------|-------------|
| `<NAME>` | `<value>` | <what it does> |

## Troubleshooting
**<Exact error message>** — <cause>. Fix: <steps>.

## Contributing
See `CONTRIBUTING.md`. Run tests with `<test command>`.

## License
<License name> — see `LICENSE`.

Watch out: a quickstart that nobody has run from a clean machine is usually broken. Test it in a fresh clone (or a container) before every release. On Windows, check both Git Bash and PowerShell commands if your users use both.


Chapter 6 — Diátaxis: four kinds of docs

A README cannot hold everything. Diátaxis, created by Daniele Procida, says documentation serves four different needs, and each needs its own kind of page:

Type Oriented to Reader is… Example for a CSV tool
Tutorial Learning New, following along "Clean your first CSV in 5 minutes"
How-to guide A task Working, has a goal "How to clean every file in a folder"
Reference Information Looking something up "All command-line options"
Explanation Understanding Thinking, wants the why "Why encoding is detected before parsing"

The four sit on two axes: learning vs doing (study vs work) and action vs cognition (practical steps vs theory). The most common mistake is mixing them — a tutorial that stops to explain theory, or a reference page that tries to teach. Keep each page one type, and link between them.

Diátaxis map: tutorials, how-to guides, explanation and reference on the axes learning vs doing and action vs cognition

A good README is a small tutorial (quickstart) plus links to the other three.


Chapter 7 — Writing clearly

Two useful public guides:

  • The Google developer documentation style guide. Its highlights include: be conversational without being frivolous; use second person ("you"); use active voice; put conditions before instructions; use sentence case for headings; use numbered lists for steps; write descriptive link text.
  • The US government's plain-language guidance. plainlanguage.gov now redirects to a set of plain language guides on digital.gov, adapted from the old site. They advise: write for your audience, summarise long documents up front, use active voice and present tense, prefer short words and short sections, and avoid "hidden verbs" (say "we analyse", not "we conduct an analysis").

Rules with before/after examples:

Rule Before After
Conclusion first "After evaluating several options over two weeks, and considering…, we propose Redis." "We propose Redis for sessions. It cuts login latency from 180 ms to 20 ms. Details below."
Active voice "The config file is read by the server at startup." "The server reads the config file at startup."
Short sentences One 45-word sentence with three commas and a semicolon Three sentences of 15 words
Condition first "Run setup.ps1 if you are on Windows." "On Windows, run setup.ps1."
No hidden verbs "Perform an installation of the package." "Install the package."
Descriptive links "Click here for the API." "See the API reference."
Concrete "The system is slow." "p95 latency is 2.4 s; the goal is 300 ms."

Trick: read your text aloud. Wherever you run out of breath or stumble, split the sentence.


Chapter 8 — Diagrams as text

Diagrams in image files go stale because nobody can edit them. Diagrams as text live next to the code, get reviewed in pull requests, and show clean diffs.

Mermaid

Mermaid turns text into flowcharts, sequence diagrams and more. GitHub renders a fenced code block with the mermaid language tag as a diagram in Markdown files, issues, pull requests, discussions and wikis. A sequence diagram of an order request (Exercise 6 draws a flowchart):

1
2
3
4
5
6
7
8
9
sequenceDiagram
participant C as Client
participant A as API
participant D as Database
C->>A: POST /orders (Idempotency-Key)
A->>D: INSERT order
D-->>A: ok
A-->>C: 201 Created

(If this blog shows those as code, paste them into GitHub or the Mermaid live editor to see the drawing.)

The C4 model

Simon Brown's C4 model is a set of zoom levels for software architecture, like a map app:

Level Diagram Shows Audience
1 System context Your system as one box, its users, and other systems it talks to Everyone
2 Container Apps and data stores inside the system (web app, API, database, queue) Developers, ops
3 Component Main parts inside one container Developers
4 Code Classes or functions (often generated, often skipped) Developers

C4 is notation- and tool-independent. Mermaid has C4 diagram syntax (C4Context, C4Container, …), but its docs mark it experimental, so the syntax may change. A plain flowchart with clear labels is often enough.

Trick: a design doc usually needs a level 1 (context) and a level 2 (container) diagram. Level 3 only where the change is.


Practice Exercises

Exercise 1 — Rewrite a bad README

Rewrite this README using the Chapter 5 template. The tool is a Python CLI called csvclean that fixes encodings and trims spaces in CSV files. It needs Python 3.10+. Config lives in csvclean.toml.

1
2
3
4
5
6
7
8
# proj2

This is my project. It uses python and some other stuff. To run it you need to
install things and set up the env and then run main. There might be bugs lol.
Config is in the config file. Ask me if something doesnt work.

TODO: write docs
Solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# csvclean

A command-line tool that fixes text encoding and stray spaces in CSV files.

## Why
CSV exports arrive in mixed encodings with padded cells. `csvclean` converts
every file to UTF-8 and trims each cell, so the files load cleanly anywhere.

## Quickstart
Requires Python 3.10 or newer.

Git Bash / macOS / Linux:

~~~bash
python -m venv .venv
source .venv/Scripts/activate # macOS/Linux: source .venv/bin/activate
pip install -e .
csvclean samples/orders.csv -o clean/orders.csv
~~~


Windows PowerShell:

~~~powershell
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -e .
csvclean samples\orders.csv -o clean\orders.csv
~~~


Expected output:

~~~text
orders.csv: detected cp1252 -> wrote UTF-8, 1,204 rows, 37 cells trimmed
~~~


## Usage
- Clean one file: `csvclean in.csv -o out.csv`
- Clean a folder: `csvclean data/ -o clean/`
- Preview without writing: `csvclean in.csv --dry-run`

## Configuration
Settings are read from `csvclean.toml` in the current folder.

| Setting | Default | Description |
|---------|---------|-------------|
| `output_encoding` | `"utf-8"` | Encoding of written files |
| `trim` | `true` | Strip spaces around each cell |

## Troubleshooting
**`UnicodeDecodeError`** — the encoding could not be detected. Fix: pass it
explicitly, e.g. `--input-encoding cp1252`.

## Contributing
Run the tests with `pytest`. Open an issue before large changes.

## License
MIT — see `LICENSE`.

What changed: a one-line purpose, a "why", a quickstart for both shells with expected output, a settings table, one real error with its fix, and a license.

Exercise 2 — Write an ADR

A team is building a billing service that stores invoices, payments and refunds. Finance needs SQL reports that join these with customer data. The team knows PostgreSQL well. The product catalogue elsewhere uses MongoDB. Write an ADR for the billing database.

Solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# ADR 007: Use PostgreSQL for billing records

Status: Accepted
Date: 2026-09-24

## Context
The billing service stores invoices, payments and refunds. These records are
strongly related (an invoice has many payments and refunds) and must stay
consistent. Finance runs ad-hoc SQL reports joining billing data with customer
data, using tools that connect over SQL. The team has years of PostgreSQL
experience. The product catalogue already runs on MongoDB, whose flexible
documents suit products with very different attributes. MongoDB also supports
multi-document transactions, so both options can keep billing consistent.

## Decision
We will store billing records in PostgreSQL, with foreign keys and CHECK
constraints on money columns. Amounts are stored as integer minor units
(see Dev Essentials 08 · Floating Point).

## Consequences
- Good: finance can query directly with SQL; the schema enforces relations.
- Good: the team can operate it with existing skills and runbooks.
- Bad: we now run two database technologies, with two backup and monitoring setups.
- Bad: schema changes need migrations; we will use a migration tool in CI.
- Follow-up: revisit if billing needs to embed catalogue documents often.

Notice the context is fair to the rejected option. A good ADR says why this choice fits this situation, not that one database is better in general. (For the move in the other direction, see MongoDB Course 23 · Migrating from SQL / Postgres to MongoDB.)

Exercise 3 — Goals and non-goals

A team plans "a new search for the help centre". Write two measurable goals and two non-goals.

Solution

Goals: "p95 search latency under 300 ms at 50 requests/s"; "the right article is in the top 3 results for at least 80% of the 100 most common queries (measured on a labelled set)". Non-goals: "no search across the community forum in this phase"; "no personalised ranking".

Exercise 4 — Sort into Diátaxis

Which type is each page? (a) "All environment variables and their defaults"; (b) "Build your first plugin, step by step"; (c) "How to rotate the API key"; (d) "Why we chose event sourcing".

Solution

(a) Reference. (b) Tutorial. (c) How-to guide. (d) Explanation.

Exercise 5 — Fix the sentences

Rewrite in plain, active language: (a) "It is recommended that the cache be cleared by the user prior to the upgrade being performed." (b) "Click here to see the migration guide." (c) "Run migrate.sh if you have an existing database."

Solution

(a) "Clear the cache before you upgrade." (b) "See the migration guide." (c) "If you have an existing database, run migrate.sh."

Exercise 6 — Draw it

Draw a Mermaid flowchart for: a browser calls a web app; the web app calls an API; the API reads from Redis and falls back to PostgreSQL on a miss.

Solution
1
2
3
4
5
6
flowchart LR
B[Browser] --> W[Web app]
W --> A[API]
A -->|1. get| R[(Redis)]
A -->|2. on miss| P[(PostgreSQL)]

Cheat-sheet

Need Use Key sections
Plan a change with trade-offs Design doc (1-3 pages mini, ~10-20 large) Summary, context, goals/non-goals, proposal, alternatives, risks, rollout, open questions
Remember one decision ADR (1-2 pages) Title, status, context, decision ("We will…"), consequences
Reverse a decision New ADR Mark the old one superseded; never delete
Help a stranger start README What, why, quickstart, usage, config, troubleshooting, contributing, license
Organise all docs Diátaxis Tutorial, how-to, reference, explanation — one type per page
Write clearly Google style guide, digital.gov guides Conclusion first, active voice, "you", short sentences, condition first
Diagram Mermaid in Markdown; C4 levels Context + container diagrams for most designs
Learn by example Rust RFCs, Python PEPs Motivation, alternatives, drawbacks, open questions

What's next

Dev Essentials 23 · Money Basics steps away from code: budgeting, an emergency fund, compound interest and index funds, explained for engineers.


References

Note: this post is in progress. Planned additions: a review checklist for design docs, a worked mini design doc end to end, and API reference docs generated from code (OpenAPI, docstrings).