Skip to content

Architecture decisions (ADRs)

An Architecture Decision Record (ADR) is a short document that captures one significant technical decision: the context we were in, what we decided, the options we turned down, and what follows from it. ADRs live here, next to the code they shape, and are reviewed and merged like code.

The architecture pages describe how the platform works now. ADRs record why it works that way, at the moment the choice was made. When you ask "why on earth is it built like this?", the answer should be an ADR.

When to write one

Write an ADR when a decision is architecturally significant. If any of these are true, it probably is:

  • It is hard or expensive to reverse (data model, storage, identity, a vendor contract).
  • It affects more than one app or team (backend and Flutter, or members and clinicians).
  • It adds or replaces a platform dependency: a third-party service, a framework, a core library, a new piece of infrastructure.
  • It changes where data lives or who can see it (Medplum, Firestore, Descope, access policies, PII or clinical data handling).
  • It sets a convention that every future change must follow (an API response shape, a state-management pattern, a testing gate).
  • You find yourself explaining the same "why" in more than one PR thread.

You do not need an ADR for bug fixes, choices that stay inside one feature and are cheap to change, or a decision that only applies while one Plan is being delivered. Those belong in the PR description or the Plan's decision log (see DPPD).

If you are not sure, write a short one. A three-paragraph ADR costs ten minutes; rediscovering the reasoning a year later costs much more.

How to write one

  1. Copy template.md to NNNN-short-title.md, where NNNN is the next free number in the register, zero-padded to four digits (0007-...). Use a lowercase, hyphenated title that names the decision, not the problem (0007-use-medplum-for-clinical-records.md, not 0007-clinical-storage.md).
  2. Fill it in. Set Status to Proposed. Keep it short: one to two pages is typical. Name the options you rejected and why; that is the part future readers need most.
  3. Add a row to the register below and an entry under Architecture → Decisions (ADRs) in mkdocs.yml.
  4. Open a PR. Use a docs(PPL-NNNN): ADR-NNNN <title> title for an ADR on its own, or put the ADR in the same PR as the change it justifies.

Two ADRs, one number

If two open PRs claim the same number, whoever merges second renumbers before merging. The number is only an identifier; it says nothing about importance.

How an ADR is reviewed and accepted

The ADR's PR is the decision forum. Discussion happens in review comments, so the reasoning stays attached to the record.

  • Request a review from each team the decision affects (@percihealth/backend, @percihealth/frontend, @percihealth/qa), not only the usual single CODEOWNERS reviewer. A decision that spans teams needs an approval from someone on each of them.
  • Post the PR link in #tech so people outside the requested teams can comment.
  • When the reviewers agree, change Status to Accepted, set the Date to the acceptance date, and merge. Merging is acceptance.
  • If the team decides against it, set Status to Rejected, record why, and still merge. A rejected ADR stops the same idea being re-argued from scratch.

Changing a decision later

Accepted ADRs are not edited except to fix typos or update the status line. To change a decision, write a new ADR:

  1. The new ADR explains what changed and sets Supersedes to the old one.
  2. In the same PR, set the old ADR's Status to Superseded by ADR-NNNN and update both rows in the register.

Use Deprecated for a decision that no longer applies but has no replacement (for example, the system it governed was removed).

Status Meaning
Proposed Under review in an open PR.
Accepted Agreed and in force.
Rejected Considered and turned down. Kept so the reasoning is not lost.
Deprecated No longer applies, with nothing replacing it.
Superseded by ADR-NNNN Replaced by a later decision.

How ADRs fit with DPPD

DPPD Proposals say what we are building and why it matters to the business. ADRs say why the system is built the way it is. Plans sit in between: each Plan has a decision log (section 3) for choices made while planning that piece of work.

  • A decision that only matters while that Plan is being delivered stays in the Plan's decision log.
  • A decision that will constrain work after the Plan is finished gets an ADR. Link the ADR from the decision-log row, and link the Plan from the ADR's Context.

Spikes often end in an ADR: the spike collects the evidence, the ADR records the decision.

For AI agents

Before proposing a change that touches a decision recorded here, read the relevant ADR and follow it. If the change would contradict an Accepted ADR, say so and propose a superseding ADR rather than silently diverging.

Register

ADR Title Status Date
0001 Record architecture decisions Accepted 2026-10-02