ADR-driven solo development: 38 decisions, one architect

TL;DR

Architecture Decision Records are usually framed as a team artifact — a way for multiple engineers to align on why a decision was made. They are arguably more valuable solo. Past-you and future-you don't share context as well as you'd think, and an ADR is the smallest thing that survives the gap. After 38 ADRs across nine months of building a single platform, I'd recommend this as one of the highest-leverage process disciplines I've adopted solo. Your mileage will depend on the lifetime of what you're building.

What an ADR is, in one paragraph

An Architecture Decision Record is a short Markdown document that captures one architectural decision, in roughly this structure: context (the situation that forced a choice), decision (what you chose), consequences (what this implies — good and bad), alternatives considered (other options you rejected and why). They live next to the code, usually in docs/adr/, numbered sequentially. The original format comes from Michael Nygard's 2011 post and has been refined by many teams since.

The discipline is small enough to fit in 20 minutes. The compound effect over a year is large.

The case for ADRs solo

Team ADRs exist to align humans who don't share a brain. Solo ADRs exist to align past-you and future-you, who share a brain but not the memory of why specific decisions were made. The intuition that "I'll remember why I did this" is reliably wrong on a six-month horizon. Specific examples from my own ADR set that I could not have reconstructed without writing them:

The pattern is: the cost of an ADR is paid once. The benefit is paid every time you'd otherwise wonder.

How I write them

My ADRs follow the standard format with two additions I find earn their keep.

Alternatives Considered, with rejection reasons. The standard ADR template includes this section but doesn't emphasize it. I treat it as load-bearing. For every meaningful decision, I list 2–3 alternatives and state — in one or two sentences — why each was rejected. This is where future-me gets the most value, because the question I most often re-ask is "did past-me consider X?" If X is in the rejected list with a reason, I can evaluate whether the reason still holds.

Tags and cross-links. Each ADR has tags (architecture / workflow-engine / concurrency / MS-readiness / etc.) and cross-links to other ADRs it builds on or contradicts. This turns the ADR set from a list of documents into a graph of decisions. When I add a new ADR that depends on three earlier ones, the new one links them, and the relationship is visible at a glance.

One thing I do not do: write ADRs for every code change. ADRs are for architectural decisions with at least one non-obvious alternative. "I added a getter for this property" is not an ADR. "I chose a CAS-based concurrency primitive over pg_cron or advisory locks for non-idempotent workflow steps" is. The bar is "would future-me wonder why this was done this way?"

When to write an ADR

Three triggers that I find correlate well with "this should be an ADR":

  1. I'm choosing between two or more options that each have real costs. If there's no genuine alternative, there's no decision — just a default. Don't ADR defaults.
  2. The choice affects code I haven't written yet. Architectural decisions propagate. A choice about "how do modules communicate" affects every cross-module interaction in the codebase. That's exactly the kind of decision that's costly to revisit and worth recording.
  3. I'm doing something that looks wrong without context. A sentinel exception in a no-throw codebase. A forwardRef across modules in an event-driven architecture. An any in the workflow engine base class. Each of these would get flagged in code review. Each is deliberate. Each gets an ADR explaining why.

The third trigger is the most important. ADRs are the antidote to future-me "fixing" past-me's deliberate trade-offs.

The status field

I use four ADR statuses: Proposed, Accepted, Superseded, Deprecated. Most live in Accepted. The interesting cases are the rest.

Superseded is what happens when a later decision replaces an earlier one. The old ADR stays — you do not delete it — but its status is updated and a link to the superseding ADR is added at the top. Future-me reading the old ADR sees "this was the decision in March 2026; it was replaced by ADR-031 in April." This is more useful than the alternative ("delete the old one and lose the history of why"), because the old reasoning often explains constraints that led to the new decision.

Deprecated is what happens when a decision becomes obsolete without being replaced. A primitive that's no longer used, an integration that's been removed. The ADR stays as a marker that this was once a thing.

I have not yet superseded an ADR with another in my set — most decisions have held — but I have deprecated two. Even those have been useful: when someone asks "why doesn't this module do X anymore?" the deprecated ADR has the answer.

The thing solo developers usually skip

Most solo developers don't write ADRs because the perceived benefit (sharing context with teammates) doesn't apply. This misframes the value. ADRs aren't primarily about sharing context with teammates. They're about creating context that can be shared at all. The act of writing a decision down forces clarity about what the decision actually is — what alternatives were available, what costs each carries, what considerations tipped the balance.

Half the time I sit down to write an ADR for a decision I think I've made, I realize halfway through that I haven't actually decided anything yet. I have a vague preference. Forcing myself to name the alternatives and articulate why each is worse than my preferred option is a way of converting vague preference into actual decision. Sometimes the alternatives turn out to be better than I thought, and I change my mind. Better to discover this at ADR-writing time than after writing the code.

This is the "rubber duck" effect, but for architecture rather than debugging. The duck makes you say the thing out loud. The ADR makes you write it down. The act of writing surfaces gaps the act of thinking doesn't.

The 38 in nine months number

I wrote 38 ADRs in roughly nine months — 36 full decisions plus a couple of refinement notes that update earlier ADRs as the design evolved. That averages to about one per week, which is sustainable. The distribution is uneven — early on I wrote four or five in the first two weeks while making foundational decisions, then settled to one or two a week as the codebase stabilized. The math is: 20 minutes per ADR, four ADRs a month, eighty minutes a month. For a system you'll live in for years, this is trivial.

The titles of mine, by topic cluster:

Pattern: foundations early, infrastructure middle, integrations late, refinement throughout. Each ADR builds on earlier ones, and the cross-links make the dependency graph navigable.

What I'd recommend

If you're a solo developer building something you intend to live with for more than a year, consider writing ADRs. Start with five and see if you keep going. The discipline that matters is:

If you're hiring a senior engineer, asking to see their ADRs is a useful filter — though hardly the only one. A candidate who has 30+ ADRs across a real codebase has demonstrated a discipline that is not widely shared. The ADRs themselves will tell you more about how they think than a single system-design interview usually does.

And if you're me, six months from now, reading this: the ADRs were worth the time. Keep writing them.