Skip to main content

Architecture decision log

Every architectural decision lives here as its own record. The table below is the index: read it top to bottom, and the latest decision on a subject supersedes any earlier one.

Superseded records are never deleted and never edited into agreement. They stay, marked, so the reasoning trail survives — including the reasoning that turned out to be wrong. That is the point of keeping them.

A record in this log is a decision we have made. There is no status column: if it is on main, it is how we do it. Changing one means a new record that supersedes it, not an edit.


🔒 Confirm the decision before you write the record

The gate is at the start, not the end.

If you are working — and especially if you are an AI agent — and you hit something that needs an architectural decision, stop and ask @mattmoran56. Get an answer, then write the record with that answer in it and open the PR. Merging is the acceptance; there is nothing to sign afterwards.

What needs asking:

  • Anything hard to reverse — a database, a platform, a sync model, an auth model
  • Anything cross-cutting — it constrains code nobody has written yet
  • Anything contentious — two reasonable people would pick differently
  • Anything that changes a decision already in this log

What does not: a library swap, a naming convention, or anything a code review settles in one comment.

:::danger Do not invent the answer An agent must never decide one of these on its own and write the record as though it were settled. Not from a plausible default, not from "the code already does it this way", not from an inference about what Matt would probably say. Ask.

If code genuinely has to exist before the decision lands, keep it small, keep it reversible, and say so in the PR — but the record does not go in until there is an answer. :::


The table

Latest first. A decision lower in the table is older; if two records address the same subject, the higher one wins.

ADRDecisionDateSupersedesSuperseded by
0010Grades stored as published, converted approximately at read time2026-08-18
0009A social feed, firewalled from the guidebook — engagement never touches guidebook data2026-08-18
0008Containers, design components, primitives — boundaries enforced by ESLint2026-08-17
0007No accounts in v1; a local device identity, external provider later2026-08-17
0006A full preview stack on every pull request2026-08-17
0005Documentation as MDX in the repo, rendered by Docusaurus2026-08-17
0004Offline-first on-device SQLite with cursor-based delta sync2026-08-17
0003Expo-managed React Native, iOS first2026-08-17
0002Cloudflare Workers + D1 for the backend2026-08-17
0001One repository, pnpm workspaces2026-08-17

Writing one

Ask first. Then copy the template and number it with the next free integer.

Keep it short — a page, not an essay. The sections that earn their place are Context (what forced a decision), Consequences (what this costs us), and Alternatives (what we did not do and why). A rejected alternative documented here does not have to be re-argued in six months.

Then add a row at the top of the table above, and add the page to apps/docs/sidebars.ts.

Superseding one

  1. Ask first — reversing a decision is exactly the kind of thing that needs confirming.
  2. Write the new record. Explain what changed: new information, a new constraint, or the original reasoning simply not holding.
  3. Fill in the old record's Superseded by row and link forward. Add a Supersedes link on the new one.
  4. Update the table so the new record sits above the old one, and note the supersession in both rows.

Do not edit the old record's reasoning. It was what we thought at the time, and that is the information worth keeping.

Rejected ideas

An idea we considered and declined does not get its own record — it goes in the Alternatives considered section of whichever ADR it lost to. That keeps the decision and the roads not taken in one place, which is where someone re-litigating it will look.