ArchDocs
Start free

How to write a software design document: outline, examples and review checklist

A section-by-section guide to writing a software design document, with what good content looks like, what people leave out, and a checklist for reviewing one.

Author
The ArchDocs team
Published
Reading
8 min read

Design docs usually fail in one of two ways. Some are a page of intent with no detail, so reviewers approve something they can't picture and the real design happens in code review. Others are twenty pages of implementation notes, so nobody reads them and the questions that needed an answer get buried on page twelve.

A useful design doc sits between those. It says what problem the change solves, what will be true when it's done, how it will work, what else was considered and what could go wrong, in enough detail that a reviewer can disagree with something specific. This guide goes through each section, with an example of good content and the omissions reviewers catch most often.

When a design doc is worth writing

Write one when the change crosses a boundary (two services, a public API, a schema, a queue between teams), when someone else has to agree with the approach before you start, or when the work will take more than one pull request. Skip it for a change inside one component that changes no interface. A two-hour fix doesn't need a document, and writing one anyway teaches a team that design docs are paperwork.

Design doc, RFC, ADR and implementation plan

These names overlap, and teams use them differently. A workable split:

DocumentAnswersLifetime
Design docWhat are we changing, why, and how will it work?One change, until it ships
Technical RFCShould we do this, and is this the right approach?Until the proposal is accepted or dropped
Architecture decision recordWhat did we decide, and why, so future changes respect it?As long as the decision holds
Implementation planIn what steps will we build it, and how will we check each one?The duration of the build

Some teams call the same document an RFC and others call it a design doc, and that's fine. What's worth keeping apart is the decision record. A design doc describes one change. An ADR records a choice that outlives it, such as "services never call the payment provider synchronously". When a design doc makes a decision like that, write the ADR and link it, so the next change finds the rule without reading an old proposal. The ADR guide covers the format.

The implementation plan can be a section of the design doc or a separate document. Keep it separate when different people will review the approach and the steps.

An annotated outline

The blank design doc template has these sections ready to copy. The examples below come from the fictional Accounts API migration in the completed example, which replaces a customer_name string in an API response with a customer object.

Problem

Say what is wrong today, who it affects, and how you know. Link the ticket, incident or metric.

Good: "Accounts API returns the customer as one string, customer_name. Support and billing need the id, display name and legal name separately, and some services split the string themselves to get them."

Common omission: the evidence. "The current design is hard to work with" gives a reviewer nothing to weigh.

Goals and non-goals

Goals are what must be true when the change is done, written so someone could check each one. Non-goals are what the change deliberately leaves out.

Good goal: "No consumer breaks during the migration." A non-goal this change could add: "Changing how customers are stored in the accounts database. This change only affects the response."

Common omission: non-goals. Without them, reviewers assume adjacent work is included and approve a scope you didn't intend.

Constraints

Limits the design has to respect: compatibility promises, deadlines, budgets, compliance rules and earlier decisions. Link the ADR when a constraint comes from one.

Good: "Remove a response field only after its consumers have moved (ADR-0012)."

Proposed approach

How the change works. Describe the parts that change and how requests or data move between them. Add a diagram when the change crosses a service boundary. Explain the design at the level a reviewer needs to find a flaw, not at the level of every function.

Good: "Accounts API returns both fields first. Each consumer moves to customer in its own pull request with a contract test. customer_name is removed last."

Common omission: the order of operations. Migrations fail in the gaps between steps, and a design that lists only the end state hides them.

Affected components

List every service, database, queue and API the change touches, and what changes in each. Then list the consumers that have to do something.

Common omission: how the list was built. The example's consumer list came from 30 days of gateway logs, and the doc says so. That one sentence is what lets a reviewer ask "what about jobs that don't go through the gateway?"

Alternatives considered

Each alternative with the reason it lost. Include doing nothing.

Good: "A versioned /v2/accounts/{id} endpoint. Rejected because both shapes would be served for as long as any consumer stays on v1, and every consumer would still have to move."

Common omission: real alternatives. A list of strawmen tells the reviewer the decision was made before the doc was written.

Risks

What could go wrong, how likely it is, what it would break and how you would notice.

Good: "A consumer missing from the list breaks when the field is removed. The list may miss jobs that call the endpoint through another path or less often than monthly."

Common omission: how you'd notice. A risk with no detection plan becomes an incident with no warning.

Open questions

Each unresolved question with an owner, and a date if there is a deadline. When a question is answered, move the answer into the section it changes and note who decided.

Good: "When can customer_name be removed? Owner: the API tech lead."

Common omission: questions written as statements. "We'll remove the field after the migration" hides the question "how do we know the migration is finished?"

Implementation plan

The steps, each with an owner and what it depends on, plus how the change rolls out and how each step is undone. Keep each step small enough for one pull request.

Acceptance criteria and verification

Write each criterion as a check, with what proves it.

Good: "Each consumer's contract test passes against a response without customer_name." Also good: "No new production errors mention customer_name for 72 hours after removal."

Common omission: the check. "The migration is complete" is a goal, not a criterion. If you can't say what would show it's false, nobody can verify it.

What doesn't go in the draft

A design doc written before the work should not contain results. Two more records get written later. The first is a work log during implementation, with decisions, departures from the plan and handoffs. The second is evidence at the end: what shipped, which pull requests, and the result of each acceptance criterion. Keep them out of the draft so nobody mistakes a plan for a record of what happened. The completed example shows both, filled in after the fictional change shipped.

Reviewing a design doc

Ask reviewers for the specific thing you need: agreement on the approach, a check of the risks, or a decision on an open question. A review request that says only "thoughts?" gets comments on formatting.

For each review comment that changes the design, update the doc and reply to the comment with what changed. When implementation departs from the plan, which is normal on a change of any size, record the departure and why instead of editing the plan to match the code. A plan that silently changes is no longer something you can compare the result against. For a change with an approved spec, write the change as a new version so the reviewers can see what they approved and what replaced it.

AI review can be a useful first reader, especially for checking a design against code nobody on the review knows well. In ArchDocs, AI review (Team and above) reads the doc, the organization's shared docs and the code in linked repositories at a pinned commit, and records findings with evidence. The findings are advice. A person still approves the doc.

Review checklist

Use this before you ask for review, and give it to reviewers.

  • The problem names who is affected and links the evidence.
  • Every goal can be checked, and the non-goals name the adjacent work that is out of scope.
  • The constraints link the decisions they come from.
  • The approach describes the order of operations as well as the end state.
  • The affected components list says how it was built and what it might miss.
  • Each alternative has a real reason it lost, and "do nothing" is one of them.
  • Each risk says how you would notice it.
  • Each open question has an owner.
  • Each acceptance criterion names the check that proves it.
  • Lasting decisions are written as ADRs and linked.
  • Nothing in the draft claims a result that hasn't happened.

Next steps

Copy the blank template and fill in the problem and goals before anything else. If those two sections don't hold up, the rest won't either. If a coding agent will implement the change, read spec-driven development with coding agents for how the spec, plan and work log fit together while the agent works.

To write design docs in ArchDocs, start from design docs. Each service you mention links to your catalog, and the open questions, work log and evidence live on the same document. Free includes ten design docs.

By The ArchDocs team8 min read

 More in Design docs and decisions

  1. Architecture decision records, a practical guideRead →

 Start

Keep your diagrams honest