All articles

Documentation for Open Standards: RFCs, ADRs and Design Docs

Open standards survive on the quality of their written record. Here is how RFCs, ADRs, and design docs actually keep distributed teams aligned on decisions.

D

DocPilot Team

AI documentation research and engineering from Onzira Labs.

11 min read

What separates a living open standard from a dead PDF

A standard that lives in an obscure PDF on a forgotten IETF page is a standard that nobody implements. The standards that shape how software actually gets built — HTTP, OAuth 2.0, TLS 1.3, the various WebAssembly specs — share one trait: their proposals, debates, and final decisions are searchable, citable, and revisable. The document is the API.

For engineering teams building anything that crosses organizational boundaries — APIs, internal platforms, schema migrations, agent protocols — that same principle applies. RFCs, ADRs, and design docs are not bureaucratic overhead. They are the medium through which a standard gets negotiated, implemented, and maintained over years. Get them right, and your team has a durable source of truth. Get them wrong, and you spend the next decade answering the same questions on Slack.

This guide covers the three document types that show up most often when teams try to formalize decisions: RFCs for proposals under discussion, ADRs for decisions already made, and design docs for the technical shape of a system. Each has a different job, a different lifecycle, and a different failure mode.

RFCs: how proposals become standards

A Request for Comments (RFC) is, in its original IETF sense, a document open for community review before a protocol becomes standard. Most engineering teams have borrowed the term for their internal "propose before you build" workflow, and the discipline is the same: you write the proposal first, you circulate it, and you only start coding after a group of humans has had a chance to disagree.

When an RFC is the right tool

RFCs are heavy. You do not want one for every refactor. Reach for the RFC process when the change:

  • Touches a public API, schema, or contract that other teams depend on.
  • Changes a security boundary, billing model, or data retention policy.
  • Will require more than a single sprint to unwind if the wrong path is taken.
  • Affects multiple services or repos owned by different teams.

If the change is purely local and reversible, a PR description is enough. If it sets a precedent, write the RFC.

Anatomy of a useful RFC

The IETF format exists for a reason: every section has a job, and reviewers know where to push back. A practical engineering RFC can be shorter but should keep the same skeleton:

  1. Summary — one paragraph. State the decision being asked for in plain language. No narrative.
  2. Motivation — why now, what breaks if we do nothing, what triggered this proposal.
  3. Detailed design — the meat. APIs, schemas, state machines, sequence diagrams. Be specific enough that an implementer could build it without asking follow-up questions.
  4. Alternatives considered — this is the section most teams skip and most regret skipping. List the other paths you rejected and why.
  5. Drawbacks — every proposal has them. Naming them is the fastest way to get useful feedback.
  6. Open questions — what you genuinely don't know yet.
  7. Rollout and migration — how the change ships, what gets deprecated, what the kill switch looks like.

A real example from a payment platform might look like:

# RFC-042: Move idempotency keys to a per-merchant namespace

## Summary
Today, an idempotency key is global. Two merchants using the same key
collide and one gets a 409. We will scope idempotency keys to the
merchant (merchant_id + key) and keep a 30-day server-side dedupe window.

## Motivation
Three production incidents in Q3 came from key collisions between
sandbox and production traffic. The current behavior is silently
unsafe — the second request returns the first request's response.

## Detailed design
- POST /v1/charges accepts Idempotency-Key, scoped to merchant.
- Key + merchant_id hashes into a dedupe table with TTL 30d.
- On collision, return 409 with the original response body cached.
- Webhooks get a new `idempotency_version` field.

## Alternatives considered
- Per-API-key scoping: rejected, breaks our OAuth flow.
- Client-generated UUIDv7: rejected, requires SDK rollout we cannot
  ship in the window.

## Drawbacks
- 90 days of cached responses means higher Postgres pressure on the
  audit DB. Mitigation: partial index on (merchant_id, key_hash).

## Open questions
- Should we honor a key across currencies, or only matching currency?
- Do we surface collision metadata to the merchant dashboard?

## Rollout
- Week 1: feature flag at 1% of merchants.
- Week 4: 100%.
- Week 12: deprecate global scoping with 6 months notice.

That structure takes about two hours to write. The decision it records will save the team months of ambiguous Slack threads.

The RFC review process that actually works

The worst RFC processes are the ones where a single senior engineer is the de facto approver. That is not consensus, that is gatekeeping. A working process has:

  • A named shepherd who runs the review meeting, not decides the outcome.
  • A review window of at least five business days. Standards move at the speed of human attention.
  • Disagreement capture — comments get recorded on the document, not in a meeting that nobody can search.
  • A clear state machine: Draft → Review → Accepted / Rejected / Withdrawn. Stale Drafts that have not been updated in 60 days should auto-close.

Tools like Notion, GitHub Discussions, and lightweight ADR repos all support this. The medium matters less than the discipline of never merging an implementation PR before its RFC reaches Accepted.

ADRs: the decision log your team will thank itself for

Architecture Decision Records are shorter, sharper, and live forever. While an RFC is a proposal under debate, an ADR is a tombstone. It records a decision that has already been made: the context, the choice, and the consequences. Once written, ADRs are immutable. If you change your mind later, you write a new ADR that supersedes the old one.

Why immutability matters

Two years from now, somebody will ask: "Why are idempotency keys scoped per merchant? Why didn't we use UUIDs?" If the answer lives in a Slack channel that has been pruned, or in the memory of an engineer who has left, the team will either repeat the debate or quietly revert the decision. Both outcomes are expensive.

The whole point of an ADR is to be boring. It does not need to be persuasive. It needs to be honest.

The MADR format

The Markdown Any Decision Record format is the most widely used because it is the most forgiving. A MADR ADR typically has:

  • Title and status — Proposed, Accepted, Deprecated, Superseded by ADR-N.
  • Context and problem statement — what forces are at play, what constraints exist.
  • Decision drivers — the criteria the team actually weighed.
  • Considered options — three or four realistic alternatives.
  • Decision outcome — the chosen option, and the reasoning, in one paragraph.
  • Consequences — what becomes easier, what becomes harder, what we are now committed to.

A short, real example:

# ADR-018: Use Postgres logical replication for cross-region read replicas

## Status
Accepted. 2024-11-04.

## Context
We need sub-second read replicas in eu-west-1 replicating from
us-east-1. The dataset is 14 TB. Current p95 replication lag is
42 minutes using WAL shipping.

## Decision drivers
- Lag under 5 seconds at p99.
- Survives a 30-minute primary outage without data loss.
- Operable by the existing DBA team — no new toolchain.

## Considered options
- Logical replication via pgoutput.
- AWS DMS.
- Dual-writing to both regions from the application.

## Decision outcome
We will use Postgres native logical replication with a single
publication per logical database and a per-schema subscription
per region. We accept the operational cost of monitoring
subscription lag and replaying failed transactions manually.

## Consequences
- We must avoid DDL patterns that break replication (e.g. ALTER
  TYPE ADD VALUE without USING). Added to onboarding checklist.
- Cross-region failover is now a manual runbook step. PagerDuty
  playbook updated.

That ADR is roughly 200 lines. It will be read by new hires on day one, by auditors during compliance reviews, and by the team during the inevitable next replication incident.

Where ADRs should live

The most common mistake is storing ADRs in a wiki nobody visits. ADRs should live next to the code they describe. The convention docs/adr/NNNN-title.md in the relevant repository, indexed by a docs/adr/README.md, works for most teams. For cross-cutting standards — a shared API gateway, an event schema registry, an internal agent protocol — use a dedicated repository. The location should be discoverable in less than 30 seconds from any contributor's laptop.

ADR lifecycle rules

  • One ADR per decision. Compound ADRs are unreadable.
  • Never edit after Accepted, except to fix typos. Supersede instead.
  • Number sequentially. Gaps are fine. Reuse is not.
  • Mark superseded explicitly. A note at the top pointing to the replacement saves archaeologists.

Design docs: the bridge between decision and implementation

Design docs sit between ADRs and code. They are heavier than an ADR, lighter than an RFC, and more technical than both. Their job is to describe a system's internal shape: how the components talk, where the data lives, what the failure modes look like.

When a design doc is necessary

If a change involves more than two services and would take a new engineer more than a week to onboard into, write a design doc. If a change fits in a single ADR and is implemented in a single PR, do not.

Design docs are most useful when:

  • The system has long-lived state and the team rotates frequently.
  • Multiple teams will integrate with the system.
  • The cost of getting it wrong is high (data loss, security breach, billing error).
  • The implementation will span multiple quarters.

Structure of a maintainable design doc

Unlike an RFC, a design doc is not asking for permission. The decision has already been made in an ADR. The design doc is showing how that decision will be realized.

A solid design doc contains:

  1. Goals and non-goals — what is in scope, and what is explicitly out.
  2. Architecture overview — a single diagram, even ASCII, showing the major components and their boundaries.
  3. Data model — entities, schemas, ownership, retention. Include migration paths.
  4. API contracts — request/response shapes, error semantics, version policy.
  5. Failure modes — what happens when each dependency is unavailable, when the network partitions, when a clock skews.
  6. Observability — what metrics, logs, and traces look like. What an on-call engineer sees at 3am.
  7. Security and privacy review — authentication, authorization, data classification, threat model.
  8. Rollout plan — feature flags, migration windows, rollback strategy.

The difference between a good design doc and a great one is usually section 5. Anyone can describe the happy path. Few teams rigorously describe the unhappy paths, and those are the ones that page someone at 3am.

Keeping design docs alive

Design docs rot faster than RFCs and ADRs because they describe systems that change. A common pattern is to treat the design doc as the canonical reference until the system is shipped, then move ownership to the ADRs (one per major decision) and let the design doc become historical. The alternative — keeping the design doc evergreen — requires a discipline most teams cannot sustain.

Whichever you choose, document the choice. Future readers need to know whether the doc is current or a snapshot.

How RFCs, ADRs, and design docs relate

These three documents form a pipeline:

StageDocumentLifetimeOwner
ProposalRFCDays to weeksProposer
DecisionADRForever (immutable)Architecture group
RealizationDesign docUntil ship, then historicalImplementing team

A healthy engineering org has all three and knows which one to reach for at which stage. A team using only RFCs is slow to ship. A team using only ADRs loses the proposal context. A team using only design docs makes decisions by committee inside the doc itself.

Tooling and workflow

The tooling is less important than the discipline, but a few patterns help:

  • Version control everything. RFCs, ADRs, and design docs all live in git. PRs become the review surface.
  • Lint the structure. A simple CI check that asserts every ADR has Status, Context, Decision, and Consequences catches drift early.
  • Link them. RFCs reference the ADR that supersedes them. ADRs reference the design doc that implements them. Design docs reference the ADRs they realize.
  • Auto-generate indexes. A script that reads frontmatter and builds an index.md keeps the table of contents current without manual work.
  • Make them searchable. Static site generators like MkDocs, Docusaurus, or Antora turn a folder of markdown into a browsable standards site in minutes.

Common failure modes

Teams that try to formalize documentation usually fail in one of three ways:

  • Document theatre. Writing RFCs and ADRs that nobody reads because the actual decisions happen in DMs. The cure is to require RFC/ADR acceptance as a precondition for implementation PRs.
  • Decision hoarding. A single person holds the ADR repo write access and bottlenecks the team. The cure is a lightweight review rotation.
  • Stale docs. ADRs from 2021 still claim a system runs on a stack it has not run on for two years. The cure is a quarterly review where every Accepted ADR gets a "still accurate?" check, and a new ADR if not.

Building a standards culture

The hardest part is not writing the documents. It is creating the habit. A team that defaults to writing before coding will outperform a team of equal talent that does not, on any project that crosses boundaries or outlasts a quarter.

Start small. Pick the next cross-team change on your roadmap and write the RFC for it. Time the review. Measure how many avoidable Slack threads you skipped in the following month. Then do it again.

Within a year, your team will have a standards site that reads like a textbook: a clean history of every significant decision, the proposals that preceded them, and the systems that implemented them. New hires onboard in days instead of months. Auditors find answers without scheduling a meeting. New contributors propose changes in the same format everyone else uses.

That is what a living standard looks like. Not the PDF on the forgotten IETF page — the searchable, citable, revisable record that the next engineer finds when they need to know why.


DocPilot helps teams keep RFCs, ADRs, and design docs in sync with the code they describe. Connect a GitHub repository and DocPilot turns your existing decisions, schemas, and architecture into a browsable standards site, indexed and always current. It is the easiest way to stop letting your documentation drift from the system it documents. Try the free tier at docpilot.io and ship your next standard with the records already in place.

Ship documentation that keeps up

Try DocPilot on your repository — free for one repo.

Connect your first repository in about a minute. No credit card. DocPilot writes and maintains the documentation from your code.

Read next