Skip to content

Design Doc Template (Technical Design Document) in Markdown

A design doc explains how engineering will build something before it is built: the problem, goals and non-goals, the proposed design, the alternatives considered, and the rollout. This Markdown design doc template has a section for each. Copy it, download the .md file, or read the filled example below.

Free to use, no attribution required (CC0).

Updated

Design doc: {{Title of the change}}

Status {{Draft / In review / Approved / Implemented / Superseded}}
Author(s) {{Name}}
Reviewers {{Name, Name}}
Approver {{Name}}
Related PRD: {{link}} · Ticket: {{link}}
Created {{YYYY-MM-DD}}
Decision needed by {{YYYY-MM-DD}}
Last updated {{YYYY-MM-DD}}

TL;DR

Three to five sentences: the problem, the proposed approach, the main trade-off, and what you need from reviewers.

{{...}}

Context and problem

What exists today, what is wrong or missing, and why now. Link the PRD for the "what" and "why"; this document is the "how".

{{...}}

Goals and non-goals

Goals

  • {{Goal 1, tied to a PRD requirement ID}}
  • {{Goal 2}}

Non-goals (deliberately out of scope for this design)

  • {{Not solving 1, and why}}
  • {{Not solving 2, and why}}

Requirements this design must meet

Trace each requirement to the section that satisfies it. A reviewer should be able to find the gap.

Requirement Source Where it is addressed
{{R1: ...}} {{PRD link}} {{Section name}}
{{Constraint: latency, cost, compliance}} {{...}} {{...}}

Proposed design

Overview

One paragraph and one diagram. Someone new to the system should understand the shape of the solution from this alone.

flowchart LR
    A[Client] --> B[{{Service or component}}]
    B --> C[({{Data store}})]
    B --> D[{{External dependency}}]
Components and responsibilities
Component Responsibility Owner
{{...}} {{...}} {{Team}}
Data model

Tables, fields, types, indexes, retention. Show the schema, not a description of it.

{{schema, types or example record}}
Interfaces and APIs

Endpoints, events or function signatures, with a request and a response. Mark what is new and what changes.

{{request / response example}}
Key flows
  1. {{The main happy path, step by step}}
  2. {{The main failure path and how it recovers}}

Alternatives considered

Give each real alternative a fair hearing, including "do nothing". The reader should see why this one won.

Option How it works Pros Cons Verdict
Proposed: {{...}} {{...}} {{...}} {{...}} Chosen because {{...}}
{{Alternative A}} {{...}} {{...}} {{...}} Rejected because {{...}}
Do nothing Keep the current behaviour {{...}} {{...}} {{...}}

Cross-cutting concerns

  • Security and privacy: {{data classification, access control, abuse cases}}
  • Performance and scale: {{expected load, limits, how you will measure}}
  • Reliability: {{failure modes, retries, timeouts, what happens when a dependency is down}}
  • Observability: {{logs, metrics, alerts, dashboards}}
  • Cost: {{estimate and what drives it}}

Migration and rollout

  • Migration: {{How existing data and clients move over; is it reversible?}}
  • Rollout: {{Flag, percentage, order of environments}}
  • Rollback: {{The exact trigger and the steps}}

Testing plan

  • Unit and integration: {{what is covered}}
  • Load or failure tests: {{scenarios}}
  • Acceptance: {{how the PRD acceptance criteria are checked}}

Risks and open questions

Item Type Owner Resolve by
{{...}} Risk / Question {{Name}} {{YYYY-MM-DD}}

Decision log

Date Decision Reason Decided by
{{YYYY-MM-DD}} {{...}} {{...}} {{Name}}

Review checklist

  • The problem and the non-goals are stated before the solution
  • Every requirement maps to a section
  • At least one real alternative is compared, with a reason for the verdict
  • Failure modes, rollback and monitoring are described
  • Open questions have an owner and a date

How to use it

  1. Step 1: Write the context, goals and non-goals first. Link the PRD for the why, then state what this design solves and what it deliberately does not. Fill in the TL;DR last.

  2. Step 2: Draw the design, then compare it with real alternatives. One diagram, the components, the data model and the interfaces; then a table with at least one alternative and "do nothing", each with a reason for the verdict.

  3. Step 3: Send it for review with a decision date. Name reviewers and an approver, collect questions in "Risks and open questions", and record every decision in the log so the reasoning outlives the thread.

What goes into a design doc

Context before the solution. A reviewer who does not know the system should understand the problem from the first section. Link the PRD for the product side and keep this document to the technical how.

Non-goals keep the review short. Without them a design doc grows to cover everything adjacent. Naming what you will not solve gives reviewers a boundary to check and a place to send scope requests.

Alternatives are the evidence. A design with no alternative looks like a decision made before the document was written. A table with the same columns for every option (how it works, pros, cons, verdict) lets a reviewer disagree with a specific row.

Failure, rollout and rollback are part of the design. Say what happens when a dependency is down, how the change reaches users, and how to undo it. These are the questions that reviews spend most of their time on anyway.

A filled-in design doc example

The example is the technical companion of the PRD example on the PRD template page: a fictional "Quiet hours scheduler" for a fictional product. The people, the system and the decisions are invented. Notice how each requirement points to the section that satisfies it, and how the alternatives table gives every option the same columns.

Example design doc (fictional product), Markdown
# Design doc: Quiet hours scheduler (EXAMPLE, fictional product "Relay")

| | |
|---|---|
| **Status** | In review |
| **Author(s)** | Mateo Silva |
| **Reviewers** | Priya Raman, Lee Chen |
| **Approver** | Engineering manager, Notifications |
| **Related** | PRD: Quiet hours · Ticket: RELAY-1842 |
| **Created** | 2026-11-09 |
| **Decision needed by** | 2026-11-23 |
| **Last updated** | 2026-11-09 |

## TL;DR

We will decide at send time whether to hold a notification, using a per-user schedule stored in the existing settings table and evaluated by the notification service. Held messages go to a digest queue that a worker flushes when quiet hours end. We chose evaluation at send time over a scheduled job because it needs no clock-driven sweeps. Reviewers: please check the digest queue sizing and the urgent-message path.

## Context and problem

Today the notification service sends every push as soon as a message arrives. The PRD asks for quiet hours (R1 to R5). The service has no concept of the recipient's local time, and the only way to stop pushes is to mute a channel.

## Goals and non-goals

**Goals**

- Hold non-urgent pushes during a user's quiet hours (R2) and let urgent ones through (R3).
- Add no more than 50 ms at P95 to delivery (non-functional requirement).

**Non-goals**

- Admin-set team schedules.
- Email notifications.

## Requirements this design must meet

| Requirement | Source | Where it is addressed |
|---|---|---|
| R1 Schedule per weekday | PRD | Data model |
| R2 Hold non-urgent | PRD | Key flows, step 2 |
| R3 Urgent breaks through | PRD | Key flows, step 1 |
| R4 Digest after quiet hours | PRD | Components: digest worker |
| Add at most 50 ms at P95 | PRD | Cross-cutting: performance |

## Proposed design

### Overview

The notification service reads the recipient's schedule from a cache, decides hold or send, and writes held messages to a digest queue. A worker flushes the queue when the schedule says quiet hours have ended.

```mermaid
flowchart LR
    A[Message service] --> B[Notification service]
    B -->|send| C[Push gateway]
    B -->|hold| D[(Digest queue)]
    E[Digest worker] --> D
    E --> C
    B --> F[(Schedule cache)]
```

### Components and responsibilities

| Component | Responsibility | Owner |
|---|---|---|
| Notification service | Decide hold or send for each message | Notifications |
| Schedule cache | Hold each user's schedule and time zone, 5 minute TTL | Notifications |
| Digest queue | Store held messages with channel and timestamp | Notifications |
| Digest worker | At the end of quiet hours, send one digest per user | Notifications |

### Data model

```text
quiet_hours(user_id, weekday 0-6, start_minute 0-1439, end_minute 0-1439, time_zone)
digest_item(user_id, channel_id, message_id, held_at)   -- deleted after the digest is sent
```

### Interfaces and APIs

```text
PUT /v1/me/quiet-hours
{"time_zone": "Europe/Lisbon", "days": [{"weekday": 1, "start": "19:00", "end": "08:00"}]}
-> 200 {"saved": true}
```

### Key flows

1. An urgent message skips the schedule check and is sent at once.
2. A normal message: read the schedule from the cache; if now falls inside quiet hours, write a digest item and stop; otherwise send.
3. If the cache is down, send the message (fail open): a late-night push is better than a lost message.

## Alternatives considered

| Option | How it works | Pros | Cons | Verdict |
|---|---|---|---|---|
| **Proposed:** decide at send time | Check the schedule when the message arrives | No clock-driven jobs; simple | Adds a cache read to every send | Chosen because the cost is small and measurable |
| Scheduled sweep | A job marks users as quiet or active every minute | Cheap per message | A minute of lag; load spikes on the hour | Rejected because of lag at the start and end |
| Client-side hold | The app suppresses pushes locally | No server work | Does not work if the app is closed | Rejected because it fails the main case |
| Do nothing | Keep muting channels | No work | Problem stays | Rejected, see the PRD |

## Cross-cutting concerns

- **Security and privacy:** other users can read only the end time of quiet hours, never the schedule.
- **Performance and scale:** one cache read per message; target 50 ms at P95, measured in the rollout.
- **Reliability:** the service fails open if the cache or the schedule store is unavailable.
- **Observability:** metrics message_held, digest_sent, schedule_cache_miss; alert if the digest queue grows for 10 minutes.
- **Cost:** a small cache and one more queue; no new paid service.

## Migration and rollout

- **Migration:** a new table, no change to existing data. Users without a schedule are never held.
- **Rollout:** behind a flag, internal for a week, then 10%, then everyone.
- **Rollback:** switch the flag off; held items are sent as a digest at the next flush.

## Testing plan

- **Unit and integration:** the hold decision across time zones and daylight saving changes.
- **Load or failure tests:** cache down, digest queue at 10x expected size.
- **Acceptance:** each PRD acceptance criterion is a test case.

## Risks and open questions

| Item | Type | Owner | Resolve by |
|---|---|---|---|
| Daylight saving changes during quiet hours | Risk | Mateo | 2026-11-16 |
| Digest limit per user | Question | Priya | 2026-11-16 |

## Decision log

| Date | Decision | Reason | Decided by |
|---|---|---|---|
| 2026-11-09 | Fail open when the cache is down | Missing a message is worse than a late push | Mateo, engineering manager |

Design doc, PRD or decision record: which one do I write?

Short answer: the PRD says what to build and why, the design doc says how, and a decision record keeps one decision and its reason. They link to each other. The usage of these names varies between companies, so check what your team calls them.

CriterionPRDDesign docDecision record
AnswersWhat and why, how success is measuredHow it will be builtWhich option we chose, and why
OwnerProductEngineeringWhoever made the decision
ReaderEveryone who builds or ships itEngineers and reviewersFuture maintainers
LengthOne to several pagesTwo to ten pagesA page or less
Links toDesign doc and ticketsPRD requirement IDsThe design doc it came from

Frequently asked questions

What is a design doc?

A document an engineer writes before building something non-trivial, to explain the problem, the proposed solution and the alternatives, and to collect feedback while changes are cheap. It is also called a technical design document or technical spec.

What is the difference between a design doc and a PRD?

A PRD says what to build and why and how success is measured. A design doc says how it will be built. The design doc cites the PRD's requirement IDs, and the table above compares them with a decision record.

How long should a design doc be?

Long enough that a reviewer can find the gap, short enough that they read it. For a typical feature that is two to ten pages. If the alternatives and cross-cutting sections are empty, the change may not need a design doc at all.

When do I need a design doc?

When the change is hard to reverse, touches more than one service or team, adds a dependency, or has several plausible designs. Many teams write the rule down in their engineering handbook.

Who should review a design doc?

The owners of the systems it touches, one engineer who did not work on it, and anyone who runs it in production. Name an approver in the header and a decision date, so the review ends.

Where should a design doc live?

Next to the code or in the team wiki, as a Markdown page with version history, so a later reader can see what was decided and when. A link that always shows the latest version prevents people from reviewing an old copy.

Keep design docs in Git, next to the code and the PRD

In Folio a design doc is a Markdown page in a Git repository. Mermaid diagrams, tables and checklists render in the editor, several people can edit live, and the page history shows who changed a decision. Link the design doc to the PRD and to the runbook with ordinary links, and the backlinks show what points at it.