Skip to content

Design documents, RFCs and ADRs

Write one when it earns its place.

Updated View as MarkdownView on Github

Most work needs none of these. Write one only when it is useful.

Which one to write

Situation Write Where
A routine change within established conventions, such as a bugfix, a refactor, or a feature that follows existing patterns. A pull request only. Code review is the gate. -
An architecturally significant decision, even if nobody disputes it. Also any decision that is hard or costly to reverse, including an agreed exception to these standards. An ADR to record it. The owning repository’s docs/adr/. With no owning repository, under RFCs and ADRs in Confluence.
A different, contested, or cross-cutting change that needs agreement before anyone builds it. An RFC first to discuss it, then an ADR if it sets a pattern others must follow. The owning repository’s docs/rfc/. With no owning repository, or when it reaches beyond one team, under RFCs and ADRs in Confluence.
Work that takes more than a sprint or one engineer, changes the data model or a contract, adds an integration, involves money movement or regulatory reporting, or needs a staged rollout. A design document before implementation. The owning team or squad’s section of the DSE Confluence space.

A design document is an implementation plan. It does not replace an ADR, which records a decision.

The ADR bar is lower than the RFC bar. An ADR needs a significant decision. An RFC needs a contested one. Most ADRs record a clear choice that nobody disputes, so most ADRs have no RFC. Never write an RFC only to justify an ADR.

Where they live

Prefer the owning repository. A decision kept beside the code stays in Git history, gets reviewed in the same pull request, and is available to people and agents working in that repository.

  • In the owning repository, keep RFCs under docs/rfc/ and ADRs under docs/adr/. Each folder must have a template. This is the default for a decision that one repository enforces.
  • Under RFCs and ADRs in Confluence, keep a decision that has no owning repository, or a proposal that reaches beyond one team or would change these standards. Create the page as a child of RFCs and ADRs, using the templates below.

Keep design documents in the owning team or squad’s section of the DSE space. Each team page says where. Link every document from the ticket and the pull request.

What “architecturally significant” means

Write an ADR when a decision affects one or more of:

  • Structure. How code, packages, or layers are organized.
  • Cross-cutting non-functional requirements. Accessibility, performance, security, internationalization, theming, or operability.
  • Dependencies. Adopting or dropping a library, or adding an integration boundary.
  • Public interfaces. APIs, contracts, token names, or anything else consumers rely on.
  • Construction techniques and conventions. A pattern that other code is expected to follow.

Also write one when the decision is hard to reverse. If a future contributor would ask “why is it done this way?”, the answer belongs in an ADR.

“Architecturally significant” is a judgement call, and people will disagree at the margin. Code review and the relevant Engineering Navigator settle it, not an exhaustive checklist.

Stack-specific triggers

Watch for these in our stack:

  • A Scala effect-system or core library choice.
  • A JDK version move (for example, 17 to 21).
  • A GKE, Terraform, or Atlantis module pattern that other services will copy.
  • A cross-service contract change, including any change to tradovate/master-scheme.
  • An agreed exception to these standards.

Repositories add their own triggers in docs/adr/README.md.

What an ADR contains

An ADR records one decision. Keep it short. The ADR template follows Michael Nygard’s original format (Context, Decision, Status, Consequences) and adds Alternatives considered.

Every ADR must name its trade-offs. If you cannot name a single downside, you have not questioned the decision hard enough.

When an RFC produced the decision, link the RFC from the ADR and summarize it. Do not copy its alternatives analysis. The RFC is the record of the discussion, and the ADR is the record of the decision.

ADR status lifecycle

An accepted ADR is a permanent record. Never edit an accepted decision. To change it, write a new numbered ADR that supersedes it, and link the two ADRs both ways.

Status Meaning
proposed Under discussion; not yet binding.
accepted The current decision. Immutable.
superseded Replaced by a later ADR. Set superseded-by to its number.
deprecated No longer applies, with no direct replacement.
stateDiagram-v2
direction LR
[*] --> proposed
proposed --> accepted
accepted --> superseded: a new ADR replaces it
accepted --> deprecated: no replacement
superseded --> [*]
deprecated --> [*]

RFC status lifecycle

Status Meaning
draft The author is still writing it.
in-review Open for comment until its decision deadline.
accepted The decider approved the proposal.
rejected The decider turned it down. The status quo stands.
implemented The accepted proposal has shipped.
superseded A later RFC replaced it.
stateDiagram-v2
direction LR
state "in-review" as in_review
[*] --> draft
draft --> in_review
in_review --> accepted
in_review --> rejected
accepted --> implemented
accepted --> superseded: a later RFC replaces it
implemented --> superseded: a later RFC replaces it
rejected --> [*]
implemented --> [*]
superseded --> [*]

Require a real alternative

Every RFC and design document must consider at least one real alternative. One alternative must be do nothing / status quo. Explain the cost of the status quo and why it is not enough. A decision without an alternative is not a decision. It is only a preference.

An RFC also needs a Risks, trade-offs, and what could break section. Send back an RFC that lacks either section.

Time-box with a named decider

Give every RFC a clear decision deadline and one named decider. An RFC left open becomes the default decision, so “a decision by default” must go to a real owner, not happen because time ran out. If the deadline passes without a decision, keep the status quo.

Numbering and index

Number decisions in order and use four digits: ADR-0001, RFC-0001. Keep an index at docs/adr/README.md so people can find decisions and cite them consistently. See adr.github.io for numbering rules and tools.

Templates

Copy a template, then fill it in. Its headings are a checklist.

The ADR and RFC templates below are the only templates. Copy them into the owning repository as docs/adr/template.md and docs/rfc/template.md. For an RFC or ADR that lives in Confluence, paste the same Markdown into a new page under RFCs and ADRs. For design and status documents kept beside code, NT-NinjaTrader/ntc-core uses a per-project _TEMPLATE.md with a status field.

ADR template

Name the file NNNN-short-title.md, using the next four-digit number.

---
status: proposed # proposed | accepted | superseded | deprecated
date: YYYY-MM-DD # the date of the decision, or of the last status change
supersedes: # OPTIONAL: the ADR number this one replaces, e.g. 0003
superseded-by: # OPTIONAL: the ADR number that replaced this one
rfc: # OPTIONAL: a link to the RFC that produced this decision
decider: # the one person who owns the call
---

# ADR-NNNN: The decision, stated as a short outcome

## Context

Why this decision is needed now, and the forces at play. Two to four sentences. Describe the problem neutrally.

## Decision

What we will do, in one or two sentences.

## Alternatives considered

1. Do nothing or keep the status quo. The cost, and why it is not enough.
2. Option B. Its trade-offs.

If an RFC produced this decision, summarize it and link the RFC instead.

## Consequences

What becomes easier, what becomes harder, and what we accept. Name at least one downside.

RFC template

Name the file NNNN-short-title.md, using the next four-digit number.

---
status: draft # draft | in-review | accepted | rejected | implemented | superseded
date: YYYY-MM-DD # the date of the last status change
author: # your GitHub handle
decider: # the one person who makes the call
deadline: YYYY-MM-DD # if undecided by then, the status quo stands
---

# RFC-NNNN: Short title

## Problem / Motivation

What problem this solves, why now, and who feels it. Leave the solution out of this section.

## Proposal

The approach, in enough detail to evaluate.

## Scope and boundaries

What is in scope, and what is not.

## Alternatives considered

1. Do nothing or keep the status quo. The cost, and why it is not enough.
2. Option B. Its trade-offs, and why it was not chosen.

## Risks, trade-offs, and what could break

Failure modes, migration hazards, and effects on existing consumers.

## Rollout / migration

How this ships, and how existing code moves to it.

## Open questions

What the decider still needs to settle.

Superseded templates

These older Confluence copies are superseded by the templates above. Do not start new documents from them:

Exceptions record an ADR

An agreed exception to these standards is hard or costly to reverse, so it needs an ADR. Add the ADR link to the exception itself. See exceptions for the process, and architecture for the standards these decisions implement.

Keeping documents current

Mark a design document or ADR as superseded instead of deleting it when the design changes in a major way. The record of why a past decision was made remains useful after the decision changes.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close