A resource for design system teams
One-pageRFCtemplate.
For changes other teams will have to live with.
One page. Five sections. A dated decision.
Filter at the proposal, not at the revert.
Someone ships a "quick improvement" without review, three teams copy it, and by the time anyone notices, reverting costs more than fixing forward. The essay behind this guide saw those RFC-less changes create three-week cleanup cycles at SAP Build Apps. Its fix is a gate that costs one page: problem, proposal, impact, alternatives, decision.
Rust, React and Carbon ask for an RFC only for "substantial" changes [S01, S02, S03]. Architecture decision records run one or two pages [S05]; Google's mini design docs one to three [S06]. This guide takes the shortest form and makes the limit and the decision trail checkable.
The kit's linter needs no packages. It reports no errors on RFC-0042, which adds LinkButton to the Acme UI example system, and 11 on RFC-0043, a well-meant proposal that skips every step. RFC-0042 ships on the October 2026 train from Field Guide 08 and registers deprecation DEP-0007 from Field Guide 09.
Practical guidance, not a standard. The five sections follow the essay's template; borrowed ideas are credited where used. Acme UI, its people and its issues are a worked example, not a real system. Prepared with AI assistance and edited by hand.
Start here
Suggest, propose, implement.
The essay's contribution model has three tiers. Most ideas stay at the first, and that is the system working. An RFC is the second tier: the point where an idea has to justify its cost to everyone who will maintain it.
| The change | Route | Why |
|---|---|---|
| Bug fix, docs fix, refactor, test | Pull request | Shape changes, meaning does not [S01]. |
| New value in an existing set | Issue; a maintainer decides | Cheap to reverse, no new concept. |
| New component, public prop or token category | RFC | API every consumer inherits [S02]. |
| Deprecation or breaking change | RFC plus a DEP entry (Guide 09) | Other teams pay for the migration. |
| Process change, including this template | RFC | Everyone plans around it. |
Put your version in CONTRIBUTING.md and link to it when you redirect a pull request or an issue. Owners: maintainers run Sections 01 and 04 with the deciders; authors own Sections 02 and 03.
Writing your first RFC
Copy rfc-template.md, read Section 02, run lint-rfc.mjs until it is clean. No evidence for Problem? Open an issue instead.
Reviewing one
Section 04 and P24. The linter checks the shape; you check the evidence, the alternatives and the costs.
Running the process
Sections 01 and 04: what needs an RFC, who decides, how long comments stay open, which train ships it.
Adding it to CI
Appendix B. Any error exits non-zero, so an RFC cannot merge in a state the process does not allow.
| Label | Meaning |
|---|---|
TEMPLATE | A section or prompt in rfc-template.md. |
LINT | Enforced by lint-rfc.mjs; the evidence line names the rule code. |
SCHEMA | A field enforced by rfc-frontmatter.schema.json (rule F1). |
PROCESS | A step in the review that leaves a record: a link, a date, a status. |
PRACTICE | A working method with a review signal rather than a gate. |
Section 01
Decide what needs an RFC.
An RFC process fails in two directions: everything needs one and nobody contributes, or nothing does and the system fragments. Draw the line once, in writing.
Write down which changes need an RFC
New public API, removal of shipped API, new conventions and process changes. Everything else is an issue or a pull request. Rust, React and Carbon all define the gate as "substantial" and list what does not count.
Evidence: The route table is in CONTRIBUTING.md, and redirects in review link to it.
S01, S02, S03.
Start at Suggest: an issue with no template
Tier one costs nothing: describe the need. Maintainers answer with "write an RFC", "we will do it" or "not in the system, and why". The essay estimates that about 20% of suggestions reach the RFC stage.
Evidence: Every RFC's Problem section links the issue it grew from (rule E1 requires a link).
The essay's three-tier model; Nathan Curtis separates small contributions from large ones that go through propose, design, code, docs and release. S08.
The RFC comes before the code
Tier three is implementation, after approval. A pull request that implements an undecided RFC waits, however good it is.
Evidence: The implementation pull request links an RFC whose status is
accepted.Essay; Rust implements only "active" RFCs. S01.
Withdrawal is a result, not a failure
Many requests withdraw themselves once the author works through the template. Record that with status
withdrawn, so the next person with the same idea finds the reasoning.Evidence: Withdrawn RFCs stay in the index with their Decision section.
Essay; Oxide keeps abandoned RFDs as a state. S04.
Most ideas should stop at Suggest. That is the filter working.
Section 02
Fit the argument on one page.
One page forces clarity; longer documents hide weak thinking in verbosity. That is the essay's claim, and the linter turns it into a number.
One page, measured
At most 400 words and 45 non-blank lines, template comments excluded. Rendered at 11 pt on A4, 400 words under the five headings fill 0.89 of a page; the 341-word worked example, with a code block and lists, fills 0.90. Put detail behind links.
Evidence: Rules L1 and L2. RFC-0043 fails L1 at 578 words.
Essay: "One page forces clarity." ADRs: one or two pages; mini design docs: one to three. S05, S06.
Five sections, in order, and nothing else
Problem, Proposal, Impact, Alternatives considered, Decision. One optional section, Open questions. A "Background" section is how an RFC grows a second page.
Evidence: Rule S1 (missing, unknown or out of order) and S2 (empty after comments are removed).
The essay's template. Rust and React split the same content into nine sections. S02, S11.
Problem carries evidence
What goes wrong, for whom, how often, with links: bug reports, research notes, a usage count. "Stakeholders feel it is flat" is an opinion. No evidence, no RFC: open an issue instead.
Evidence: Rule E1: the Problem section must contain a link or an issue reference.
Essay: "Include evidence." Rust: RFCs without convincing motivation are poorly received. S01.
Proposal shows the code a consumer will write
The API surface, one snippet, and what is out of scope. A reviewer who can see
<LinkButton href>can argue with it; a paragraph about "navigation affordances" cannot be reviewed.Evidence: A fenced code block in Proposal; reviewers ask for one when it is missing.
React's template leads with a basic example; Google design docs state goals and non-goals. S02, S06.
Impact answers four questions
Teams affected, with the count you measured. Breaking change: yes or no. Migration: yes or no, and how. Costs and drawbacks: what gets worse.
Evidence: Rule I1: both yes/no lines present, and "Breaking change" agrees with
breakingin the frontmatter.Essay's Impact section; Rust and React ask for drawbacks explicitly. S01, S11.
Alternatives include doing nothing
At least two options, one of them "Do nothing" with its consequence. If doing nothing is fine, the RFC is not needed.
Evidence: Rule A1.
Rust "Rationale and alternatives"; MADR "Considered Options"; Google "Alternatives considered". S06, S07, S11.
The decision is a sentence with a date
Accepted, Accepted with changes (list them), Deferred until a date, or Rejected, each with the reason. Until then the section says "Pending".
Evidence: Rule D1: the section's first word matches
status, and a decided RFC carries adecideddate.Essay's four outcomes; Nygard records status and consequences. S05.
No template residue
Placeholder text left in (
TBD,Your Name,RFC-0000) means nobody read the page before asking others to.Evidence: Rule P1, with the placeholder list in
rfc-policy.json.Kit rule.
If it does not fit on a page, it is two decisions or none.
Section 03
Make the status machine-readable.
The frontmatter is what an index, a release calendar, a deprecation registry and an agent read. Keep it small and closed.
Frontmatter validates against the schema
id,title,status,authors,deciders,created,discussion,commentsClose,packages,breaking,deprecations;decided,targetandsupersededBywhen they apply. Unknown fields fail.Evidence: Rule F1 against
rfc-frontmatter.schema.json(JSON Schema 2020-12).Oxide RFDs carry authors, state, discussion link and labels; MADR carries status, date and decision-makers. S04, S07.
Status comes from a fixed set
draft,discussion,accepted,deferred,rejected,withdrawn,superseded. Anything else is a status nobody can filter on.Evidence: The
statusenum.Maps to Rust's active, postponed and closed, Oxide's six states and Nygard's proposed, accepted, deprecated and superseded. S01, S04, S05.
Name the deciders
A person or a named group decides. Not "everyone who commented", and not the author. Comments inform; deciders decide.
Evidence:
decidersis required, and the Decision section names them.MADR decision-makers, consulted and informed. S07.
Never rewrite an accepted RFC; supersede it
A reversed decision stays in the record, marked superseded, with a link to the RFC that replaced it. It is still relevant that it was the decision.
Evidence: Rule D1 requires
supersededBywhen status issuperseded.Nygard: keep the old record and mark it superseded. S05.
Link the train and the deprecations, do not copy them
target: 2026.10names the release train (Field Guide 08).deprecations: [DEP-0007]names registry entries (Field Guide 09) and each id must appear in the text.Evidence: Schema patterns
YYYY.MMandDEP-NNNN; rule X1. The kit's tests resolve RFC-0042's train and DEP id against the other two kits.Kit rules.
Status is data. Keep it where the tools can read it.
Section 04
Review in the open, decide on a date.
A comment period is a promise to the people who are not in the room: you will hear about this, and you will have time to object.
Comments stay open for five working days
Count working days, so a proposal opened on Friday does not close on Monday. Five working days sits between React's and Carbon's three calendar days and Rust's ten calendar days, which Rust chose to guarantee five business days.
Evidence: Rule C1:
commentsCloseis at least five working days aftercreated.S01, S02, S03; Oxide asks for three to five business days. S04.
Decide after the period closes, and date it
A decision taken before comments close tells the organisation that commenting is theatre.
Evidence: Rule D1:
decidedis on or aftercommentsClose.Kit rule.
Accepted means scheduled
An accepted RFC names the release train it ships in, and is accepted before that train's decision cut-off: five working days before the code freeze. RFC-0042 was decided on 10 September 2026; the cut-off for train 2026.10 is 29 September.
Evidence: Rule D2; the cut-off dates come from Field Guide 08's
release-calendar.mjs.Kit rule.
Changes during implementation amend the RFC
The implementation matches the approved proposal. When it cannot, update the RFC in the same pull request and tell the deciders. RFC-0042 was accepted with one change, and the Decision section records it.
Evidence: The RFC diff appears in the implementation pull request.
Essay: implementation matches the approved spec exactly.
Keep every RFC in one index
One folder in the design system repository, one file per RFC, the frontmatter as the index. Rejected, withdrawn and superseded RFCs stay: they answer the question before it is asked again.
Evidence:
rfcs/lists every id; a status filter over the frontmatter produces the index page.Rust, React and Oxide keep RFCs in a repository. S01, S02, S04.
The linter runs on every RFC pull request
A non-zero exit blocks the merge. Run it locally before asking for review; the output says what to fix, by rule code.
Evidence:
node scripts/lint-rfc.mjs rfcs/*.mdin CI and in a pre-commit hook.Kit script.
Reviewers check what the linter cannot
Whether the evidence supports the problem, whether the alternatives were really considered and whether the costs are honest. RFCs that are disingenuous about drawbacks or alternatives tend to be poorly received, and should be.
Evidence: A reviewer comment on each of the three before the decision.
S01; Vaidehi Joshi on RFCs losing value when nobody engages. S09.
The linter checks the shape. People check the argument.
Appendix A
The template.
rfc-template.md from the kit. The linter ignores the HTML comment prompts when it counts words.
rfc-template.md (body; the frontmatter fields are in the table below)
# RFC-0000: Short statement of the change
## Problem
<!-- What goes wrong today, for whom, how often. Link the evidence. -->
## Proposal
<!-- The API surface, the code a consumer will write, what is out of scope. -->
## Impact
- Teams affected: <!-- with the usage count you measured -->
- Breaking change: no
- Migration: no
- Costs and drawbacks:
## Alternatives considered
1. Do nothing: <!-- what happens if this is rejected -->
2.
## Decision
Pending.Prompts shortened for print. The untouched template fails the linter on purpose (S2, P1, A1).
| Field | Rule | Used by |
|---|---|---|
id, title | RFC-NNNN; 10 to 90 characters; the H1 repeats both (F2) | Index, changesets, registry |
status, decided, deciders | Fixed enum; decided date once decided (D1); deciders named | Index, release checklist |
created, commentsClose | At least five working days apart (C1) | Review reminders |
target | YYYY.MM, required when accepted (D2) | Guide 08 calendar |
breaking, deprecations | Agree with Impact (I1); DEP ids appear in the text (X1) | Guide 09 registry |
Appendix B
One RFC that passes, one that does not.
RFC-0042 is the worked example the other two guides build on. RFC-0043 is the "quick improvement" the essay warns about, written the way such proposals usually arrive.
examples/0042-link-button.md (excerpt)
## Problem
`Button` renders an `<a>` when it receives `href`. One component then has
two roles, and the props that only make sense for one of them are accepted by
both. A disabled Button with `href` still navigates (#1712). A loading Button
with `href` shows a spinner on a link that has already navigated (#1760).
A usage scan on 2026-08-31 found `href` on Button in 9 of 41 consuming
repositories, 212 call sites.
## Impact
- Teams affected: 9 of 41 consuming repositories, 212 call sites.
- Breaking change: no. Removing `href` is breaking and ships in 5.0.0 under DEP-0007.
- Migration: yes. A jscodeshift codemod rewrites literal `href` uses.
- Costs and drawbacks: one more component to document.
## Decision
Accepted on 2026-09-10 by the design system core team, with one change:
`LinkButton` has no `disabled` state. Ships in train 2026.10.The Proposal and Alternatives sections are in the kit file. Prior art in the RFC: Atlassian ships a separate LinkButton for navigation [S10].
terminal
$ node scripts/lint-rfc.mjs examples/0042-link-button.md
examples/0042-link-button.md: 341/400 words, 23/45 lines, 0 error(s)
$ node scripts/lint-rfc.mjs examples/0043-button-elevated.failing.md
examples/0043-button-elevated.failing.md: 578/400 words, 15/45 lines, 11 error(s)
line 17 S1 unknown section "Background"; the page has Problem, Proposal, ...
L1 578 words; one page is at most 400. Cut, or link the detail
line 27 P1 placeholder "TBD" left in
line 21 E1 Problem cites no evidence; link the issue, research or usage data
line 36 A1 Alternatives needs "Do nothing" and at least one other option
line 31 I1 Impact must state "Breaking change: yes|no" and "Migration: yes|no"
line 1 C1 comment period closes 2026-09-15; the earliest is 2026-09-21
line 40 D1 status is accepted but the Decision section starts "Looks good ..."
line 1 D1 status accepted needs a decided date
line 1 D2 an accepted RFC names its target release train (YYYY.MM)
line 1 X1 DEP-0009 is in the frontmatter but not in the textOutput from the kit, long lines shortened for print. RFC-0043 also proposes a major change (a new default tone) outside a major window; Field Guide 08's changeset gate catches that one.
.github/workflows/rfcs.yml (steps)
on:
pull_request:
paths: ["rfcs/**"]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: node scripts/lint-rfc.mjs rfcs/*.mdAdapt paths to your repository. This workflow was not executed for this guide; the linter it calls was.
Keep with the RFC
Leave a decision record.
Fill this in when the status changes from discussion to a decision. It is the part of the RFC that the next team, and the next agent, will read first.
Sources / maintenance
Keep the guide current.
Sources checked 24 September 2026. Process details (comment periods, states, section names) are quoted from each project's own repository or documentation on that date. The Medium article in S08 blocked automated fetching; its content was verified through search results.