Case study

Alfred

A personal assistant rebuilt from scratch after the first attempt collapsed under its own undocumented decisions.

Decisions this project turns on — 6 of 79

ADR-0001Record Architecture Decisionsaccepted 2026-05-15
ADR-0002Defer Recovery Mechanismsaccepted 2026-05-15
ADR-0003Per-Folder CLAUDE.md Filesaccepted 2026-05-15
ADR-0005TypeScript Types Generated from FastAPI OpenAPI Schemaaccepted 2026-05-15
ADR-0009Playwright for Local End-to-End Testsaccepted 2026-05-16
ADR-0013Frontend Visual Contract (Primitives Inventory + Tier Rules)accepted 2026-05-17

The project before this one

I had built a PersonalAssistant to help me organise myself day to day. It started as a simple todo list which grew arms and legs, with a little bit of LocalLLM to help parse and keep me in check, spanning 14 domains from wellness to finances. In the background, it went as far as researching venture ideas and specific topics I had given the PersonalAssistant as tasks, and keeping me updated via a daily Zeitgeist - a digest containing a blend of current events, news and a glance of my day. In the end, the codebase had 84 API route modules, 315 frontend pages and 55 agent files, with no real recorded documentation outside of CLAUDE.md, which had reached 600 lines.

The size of the repo wasn’t the issue. The problem was the decisions of why something was built. Duplicated efforts happened because the triage wasn’t done, and there was no reasoning for why it was built. It was just built to suit that particular task. A diff would only show changes, not the why.

There was some plaster and Blu-Tack kind of tooling to help remedy it. Preflight scripts listed branches, worktrees and conflicts at the start of every session. This soon became context heavy as the repo grew. There was even a postflight script which checked that docs were updated. Eventually, I had a SQLite inventory to speed up codebase parsing as grep and find stopped being fast enough with the size this repo had gotten to. By the time PersonalAssistant had come to a close, this amounted to 40,000 symbols and 23,000 dependencies.

None of this added features - it was purely reactive to trying to develop in a mess of my own creation.

The decision

Rebuild from the ground up. Make the record of why the basis of any decision from new features and code changes rather than doubling back and working it out after the fact.

The first decisions of the new repo were covered in two ADRs:

  • [ADR-0001] Record architecture decisions. Every cross-cutting change starts with a numbered ADR covering status lifecycle. Enforcing a convention rather than tooling to fix down the line. Like creating an ADR linter would defeat the purpose of keeping it simple.
  • [ADR-0002] Defer recovery mechanisms. The preflight, postflight and inventory tooling was explicitly not rebuilt as these were created to treat the symptoms of bloat, whereas the ADR gates set the rules from the start.

ADR-0002 sets the tone for the new repo. The decision to build less is worth a lot more than I would have thought. Rebuilding the repo in this way ensures that changes are more meaningful and concise with less room for error. The tooling that was built, particularly the inventory CLI, was useful in the old project but proper documentation is miles better than creating tools to fix avoidable issues.

What it buys

Alfred’s repo now carries 74 ADRs. Each one names alternatives that have been argued, rejected and the details of why. Now, when the codebase is reviewed, the decision can be looked up in the ADR and the why can be plainly seen - the constraints which led to it, not the ones that happen to apply now.

In short, the ADR discipline gives:

  • Types generated from the API contract rather than hand-written on both sides, so backend and frontend cannot silently disagree ([ADR-0005]).
  • A visual contract for the frontend ([ADR-0013]) and Playwright end-to-end coverage ([ADR-0009]). This was to ensure that testing was in place and grew with the UI instead of writing it all retroactively.
  • Per-folder CLAUDE.md files ([ADR-0003]) instead of one 600-line root file, splitting up into more logical (and smaller) CLAUDE.mds allows for context to be more direct and less room for error.

Closing thoughts

The ADR gate is enforced by reviewer convention, not by CI. This is by design, a recorded choice but with a failure mode - it depends on the reviewer, of which there is only one.

The rebuild is still ongoing, four domains and 414 commits over three months - with its predecessor having 5,159 commits for 14 domains in nearly six months. There is still some amount of work to do with the other 10 domains remaining. Although it has taken significantly more time with ADRs, everything is built with it in mind rather than a hodgepodge of prompts to get a change to where it needs to be.

Decision numbers, statuses and dates are read fromAlfred/docs/adr/ at sync time, not typed here.