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 108

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. Something I had to fix as it was doing my nut in.

The decision

I rebuilt from the ground up, and made the record of why the basis of any decision, from new features to code changes, rather than doubling back and working it out after the fact.

I covered the first decisions of the new repo in two ADRs:

  • [ADR-0001] Record architecture decisions. Every cross-cutting change starts with a numbered ADR covering status lifecycle. I enforced it as a convention and built nothing to police it, because an ADR linter would defeat the purpose of keeping it simple.
  • [ADR-0002] Defer recovery mechanisms. I explicitly did not rebuild the preflight, postflight and inventory tooling. I had created all of it 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 I built, particularly the inventory CLI, was useful in the old project, but proper documentation is miles better than creating tools to fix avoidable issues. Seems easy enough, right?

What it buys

Alfred’s repo now carries 108 ADRs. Each one names the alternatives I argued and rejected, and the details of why. Now when I review the codebase I can look the decision up in the ADR and see the why plainly, the constraints which led to it and not the ones that happen to apply now.

The discipline shows up in three of those records:

  • Types generated from the API contract ([ADR-0005]). I had been having them hand-written (by Claude) at both ends, where backend and frontend could silently disagree…
  • A visual contract for the frontend ([ADR-0013]) and Playwright end-to-end coverage ([ADR-0009]), so the testing grew with the UI and I never had to write it retroactively.
  • Per-folder CLAUDE.md files ([ADR-0003]). I split the 600-line root file into smaller and more logical ones, which lets context arrive more directly and with less room for error.

Closing thoughts

Reviewer convention enforces the ADR gate and CI does not. I chose that deliberately and recorded the choice as one, along with its failure mode, which is that the reviewer is me and there is only one of me. I am the judge and jury.

The rebuild has five shipped domains and 695 commits over four months, where its predecessor ran to 5,159 commits for 14 domains in nearly six. That is about 172 commits a month against about 860, five times fewer, and the other nine domains are still to come. It has cost me time. Those same figures work out at roughly 25 days a domain against 13 before, nearly twice as long, and everything is built with the ADRs 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 from Alfred/docs/adr/ at sync time, not typed here.