Software engineering · For beginners
Write It Before You Build It: A Practical Guide to Design Docs
Most expensive mistakes in software are not typos. They are the wrong thing built well, or the right thing built in a way that nobody else agreed with. A design doc is a short document you write before you build, so those mistakes show up while they still cost a paragraph instead of a month. This post covers what goes in one, a complete example, a template you can copy, the benefits, the costs, and the cases where you should not write one.
- A design doc explains what you plan to build, why, and how, and what else you considered.
- Its main job is to get feedback while change is still cheap.
- Good ones are short, name their goals and non-goals, and compare real alternatives.
- They cost time, and they go stale. Do not write one for small, easy-to-undo work.
- Ask the other teams the change affects: security, operations, support, product and more. Each sees a risk you cannot.
- A one-page template and a worked example are below.
Contents
1. What a design doc is
A design doc is a written plan for a change, shared with the people who will be affected by it before the work starts. It is not a specification that predicts every detail, and it is not documentation of finished code. It is a thinking tool: writing forces you to find the gaps, and sharing it lets other people find the ones you missed.
Analogy. An architect draws plans before the builders pour concrete. Moving a wall on paper takes an eraser. Moving it after the walls are up takes a demolition crew. A design doc is the paper stage.
It is usually a page or three, written in plain language. If you can explain the idea to a new teammate without opening the code, you are close to a good doc.
2. How a design doc lives
The document is not a one-off. It moves through a few simple stages, and the most valuable ones are the middle two: review and decision.
3. What goes inside
Different teams use different headings, but the useful ones are always the same. Each answers a question a reader will have.
| Section | Question it answers |
|---|---|
| Context | What is the situation, and why now? |
| Goals | What must be true when this is done? |
| Non-goals | What are we deliberately not doing? |
| Proposal | What exactly will we build, and how will the pieces fit? |
| Alternatives | What else did we consider, and why not those? |
| Risks | What could go wrong, and how will we notice? |
| Rollout | How do we ship it safely and undo it if needed? |
| Open questions | What do we still not know? |
| Reviews | Who looked at it, what did they say, and what changed because of it? |
The two sections people skip: Non-goals prevent scope creep by writing down what you will not do. Alternatives prove you chose rather than defaulted. Reviewers read these two first.
4. A worked example
Here is a small but realistic design doc, shortened to fit. The scenario: an online shop sends the order receipt email during checkout. The numbers are made up for illustration.
Design doc: send order receipts asynchronously
Author: Aiko · Reviewers: Product, Security, Operations, Support, QA, Finance, Data · Status: In review
Context
Checkout calls the email provider and waits for it before showing the “order placed” page. When the provider is slow, customers wait, and some press the button twice. Last month, 3% of checkouts took longer than 8 seconds, and every one of them was an email delay.
Goals
- Checkout never waits on email.
- Every receipt is still sent at least once, even if the provider is down for an hour.
Non-goals
- Changing the receipt design or wording.
- Replacing the email provider.
- Sending SMS or push receipts (a separate project).
Proposal
When an order is saved, we also save a small “send receipt” job in the same database transaction. A background worker reads pending jobs, sends the email, and marks the job done. Failed jobs retry with growing delays, up to 24 hours, then raise an alert.
Alternatives considered
| Option | Why not |
|---|---|
| Keep it synchronous, add a shorter timeout | Fewer slow checkouts, but a timeout means a lost receipt with no retry. |
| Fire and forget in a new thread | Simple, but a crash or restart silently drops emails. |
| Managed message queue service | The most robust, but a new system to run for about 200 orders a day. Revisit if volume grows tenfold. |
Risks
- Duplicate emails if a retry follows a send that actually worked. Mitigation: store the provider’s message id and skip jobs that already have one.
- Worker stops silently. Mitigation: alert when the oldest pending job is more than 15 minutes old.
Rollout
Ship behind a flag. Enable for 10% of orders for two days, compare receipt delivery rates, then 100%. To roll back, turn the flag off and checkout returns to the old path.
Open questions
- Is 24 hours the right retry limit for the support team?
- Should the customer see “receipt on its way” on the confirmation page?
Notice what the doc does not contain: no class names, no full code, no framework tutorial. It gives reviewers enough to disagree with the decision, and that is the point.
5. Reviews from other teams
Engineers reading an engineering doc will mostly find engineering problems. But the receipt change also touches customer data, on-call duty, the support desk, the budget and the team’s schedule. Each of those groups sees a different risk, and only they can see it. Asking them early is much cheaper than hearing from them after launch.
Who to ask, and what they look for
| Stakeholder | Typically checks |
|---|---|
| Product or business owner | The problem is real, the goals match what customers need, the scope is worth the cost. |
| Architect or tech lead | The design fits the wider system, reuses existing building blocks, follows agreed standards, and does not make future changes harder. |
| Engineering peers | The design fits the codebase, is testable, and does not create hidden maintenance work. |
| Teams that use your API or data | Nothing they depend on changes without warning. This is the compatibility check. |
| Security and privacy | What personal data is stored, who can read it, how long it is kept, and what an attacker could do. |
| Operations or SRE | Monitoring, alerts, capacity, runbooks, and how the change fails. |
| QA and test | That the failure cases can be reproduced and checked before release. |
| Designers (UX) | Anything the customer sees is clear, consistent and accessible. |
| Customer support | That support can see what happened to a customer’s request and explain it. |
| Data and analytics | That the success measures can be collected, and that existing reports do not silently break. |
| Finance | Running cost, vendor fees, and any change to billing. |
| Engineering or project managers | Effort, timing, staffing, dependencies on other teams, and whether the risk is acceptable for the schedule. |
| Legal or compliance | Regulations, contracts and data-retention rules, where they apply. |
You do not need all of them every time. Pick the groups whose work the change could affect. Asking someone who is not affected adds delay and no insight.
What the reviews looked like for the receipts doc
Here is a realistic set of comments on the example from section 4, and what the author did with each one. Notice that some comments changed the design, some were declined with a reason, and one fixed an open question.
| Reviewer | Comment | Outcome |
|---|---|---|
| Security | “The job table stores customer email addresses. Who can read it, and how long do we keep it?” | Accepted. Only the worker can read it. Completed jobs are deleted after 30 days. Added to the Risks section. |
| Architect | “The platform already has a standard retry helper. Can the worker use it instead of custom retry code?” | Accepted. The worker now uses the platform helper, so the retry delays come from shared code. Proposal section updated. |
| Operations | “An alert is good. Who gets it, and what do they do when it fires?” | Accepted. Alert goes to the on-call rotation. Added a short runbook and a queue-age dashboard. |
| Support | “Customers ask ‘where is my receipt?’ Can we see whether it was sent?” | Accepted. Added a read-only job status view for the support tool. Also answered the open question: yes, show “receipt on its way” on the confirmation page. |
| Product | “A receipt a day late is not useful for many orders. Is 24 hours right?” | Accepted. Retry limit reduced to 6 hours, then alert. Open question closed. |
| QA | “How do we test when the provider is down?” | Accepted. Added a test plan using a fake provider that fails on demand. |
| Finance | “Do retries increase what we pay the email provider?” | Answered, no change. The provider bills per accepted message, and the duplicate guard prevents double sends. Noted in the doc. |
| Data | “Can we measure the time from order to receipt?” | Accepted. Added a delivery-delay metric to the success checks. |
| Engineering manager | “This is about two weeks for one engineer, and on-call needs a short briefing. Can it land before the holiday sales freeze?” | Accepted. Added a timeline: build in two weeks, a 10% rollout in week three, and full rollout only after the freeze ends. |
| Mobile team (uses the order API) | “Does the order response change?” | Answered. No. Added a Compatibility line to say so. |
| Engineering peer | “Use a managed message queue instead of a database table.” | Declined, with reason. Already listed under Alternatives: too heavy for about 200 orders a day. Revisit if volume grows tenfold. |
When reviewers disagree
They will. Support may want receipts retried for as long as possible, while Product wants them dropped after a few hours. Do not average the opinions. Write both down, explain the trade-off, and let the person who owns the decision choose. Then record the outcome in the decision log so nobody reopens it by accident:
## Decision log
- 2026-10-12: Retry receipts for 6 hours, then alert a person.
Support asked for 24 hours; Product argued a late receipt is not
useful. Decided by the Product owner. Revisit if support tickets
about missing receipts rise.
How to run a review well
- Ask specific people specific questions. “Security, can you check the data retention section?” gets better answers than a broadcast to everyone.
- Set a date. “Comments by Friday” stops the doc waiting forever. Silence after the deadline means no objection.
- Separate blockers from nits. Ask reviewers to mark which comments must be fixed before building, and which are suggestions.
- Answer every comment, even with “declined, because…”. People stop reviewing if they feel ignored.
- Do not get defensive. A comment about your design is not a comment about you.
- Track who has approved. A small sign-off table at the end of the doc shows at a glance who is still outstanding.
- Talk when text stalls. After two rounds of back-and-forth, a ten-minute call is faster. Then write the outcome back into the doc.
6. A template you can copy
Paste this into your wiki, a markdown file in the repository, or a shared document. Delete sections that do not apply, but think twice before deleting Non-goals and Alternatives.
# Design doc: <short, specific title>
Author: <name>
Reviewers: <names>
Status: Draft | In review | Approved | Superseded
Last edit: <date>
## Summary
Two or three sentences. What are we doing and why?
## Context
What is the situation today? What problem hurts, for whom, how much?
Include numbers or examples if you have them.
## Goals
- What must be true when this is done? (testable if possible)
## Non-goals
- What this project deliberately will not do.
## Proposal
How it works, in plain words. Add one diagram.
Cover data, interfaces, and how the main pieces talk to each other.
## Alternatives considered
| Option | Pros | Cons | Why not |
|--------|------|------|---------|
## Risks and mitigations
- Risk: ... Mitigation: ...
## Security, privacy, and cost (if relevant)
Who can access what? What personal data is involved? What does it cost to run?
## Rollout and rollback
How do we release it gradually? How do we undo it?
## How we will know it worked
Metrics, alerts, or checks, and the target for each.
## Open questions
- Things nobody knows yet, and who will find out.
## Reviews and sign-off
| Team | Reviewer | Status (pending / approved / concerns) | Notes |
|------|----------|----------------------------------------|-------|
## Decision log
- <date>: <what was decided and why>
Scale the template to the work. A two-day change may need only Summary, Proposal and Risks, half a page in total. A change touching many teams may need every section.
7. What makes one good
- Write for a smart stranger. Someone from another team, or you in six months, should follow it without a meeting.
- State the problem before the solution. If readers disagree about the problem, no solution will satisfy them.
- Be specific. “Checkout is slow” is vague. “3% of checkouts take over 8 seconds” can be checked.
- Compare real alternatives, including doing nothing. A list with one option is a decision announcement, not a design.
- Use one good diagram instead of three paragraphs of boxes and arrows in words.
- Mark what you are unsure about. Open questions invite help. Hidden doubts become production incidents.
- Keep it short. If it reaches ten pages, split it, or move detail into an appendix.
- Review it like you want yours reviewed: kindly, with reasons, and aimed at the design, not the author.
8. Benefits
| Benefit | What it looks like |
|---|---|
| Cheaper mistakes | A wrong assumption is fixed in a paragraph, not after a month of code. |
| Clearer thinking | Writing exposes vague parts. If you cannot explain a step, you do not understand it yet. |
| Shared understanding | Everyone starts from the same picture, so fewer surprises in code review. |
| Better decisions | More eyes catch security, cost, and operations concerns early. |
| A memory for the team | New joiners learn why things are built this way, which code alone cannot tell them. |
| Easier to say no (or yes) | Debates focus on a page, not on whoever argues the loudest. |
| Asynchronous work | People in other time zones can contribute without joining a call. |
9. Costs and common failures
Design docs are not free, and bad ones are worse than none. Watch for these:
- Time. A solid doc and its review can take days. If the change takes an hour, the doc costs more than the mistake would.
- Going stale. Code changes, the doc does not, and soon it misleads. Mark old docs Superseded or link to the newer one.
- Paper certainty. A confident doc can hide guesses. Some things only become clear by building; treat a prototype as a valid way to answer an open question.
- Doc theatre. Writing one to satisfy a process, with the decision already made, wastes everyone’s time.
- Review as a bottleneck. Too many required reviewers, or no deadline for feedback, stalls the work. Name a few reviewers and set a date.
- Too much detail. Pseudo-code for every function turns a design doc into a bad program.
- Hard to find later. Keep docs in one known place and give them clear titles.
10. When not to write one
Match the weight of the paper to the weight of the decision. Two questions help: how hard is this to undo? and how many people does it affect?
| Skip the doc when… | Do this instead |
|---|---|
| The change is small and easy to undo (a bug fix, a rename, a copy tweak) | Explain it in the PR description. |
| The problem is well known and the solution is obvious to everyone | Post a two-line message and move on. |
| You do not yet know what is possible | Build a quick throwaway prototype, then write up what you learned. |
| It is a personal project or a one-day experiment | Keep a few notes for yourself. |
| An emergency fix is needed now | Fix first, write the short post-incident note after. |
| The doc would only record a decision already final | Write a short decision record, not a design doc. |
Rule of thumb: write one when the work will take more than a few days, touches other people’s code or data, is hard to reverse, or you find yourself explaining it more than twice.
11. Takeaways and glossary
- A design doc is a cheap way to find expensive mistakes early.
- Put real effort into goals, non-goals and alternatives. They are what reviewers read first.
- Keep it short, specific and honest about what is unknown.
- Scale it to the work, and skip it when the change is small or easy to undo.
- Treat it as a living document, and retire it when it is out of date.
| Term | In plain words |
|---|---|
| Design doc | A written plan for a change, shared for feedback before building. |
| Goal / non-goal | What the project must achieve, and what it deliberately will not do. |
| Alternative | Another way to solve the same problem, with the reason it was not chosen. |
| RFC | “Request for comments”: a design doc shared widely, often across teams. |
| Decision record | A short note capturing one decision and why it was made. |
| Stakeholder | Anyone whose work or customers are affected by the change. |
| Sign-off | A reviewer’s recorded approval that their concerns are addressed. |
| Blocking comment | A review comment that must be resolved before the work can start. |
| Rollout / rollback | Releasing a change gradually, and the plan for undoing it. |
| Feature flag | A switch that turns a new behaviour on or off without a new release. |
| Prototype | A quick, disposable build used to answer a question. |