Skip to content

Software Design Document

beta  ·  Family decision-docs  ·  Phase develop  ·  Sizes lean, full  ·  ~2,350 tokens

Describes how a system will be built: the technical design, its structure, the alternatives weighed, and the cross-cutting concerns, so the design can be reviewed before the code exists. Describes an implementation, as distinct from an RFC (which proposes a decision) and an ADR (which records one).

Fast reference for using the SDD bundle. For the full reasoning, history, and sources, read sdd_companion.md.

  • You are about to build something more than a one-file change, and thinking the design through and getting it reviewed first would catch an expensive mistake on paper.
  • The work is hard to estimate or hard to deliver without settling the design (a good default trigger).
  • The design touches more than one component or team, and people who were not in the room need to reason about it.
  • A choice in the design is hard to reverse (a data model, a public API, a persistence or auth decision) and worth a record.
  • You want feedback on whether to do this at all. That is an RFC: it proposes a decision and asks for input. (Many teams call this a design doc too; the distinction that matters is whether the primary output is a decision-before-building or a description of the implementation.) A design doc describes how you will build something.
  • You are recording a single, already-made, hard-to-reverse decision. That is an ADR. (A design doc may still spin one off; see below.)
  • The change is trivial or fully reversible. A pull-request description is enough. A design doc for a one-line change is process for its own sake.
  • The design is genuinely unknowable until you build it, and a spike would teach you more than a document. Prototype first, then write the doc if the design still warrants review.

Design doc, or RFC, or ADR? (the question people actually have)

Section titled “Design doc, or RFC, or ADR? (the question people actually have)”
Design doc / SDD RFC ADR
Answers “How will we build it?” “Should we, and which way?” “What did we decide, and why?”
Timing Before/while building Before the decision After the decision
Describes An implementation A proposal A decision
State Living, then archived Mutable while open Immutable once accepted
Scope The whole design One proposed change One decision

They are a division of labor, not a competition. Much of the industry uses “design doc” and “RFC” interchangeably, which is fine when one document does both jobs; the moment feedback on the decision is the point, it is an RFC. The ADR is genuinely distinct: when your design makes a significant, hard-to-reverse choice, record that decision also as an ADR, so the next person finds it next to the code rather than buried in a design doc.

  • Lean (default): Context and Scope, Goals and Non-Goals, The Design, Alternatives Considered, Cross-Cutting Concerns. This is the Google-style “mini design doc,” enough for most changes worth a design doc at all (roughly one to three pages).
  • Full: breaks The Design into Static Structure, Runtime and Data Flow, Interfaces and Contracts, and Deployment and Operations, and adds Quality Attributes and Risks and Open Issues. Use it when the design is hard to reverse, crosses teams, carries security/privacy/regulatory weight, or has real non-functional targets.

Grow lean into full by adding sections; never reorder the shared ones. The scaling signal is the cost of being wrong, not the size of the system.

Quality rubric (self-grade before you circulate)

Section titled “Quality rubric (self-grade before you circulate)”
  • The scope has a boundary: the doc says what is in and, explicitly, what is out.
  • Goals are outcomes, not a build list, and there is at least one non-goal.
  • The design is concrete enough to disagree with, and a diagram carries the structure where prose would be worse.
  • Alternatives are real, including “do nothing” or the obvious approach, each described well enough that a reader could prefer it. No straw men.
  • Cross-cutting concerns are walked explicitly (auth, privacy, observability, failure, cost), not waved off as “standard.”
  • (Full) Quality attributes have numbers or scenarios, not adjectives.
  • (Full) Risks and open issues are named, and any hard-to-reverse decision is flagged for an ADR.
  • You know which of design-doc / RFC / ADR you are writing, and this is the right one.
  • Status is current, and there is a plan for the doc after shipping (archive, or mark last verified).
  • Every guidance comment is deleted; no placeholders remain.
  1. Conflating design doc, RFC, and ADR. Writing a “design doc” that is really an undecided proposal (an RFC), or burying a significant hard-to-reverse decision inside a design doc instead of recording it as an ADR where it can be found. Know which of the three you are writing.
  2. Big design up front. A complete, detailed design produced before any code and treated as fixed. It commits you before you have learned the most, and produces a design the implementation quietly abandons. Write “just enough” design; reach for the full variant only when the cost of being wrong earns it.
  3. The stale living doc. A design doc kept as the system’s description long after the code diverged. A half-correct document everyone trusts is worse than an obviously archived one. Archive it after shipping, or mark when it was last verified against reality.
  4. Writing to exhaustion. Using length and exhaustive detail as a substitute for clear thinking, so the design is approved because no reviewer could finish it. Detail the risky parts; compress the obvious ones.
  5. No alternatives. A design presented as the only option cannot show it was reasoned; reviewers supply the missing options adversarially.
  6. Wishes instead of targets. Quality attributes stated as adjectives (“fast,” “scalable”) with no number or scenario, so nothing in the design can be checked against them.

There is no develop-sdd skill in the product-on-purpose org today, so this bundle’s pairs_with is empty. The org has a skill for the decision record (develop-adr) but none for the design document. Until one exists, this template is filled by hand. Recorded as context in STATE.md.

sdd_template-lean.md · ~2,350 tokens

---
title: "{{title}}"
status: "{{status}}"
authors: ["{{authors}}"]
created: "{{date}}"
updated: "{{date}}"
doc_type: sdd
size: lean
source_template: sdd
source_template_version: 0.1.0
---
<!--
LEAN SOFTWARE DESIGN DOCUMENT. The smallest design doc that still gets a design thought through and
reviewed before it is expensive to change. Use it for a change that is more than a one-file edit but is
not a hard-to-reverse, multi-team, or safety-critical design. To grow it into a full design doc (see
sdd_template-full.md), ADD sections and subsections; never rename or reorder the ones below, because the
full variant is a strict superset of this one.
WHAT A DESIGN DOC IS, AND IS NOT
A design doc describes HOW you will build something, so people can review the design before the code
exists. It is not an RFC (which PROPOSES a decision and asks for feedback) and not an ADR (which RECORDS
a decision after it is made). If what you actually want is feedback on whether to do this at all, write
an RFC. If you are recording a single hard-to-reverse choice, write an ADR. See sdd_companion.md
section 8. The value of this document is the review it gets, not the document itself.
STATUS is a lifecycle, not a label. Move it as the design moves:
draft -> in-review -> approved -> implemented (and later, possibly, superseded)
Keep it current, and once the code ships, either archive this doc or mark when it was last checked
against the real system. A design doc everyone trusts and no one has verified is worse than none.
HOW TO FILL THIS IN
1. Read the comment under each heading: WHAT it wants, WHY it matters (with a pointer into
sdd_companion.md for the deep reasoning), guiding questions to ASK, a GOOD and a WEAK example, and
the TRAP to avoid.
2. Replace each {{placeholder}} with your content.
3. If a section does not apply, write "N/A" and one line of why, rather than deleting it.
4. Before you circulate it: self-grade against sdd_guide.md, then DELETE every HTML comment. They are
guidance, not content.
-->
# {{title}}
## Context and Scope
<!-- WHAT What exists today, what is changing, and the boundary of this design. Orient a reader who was
not in the meetings, then say plainly what is in scope and what is out.
WHY A design doc with a vague boundary invites reviewers to drag in every adjacent system, and the
review never converges. The scope line is the cheapest way to keep the discussion on the
thing you are actually designing. Deep dive: sdd_companion.md section 3 (Anatomy > Context and
Scope).
ASK What is the current state? What is changing and why now? Where does this design start and
stop? What nearby system is explicitly NOT part of this?
GOOD "Today, task-list filters and sorts live in URL query params and are lost on navigation; there
is no way to save or share a view. This design adds saved, shareable views. In scope: storing,
listing, and sharing a view within a project. Out of scope: cross-project views, and any change
to the filtering engine itself."
WEAK "We are building saved views." (no current state, no boundary, so the review has nothing to
anchor on)
TRAP A Context section that re-derives the whole product's history. Orient in a paragraph; do not
re-litigate the requirements the PRD already settled. -->
{{context_and_scope}}
## Goals and Non-Goals
<!-- WHAT Goals: what the design must achieve to succeed, ideally observable. Non-goals: what it is
deliberately NOT trying to do, to keep the design and its review bounded.
WHY Non-goals are the highest-value line of scope control in a design doc. Most runaway reviews are
reviewers arguing about something you never meant to solve; naming it a non-goal ends that
thread before it starts. Deep dive: sdd_companion.md section 3 (Anatomy > Goals and Non-Goals).
ASK What must be true for this design to have worked? What are we explicitly not solving here, and
why? What is a reasonable-but-separate concern a reviewer might raise?
GOOD Goal: "A user can save the current filter/sort as a named view and reuse it across sessions and
devices." Non-goal: "Real-time collaborative editing of a view. Views are saved and shared, not
co-edited live; that is a separate feature."
WEAK A goals list with no non-goals, so every reviewer expands the scope in a different direction.
TRAP Goals written as a feature list ("build the views table, build the API") rather than as
outcomes. State what must be TRUE, not what you will type. -->
{{goals_and_non_goals}}
## The Design
<!-- WHAT The heart of the document: how the system is actually structured to meet the goals. The core
idea, the main components, how they interact, and the data that flows between them. A diagram
is usually worth more than the paragraph it replaces. For a heavier design, the full variant
breaks this into structural, runtime, interface, and deployment views.
WHY This is the object of review. It has to be concrete enough for a colleague to disagree with,
and no more exhaustive than that: the job is to convey the design and expose the risky parts,
not to pre-write the code. Deep dive: sdd_companion.md section 3 (Anatomy > The Design).
ASK What are the components, and how do they depend on each other? What is the data model? What are
the key interfaces? What is the important runtime behavior (the main sequences, the failure
paths)?
GOOD "Add a `saved_view` table (id, name, owner_id, project_id, scope, config JSON, timestamps) in
the existing Postgres database. The task-service exposes CRUD endpoints under
/projects/{id}/views. The frontend adds a Views dropdown that captures the current filter/sort
into `config` on save. Shared views are visible to all project members; private views only to
the owner. [figure: component and data-flow sketch, illustrative]"
WEAK "Use a database to store views and an API to serve them." (names categories, not a design
anyone can review or build from)
TRAP Turning this into an exhaustive spec that no reviewer can finish. Detail the risky and novel
parts; compress the obvious ones. Approval by fatigue is not approval. -->
{{the_design}}
## Alternatives Considered
<!-- WHAT The other designs you genuinely weighed and why you are not proposing them, including "do
nothing" or "the obvious approach" where those are real options.
WHY Alternatives are the evidence the design is reasoned rather than merely preferred, and they are
how a reviewer surfaces the option you missed. Their absence invites the reader to supply them
adversarially. Deep dive: sdd_companion.md section 3 (Anatomy > Alternatives Considered); the
no-alternatives anti-pattern is in section 7.
ASK What else could meet the goals? What is the obvious approach you rejected, and why? What would a
skeptic build instead? Is "do nothing / keep it in the URL" acceptable, and why not?
GOOD "Store config in the URL and localStorage only (rejected: no sharing, lost across devices). A
generic user-preferences key-value blob (rejected: no project-scoping or sharing semantics, and
awkward to query). A separate views microservice (rejected: a new service and a data-sync
problem for what is fundamentally one table)."
WEAK One decoy option dismissed in half a sentence.
TRAP Straw men. Listing only options you would never pick makes the design look inevitable, which
reads as a sales pitch and costs the reviewer's trust. -->
{{alternatives_considered}}
## Cross-Cutting Concerns
<!-- WHAT The concerns that touch every part of the design rather than living in one component: security
and privacy, observability, error handling, data retention, cost, accessibility,
internationalization. For each, one honest line: how the design handles it, or that it does
not apply and why.
WHY This is where a design that looked clean reveals its real cost: the auth check, the privacy
surface, the observability that has to be designed in rather than bolted on. Deep dive:
sdd_companion.md section 3 (Anatomy > Cross-Cutting Concerns).
ASK What is the auth and privacy model? What do we log and measure, and are there quotas or cost
limits? How does it fail, and what happens then? Any accessibility or i18n impact?
GOOD "Auth: shared views are readable by any project member; write is owner-only; a membership check
gates every read of a shared view. Privacy: private views are never returned to non-owners.
Observability: emit view.created and view.applied events. Cost: cap 100 saved views per user to
bound storage."
WEAK "Standard security applies." (says nothing a reviewer can check)
TRAP Leaving the section blank because "nothing cross-cutting changed." Walk the list explicitly; a
one-line "N/A, auth is unchanged and inherited" is a real answer, silence is not. -->
{{cross_cutting_concerns}}

The reasoning, the history and every source, in the repository:

  • Companion - the long-form argument: why these sections, where the sources disagree, and what the bundle refuses to claim
  • History - what changed in this bundle, and when
  • Research log - every source consulted, with what each one actually supports
  • Catalog metadata - the machine-readable record this page is generated from

Catalog record: 11 sections across 1 format(s).