Sailer.Software

Wildcard at Work

Product Impact Engineer @ Manex AI


Docs Are Dead, Long Live Decisions

Code is cheap now, and so is documentation. And still I often face outdated, useless technical documentation in nearly every project I encounter. Either it is solely AI-written and therefore more or less just a description of the status quo. Or it is handwritten / AI-supported and therefore outdated (and most likely still just a description of the status quo).

But there’s no point in documenting the status quo anymore. When I want to understand a project’s architecture, coding standards, or whatever, I don’t open the docs anymore. I open my coding harness, point it at the code and get a more precise answer than any documentation could give. A good harness also knows me after working with it for a while: how senior I am and how I think about software. So it explains things at my level, far better than a static page maintained by whoever drew the short straw this quarter. Sure, juniors may need a few starter questions, but an onboarding skill covers that easily.

And sure, you still need user documentation, as users (as long as you’re not working on an open source project) have no access to your code. So give them your usual AI-generated Docusaurus page with the obligatory “Getting started”, a good llms.txt with an overview and an llms-full.txt with way too many details, so that their harness can answer any possible question.

So are docs dead now?

No. Because we still have several communication problems that can’t be solved with code alone. First of all, code is very precise at explaining the flow of a program, and even better at describing how things are built. But it’s very bad at telling you why it exists at all.

This isn’t a new insight. We’ve known about this problem for a long time and have tried to solve it with different approaches, like comments in the code (I don’t see a reason for those anymore) or documentation frameworks like arc42 (in my humble opinion one of the best approaches so far).

This doesn’t just apply to why the software was built in the first place, but also to where it’s heading. Maybe we’re in the middle of migrating from a monolith to microservices, so the architecture is a bit of a mess right now. Point a fresh agent at the repo and it will propose lots of changes, because from its point of view, the architecture simply looks broken. A coding harness with a good memory system will pick up your intentions across sessions, but the rest of the team working on the project (and their agents) won’t get the memo.

Therefore, we need a clean way to tell our best coding buddies what we intend to do and, in particular, why. Fortunately, we already have a solution for that: it’s called Architecture Decision Records1. So basically, we write down every important architectural decision we make in a more or less predefined structure, e.g. as a Markdown file in an adr folder in our repo. Usually an ADR is structured like this:

  • Title: A short, unique identifier and name (e.g., 0001-use-postgresql-for-primary-db.md).
  • Status: The current state of the decision, typically Proposed, Accepted, Rejected, or Superseded.
  • Context: The forces, background, and technical or business problem that made the decision necessary.
  • Decision: The clear, direct choice that was made.
  • Consequences: The positive outcomes, negative side effects, and trade-offs accepted as a result of this choice.

ADRs are particularly good here because they always focus on the why of a decision, and they come with a status. Even a rejected decision keeps its reasoning, which solves another problem: when implementing bigger features, LLMs tend to rewrite large parts of the system. Their task is to ship this one feature, not to think about the whole system. You see this all the time when you ask an agent to refactor code. In my experience, adding ADRs to your harness’s context fixes this problem.

General Decision Records

As the name implies, ADRs are usually limited to architecture. I propose going further and using them for every strategically important decision in a project, technical or not. I call them General Decision Records (GDRs).

Take UX. Say the UX team decides that the mobile app will only offer a reduced feature set: viewing data and approving requests, while everything else stays on desktop, because clarity on a small screen matters more than feature parity. That decision goes into gdr/ux/ in the repo. The next time someone asks their agent to “add the settings page to mobile”, the harness knows why that’s a bad idea and pushes back instead of building it.

The same applies to marketing, sales or any other decision made by someone that shapes the software. It’s important that a software engineer writes the GDR. Why?
Because it forces the people building the system to actually understand why it has to meet certain requirements.

Yes, this creates redundancy and maybe sounds like an antipattern in the first place. The UX or marketing team has probably documented the decision somewhere already, but most likely in another system, one the engineering team may not even have access to. Sure, you could ask your colleagues to write the GDR and commit it to your repo themselves, but you really don’t want that. Most importantly, when engineers write it, the people who need to know it actually know it, and they translate it into technical language that fits the codebase.

Another nice side effect: there’s now a clear record of what has and hasn’t been communicated to the engineering team. If a decision isn’t in a GDR, it never reached engineering, so nobody can blame you for not implementing something no one told you about.

What does this look like in practice?

Claude put together an example repo2, including a few useful skills for creating and managing GDRs.

Start by mentioning in your AGENTS.md that you use GDRs, with a short description of how they work (you can copy it from the example repo). Then you have a few decisions to make.

First, how do you organize records by the team that owns the decision? You can use a subfolder per team, like gdr/se/, gdr/sales/ and gdr/ux/, or encode it in the filename, e.g. 0001-se-use-modular-monolith-architecture.md. There’s no right or wrong here; pick whatever makes more sense for you.

Second, define what a record has to look like. I’d recommend a gdr-template.md for that. If the structure differs by team, the subfolder approach is probably the better fit, with its own template in each subfolder.