Skip to content

Software design document: a template that expires on purpose

Scribelet Team
13 min read

Six months after the payment service shipped, someone searches the wiki for how it works. The top hit is the design doc.

It is twenty pages, carefully written, and it describes a system with three queues and a Redis cache. The service that runs in production has one queue and no Redis. That change happened in week two, in a pull request comment, and the doc was never touched again.

The doc wasn't wrong when it was written. It was a proposal. Then the proposal got argued with, which is what proposals are for, and the argument happened somewhere else.

This is the part almost nobody writes about. There is no shortage of guidance on what sections belong in a software design document. There is very little on what the document is for after the code merges, which is when most of them quietly become misinformation with a table of contents.

So: what a software design document is, what belongs in one and what to cut, a template with expiry built into it, how it differs from an ADR or a runbook, and what to do with the thing once the system it describes actually exists.

What is a software design document?

A software design document (SDD) is a written proposal for how a system will be built, produced before implementation starts, so that reviewers can find problems in the design while changing it is still cheap. It states the problem, the proposed approach, the alternatives that were rejected, and the trade-offs accepted. Working through those is also what turns a day-one estimate from a guess with a huge margin into a narrowing range, because the design phase is where the biggest unknowns finally get resolved.

Note the tense. A software design document describes a system that doesn't exist yet. Most definitions call it a blueprint, and that word does real damage, because a blueprint implies a faithful description of a finished building.

The formal standard is more careful. IEEE 1016 frames a software design description as a representation used to communicate a design to its stakeholders, which describes an audience, not a finished system. A design doc is closer to a planning application: a serious, detailed argument about something you intend to build, subject to revision by everyone who reads it, including the timeline, which will slip for reasons the plan cannot yet name.

Getting the tense right changes how you write one, how long you make it, and what you do with it afterwards.

Scribelet was built on the observation that documents start decaying the moment they are saved. A design doc is the purest case: it is out of date by design, on a schedule you can predict. Start writing with Scribelet if you want the docs that outlive the design doc to stay true.

What actually goes in a software design document

The section list is not the hard part, and it is where most guides stop. Angela Zhang's guide to writing a good design doc remains the best public version of the list, and it is worth reading. Here is the working set, with the thing each section is really for.

Context and problem statement. What is broken or missing, for whom, and what it costs today. If a reader cannot restate the problem after this section, nothing below it matters.

Goals and non-goals. Non-goals do more work than goals. They are how you stop the review from expanding into every adjacent system, how you keep a product from bloating past its scope, and the first thing to check when someone later asks why the design did not handle a case. On a rewrite they matter even more: an explicit non-goals list is the surest defense against a bloated second-system rewrite, the pull to pour every feature the first version skipped into its successor.

Proposed design. The approach, the components, how data moves between them. Diagrams belong here, and if you write them as diagrams as code they stay in the doc's diff instead of rotting as an exported image; so do the interfaces between pieces owned by different people, because those are where the expensive misunderstandings live. Describe the version you will build next, not the entire end state: a complex system designed from scratch rarely survives contact with reality, so the strongest designs specify a simple system that works and the next layer they will grow onto it.

Alternatives considered. The designs you rejected and why. This is the highest-value section in the document and the most commonly skipped. It is also the section most likely to still be true in a year, since the reasons an approach was rejected tend to outlive the approach that won.

Trade-offs and risks. What this design is bad at. A design doc with no weaknesses listed has not been thought about hard enough.

Rollout and migration. How the change reaches production, and how it comes back out if it goes badly.

Open questions. What is genuinely undecided. Naming these attracts exactly the review comments you need.

What to cut

Cut the boilerplate front matter: scope statements, glossaries of terms every reader knows, document revision history nobody maintains. Cut anything the code will state more precisely than prose ever could, such as full schemas and exhaustive API signatures. Cut restatements of the company's standard architecture.

Google's internal practice, described in Design Docs at Google, pushes the same way: the useful documents are the short ones that get read, and the ceremony around them is what kills them.

The test for a section is simple: could a reviewer disagree with it? A section nobody can disagree with is not a design decision, and it is costing you review attention that should go somewhere else.

A software design document template

Copy this. The first four fields exist because a design doc has a predictable expiry date, and almost no published template acknowledges that.

# Design: [system or change]
 
Author:
Reviewers:
Status: draft | in review | accepted | shipped | superseded | abandoned
Written: YYYY-MM-DD
Design last true as of: YYYY-MM-DD
On merge, this becomes: [ADR #, runbook, README section, or "archive"]
 
## Context and problem
What is broken, for whom, and what it costs today.
 
## Goals
## Non-goals
 
## Proposed design
The approach. Components, data flow, interfaces between pieces owned by
different teams.
 
## Alternatives considered
| Option | Why not |
|---|---|
 
## Trade-offs and risks
What this design is bad at.
 
## Assumptions
| Assumption | What would invalidate it |
|---|---|
 
## Decisions deferred
Decisions we are explicitly not making now, and when we will make them.
 
## Rollout and rollback
 
## Open questions

Four fields there are unusual, and they are the point of the template.

Status including shipped and abandoned. Most templates stop at "accepted", which means every document in the folder looks equally live forever. A doc marked shipped tells a reader the system exists and this file is history. A doc marked abandoned saves the next person from designing the same thing again.

Design last true as of. Separate from the written date. When the design changes in review or during implementation, this is the field you touch. The gap between the two dates is the fastest available signal that a doc has drifted.

Assumptions with an invalidation column. "We assume fewer than 500 writes per second" is a fact with a shelf life. Writing down what would break it turns a buried assumption into something checkable later.

On merge, this becomes. The most important line in the template. It forces the author to decide, up front, which durable document inherits this content once the code ships. Without it, the design doc becomes the permanent home for information it was never built to hold.

Software design document vs ADR, RFC, PRD, and runbook

The related searches for this term are full of people trying to work out which document they actually need. The honest answer is that these are different documents with different expiry dates, and using one for another's job is where most documentation rot starts.

DocumentAnswersWrittenExpires
Design doc / SDDHow should we build this?Before implementationAt merge. The system it describes is now the source of truth.
Technical design documentSame thing, different house style. Often narrower, one component rather than a system.Before implementationAt merge.
ADRWhy did we choose X over Y?At the moment of decisionNever. It records a decision that was true then, and gets superseded, not edited.
RFCShould we do this at all?Before the designWhen the decision is made.
PRDWhat should this do, and for whom?Before the designWhen the feature ships.
RunbookWhat do I do when this breaks at 3 a.m.?After the system existsContinuously, every time the system changes.
READMEHow do I use or run this today?With the codeContinuously, and it lives next to the code so it can be reviewed with it.

Timeline comparing when a PRD, RFC, software design document, ADR, runbook and README are written and when each one expires

Two rows deserve attention.

The design doc and the technical design document are the same artifact in most organisations. Where teams distinguish them, the design doc covers a system or feature and the technical design document covers one component's implementation. If your team uses both words for the same thing, that is normal, and standardising on one is not worth a meeting.

The ADR is the only document in this table that never expires, because it is scoped to a decision rather than a system. That is exactly why it is the right destination for the "alternatives considered" section when the design doc retires. Our guide to architecture decision records with real examples covers the template and the supersession pattern that makes this work.

Why the doc stops being true at merge

Design docs do not rot the way a wiki page rots. They fail on a schedule, and the schedule is short.

The divergence starts in review. Someone points out that the three-queue design has a head-of-line blocking problem, and the team agrees on one queue with a priority field. That decision is now in a comment thread. It reaches the code the same week and it never reaches the document, because updating the document is nobody's task in the sprint and the code is what needs to ship.

By merge day, the doc describes a design that was superseded by its own review. Nothing about this is negligent, and nobody was lazy. The review worked exactly as intended: it changed the design. The failure is that the artifact was treated as durable when it was written to be argued with.

Then a second, worse thing happens. The doc stays in the wiki, well written and confident, and it outranks the code in search results. Six months later it's the first thing a new engineer reads about the payment service, and the specific hazard of a design doc is that it's plausible.

A stale runbook fails loudly, with a command that exits non-zero. A stale design doc fails silently, by teaching someone a mental model of a system that was never built. This is the same failure mode we covered in why internal documentation goes stale: the first draft always gets written, and the second draft is nobody's job. The AI-native heir to the design doc inherits it too: the spec you hand a coding agent is authoritative right up until the code moves past it.

Write sections a reviewer can check

Most weak design docs are weak in the same way. They describe intentions rather than commitments, which makes them impossible to disagree with in review and impossible to check afterwards.

Before:

The service will handle high traffic and scale as needed. We will use caching to improve performance and ensure a good user experience.

Nobody can review this. There is no number to challenge, no mechanism to question, and in a year there is no way to tell whether it is still true.

After:

Peak load today is 400 writes per second, measured over the last 90 days. This design targets 1,200, which is three times the current peak, using a single queue with a priority field. Above roughly 2,000 writes per second the single consumer becomes the bottleneck and we would need to partition by tenant. We are explicitly not building partitioning now (see Decisions deferred).

The second version can be argued with, which is the point of the review. It also stays useful afterwards: the numbers can be compared against reality later, and "we are not building partitioning yet" is exactly the context the next engineer needs.

A good heuristic while drafting: if a sentence would survive being copied into a different company's design doc unchanged, cut it. It is not carrying information about your system.

Scribelet's background agents run against claims like these. When a doc asserts a number, a version, or a limit, that assertion is something a verification pass can check later. See how AI verification works if the idea of a document that reports its own drift is new.

What to do with the doc after the code ships

This is the step that separates a design doc that ages gracefully from one that quietly misinforms people for years. Do it in the same pull request that merges the implementation, while the divergence is still fresh in someone's head. If nothing prompts for it there, it will not happen: three templates instead of one covers how to make the prompt fit the size of the change.

Diagram showing a software design document moving from draft to review to shipped, then handing its content off to an ADR, a runbook, a README, and an archive

  1. Set Status: shipped and update Design last true as of. Two fields, thirty seconds, and every future reader now knows what they are holding.
  2. Move the decisions out. Each entry in "alternatives considered" that the team will care about later becomes an ADR. The design doc explained one system; the ADR explains one choice, and it stays valid long after the system is rewritten.
  3. Move the operations out. Rollout, rollback, failure modes, and anything that answers "what do I do when this breaks" belongs in a runbook, where it will be exercised and corrected.
  4. Move the usage out. How to run the thing, its configuration, its interfaces: README, next to the code, reviewed when the code changes.
  5. Archive the rest. Not delete. Move it somewhere clearly marked as history, so the search hit six months from now carries the right label.

Note what survives. The design doc is the only document in this list built to be discarded, and the four documents it feeds are the ones built to last. Treating the design doc as permanent is what puts durable content in a disposable container.

Keeping the surviving documents true

The conversion step solves the design doc. It does not solve what comes next, because the ADRs, runbooks, and READMEs it produces are subject to the same decay as any other document. They just decay more slowly, which is a real improvement and not a fix.

What breaks them is external change: a library deprecates the API a runbook depends on, a service you documented changes its rate limits, a link in an ADR moves. None of it generates a notification. The document keeps looking correct. This is the general case of outdated documentation, and it is why a review cadence based on the calendar rarely survives contact with a quarter that has a fire in it.

Scribelet runs background agents against your notes and docs on a schedule you set. They search the web, compare what they find to what you wrote, and return a diff: green for what changed, red for what no longer holds, with source links so you can judge the finding yourself. If your docs live alongside code, the GitHub connector pulls the repository into the same checks, so a doc claiming a flag that no longer exists gets flagged.

You choose the model and, if you want the sovereignty path, bring your own API key. Your keys are encrypted at rest, and your documents go to the provider you picked.

None of this writes your design doc for you. It answers the question that comes after: which of the documents you kept are still true today. Try AI verification free and start with the doc you trust least.

The short version

A software design document is a proposal for a system that does not exist yet. Write it to be argued with, keep it short enough that people actually review it, and give it fields that make its expiry visible: a status that includes shipped, a date for when the design was last true, assumptions with what would invalidate them, and an explicit note about which document inherits the content at merge.

Then, when the code ships, actually do the handover. Decisions become ADRs. Operations become runbooks. Usage becomes a README. The design doc gets marked shipped and archived, having done its job.

The doc that describes your system is not the doc you wrote before you built it. It never was.

Share this article

We use cookies for analytics to improve your experience. Learn more