Skip to content
All posts
  • 10 min read
On this page

I rewrote AB Method

My agent workflow kept catching problems after the code was written. So I went back to two books, found the cracks, and rebuilt it from the ground up as a breaking v4.

Working on different and more complex projects made me realize one thing: keep it simple, and keep it dynamic. Those are the two things AB Method, the workflow I've been polishing for the past year and a half, was missing. I was getting complex code and a lot of tech debt.

That's where I introduced the cleanups: critics that review the diff and catch the slop. But they always came after the implementation, which breaks the whole point. The point is to zoom out, see the broader picture, and find the opportunities before you start building, not to find them in a diff you already paid for.

Back to the basics

So I did what I read in one of my favourite books of all time, Miyamoto Musashi's The Book of Five Rings: I went back to the basics. I picked the two best books about software, in my opinion, The Pragmatic Programmer and A Philosophy of Software Design, and went looking for the gaps. My agents weren't communicating clear intent to me, and they weren't writing clear code.

I had been patching the workflow for months instead of drilling down to the architecture underneath it. Every patch was a new rule on top of the old ones. Here's where the cracks actually were, and how v4 fills them:

6 holes in planning, and everything gets caught at Review, after the code exists.0 holes. Each one is closed before the code exists. Review is just the safety net.

Grillgrill the requestcommunication rules
Planmissions + critiqueplacement: reuse, extend, extractprototype pickertest plan
Buildtests from the plan onlycode rules while writing
Reviewsame critics, now a safety net
Docsedit in placecheck-docs budget gate
What checks the work at each stage of a task. Flip between the versions to see where the holes were.

In v3, planning had holes. Nobody decided where the code should live, what the UI should look like, or which tests were worth writing, so the agent decided while building, and the critics at Review cleaned up after it. Two of the holes, the guessed UI and the docs that only grow, weren't caught by the method at all. I was the check.

The principles

These are the rules v4 is built on, and why each one exists.

1. Answer first

Every workflow now shares one short file of rules for how the agent talks to me: the first sentence is the answer, no recaps of what I just said, status lines instead of narration, and questions only when the answer changes what happens next.

Why: an agent that buries the point makes you do the summarising. It sounds minor, but I read hundreds of these messages a week. Clear intent is a feature.

2. Decide where the code lives before you write it

Before any mission is written, the plan records a placement for every capability the task adds: reuse something that exists, extend the module that owns the concern, or extract the logic into its own module first.

progress-tracker.md
## Placement
- refund calc → extract `src/billing/refund.ts` from checkout + admin (mission 1)
- refund button → reuse `ui/ConfirmButton`

Why: this was the biggest crack. Agents aren't ambitious. Left alone, they add the thing inline, right where they are, and a reviewer flags the duplication afterwards. The Pragmatic Programmer calls the goal ETC, Easier To Change, and DRY is about knowledge, not copy-pasted lines. Ousterhout's deep modules say the same from the other side: pull complexity down into one place behind a small interface.

3. Make the change easy, then make the easy change

When placement says "extract", the extraction becomes its own refactor mission, and it goes first. It adds no new tests; the existing ones have to stay green.

Why: Kent Beck's line is the whole idea. Building a feature on top of a refactor you're doing at the same time is how you get a diff nobody can review.

4. Test what matters, not everything

v3 ran every mission through test-driven development, no exceptions. So the number of tests grew with the number of missions, not with risk, and the pipeline got slower every week.

Now each task has a test plan, and nothing outside it gets a test. Every mission is tagged:

TagForHow it's verified
verify: testdomain rules, critical flows, real bugstest first: red, then green
verify: buildstyling, wiring, config, refactorstypecheck, build and the existing tests

Focused tests run per mission, and the full suite runs once at the end.

Why: a test is code you maintain forever. It has to earn that by protecting a use case where you can say what breaks for the user if it regresses. A test that only proves the framework works is a cost with no return.

5. Prototype before you build

When a task adds new UI, the agent first builds several genuinely different versions of it, as one disposable HTML file with a picker. I flip through them, pick one, and that pick becomes the spec the missions implement. The file never touches the codebase, and it's deleted when the task is done.

Why: The Pragmatic Programmer says prototypes are for learning, and you throw them away. Ousterhout says to design it twice, because your first idea is rarely the best one. Before this, the agent's first guess was the design, and I only saw it once it was wired into the app.

6. Write less

The code rules apply while the code is written, not just at review:

  • The deletion test: if removing a line, a parameter or a wrapper loses nothing, don't write it.
  • No speculative generality: no options, factories or extension points with a single caller.
  • Comments explain why, never what. The default is no comment.

Why: the slop reviewer already knew all of this, it just knew it too late. Moving the same checklist to write time means the slop never gets written, instead of getting deleted.

7. Small docs, by rule

This one surprised me the most. AB Method keeps domain and architecture docs so the agent understands the project, and every task appended to them. Nothing ever removed anything. In my biggest repo, the frontend patterns doc alone had grown to almost 19,000 words, full of "this task added three more" and lists of every file that used a pattern.

v4 gives every doc a word budget and a few rules: a pattern is a rule plus one example, no task history, and no [DEPRECATED] entries, since git already keeps the old version. Pattern docs are split by topic behind a short index, so a mission reads only the topics it touches.

a frontend mission reads7,339 words of docs before it writes a linewas 38,439

frontend patterns, per mission18,830 979

UBIQUITOUS_LANGUAGE.md8,669 2,868 / 3,000

backend patterns, per mission7,998 1,028

tech-stack.md7,378 1,496 / 1,500

billing CONTEXT.md5,872 1,997 / 2,000

matching CONTEXT.md4,245 1,974 / 2,000

root CONTEXT.md3,562 1,996 / 2,000

all docs: 96,978 → 53,483 words · over budget: 23 → 0

Word counts from the biggest repo I run AB Method on, before and after one run of /ab-cleanup. Pattern docs count the index plus two topic files, which is what a mission reads now; the tick is the file's budget.

Why: docs the agent reads on every mission are context it pays for on every mission. Slow growth is the dangerous kind, because no single task looks like the one that broke it. Ousterhout says complexity is incremental, and these docs were the proof.

8. Gates, not wishes

The doc budgets aren't a polite request in a prompt. They're checked by a script:

npx ab-method check-docs

It fails when a doc is over budget, has a line over 60 words, names a task, or carries a [DEPRECATED] entry. A workflow that writes docs isn't done until it passes.

Why: models skim instructions like "keep it short". They don't skim a red exit code.

Putting it to the test

Rewriting a workflow is easy to justify on paper, so I ran both versions against each other, twice: once on an empty repo and once on a large production codebase. Each time it was two fresh agents, same model, same request, one with v3 installed and one with v4. I played the user for both, and every time they asked me something, they got the same answer.

Greenfield: a new app

The request was a small full-stack app:

Build a small reading-list web app: I paste a link with a title, see my list, mark items as read, and filter to unread only. Data must persist across restarts. Full vertical slice: UI, API, storage.

Both landed on the same stack (Next.js, TypeScript, SQLite through Node's built-in node:sqlite), and both finished with typecheck, build, unit tests and one Playwright test green. The difference was in how they got there:

  • v3 asked 6 questions, v4 asked 4. v4 made the obvious calls itself, like how to normalise a URL before checking for duplicates, and stated them as defaults I could override.
  • v4 prototyped the UI. It built three working layouts as one throwaway HTML file, and I picked one before any mission was written. v3 never asked about the UI and went with plain defaults.
  • v4 planned its tests. Four use cases, written down before the code: the link rules, persistence across a restart, the API contract, and one end-to-end flow. The scaffolding mission was verify: build, so it got no test at all.
  • The plan critic earned its keep. v4's critic noticed the glossary said a title is "human given" while the plan filled it in with the URL, and made me pick which one was true.

Here's what each one built:

The reading-list app built with AB Method v4, all view
The same app from the same request and the same answers. v3 never asked about the UI; v4 built three throwaway layouts and I picked one before any code was written.

And the numbers, measured on the two repos:

v3v4
Grill questions64
App logic335 lines335 lines
CSS50 lines226 lines
Tests38 tests, 441 lines16 tests, 194 lines
Bugs the end-to-end test caught21
Words of docs written860402
Agent time~14 min~15 min

The logic came out the same size, to the line. v4 protected it with less than half the test code, and the extra CSS isn't bloat: it's the design I picked, with an unread count, empty states, and motion that respects reduced-motion settings. v3's bigger suite did catch one more bug, a race in how its unread view reloads from the API. v4 filters in the browser, so it doesn't have that path.

The time came out even, because the prototype step cost about what the extra tests saved. I'll take that trade every time: the same minutes, spent on deciding what to build instead of on testing what didn't need it.

Brownfield: a large production codebase

An empty repo is the easy case. The real test is a big codebase with history: a multi-tenant product with about 7,300 unit tests and close to 100k words of domain and architecture docs. Both agents got the same throwaway feature: an "Export CSV" button that downloads a ranked list from one page. The v3 agent worked on the repo as it was, with the docs at full size. The v4 agent worked on the same code after one run of /ab-cleanup.

Both shipped a working feature, with the full suite green, and they converged on the same spec. This is where the cracks from the first figure showed up for real:

  • v3 wrote a second CSV writer. The codebase already had one, buried inline in an older export. v3 never found it and wrote its own, so the project now has two ways to escape a CSV cell. v4's placement step found it, extracted it into a shared module as its first mission, and built on top. The older export got the new protection against spreadsheet formulas for free.
  • v4's plan critic pushed back twice, both times rightly. The new endpoint would be reachable by partner API keys, which the plan had listed as out of scope, and the plan used "rank" for a third meaning the glossary didn't have. v3's critic had no objections. v3 did spot the API key question during its grill, so it wasn't blind, just later.
  • The docs moved in opposite directions. v3 appended to them. v4 had to fit its additions into the budget, so it cut two redundant sentences to make room, and left one line out instead of going over.
v3v4
Grill questions64
Plan critic objections02
CSV writers in the codebase afterwards21
Source lines added321316
Comment lines added5829
Tests37 tests, 552 lines26 tests, 420 lines
Doc words, net+159−17
Agent time~29 min~23 min

The code came out the same size again, but v4's includes the extraction: it removed a duplicate instead of adding one. It also wrote half the comments, fewer tests, and finished six minutes sooner, mostly because its grill was shorter and the docs it had to read were a fifth of the size.

Two runs, two kinds of project, so read them as data points, not a benchmark. But they point the same way: v4 spends its effort before the code exists, and that's where it pays off most.

Tear it down

So here's my advice to you, fellow reader. Never be afraid to tear it down and ship breaking changes, as long as you can justify the cost. Always be ready to be wrong, and never let your ego drive you into failure. Consider everything you've ever thought to be true as possibly false, and rebuild from the ground up.

In my case it was worth it. The core workflows went from 2,989 lines to 2,097, the main one from 570 lines to 207, and every check that used to sit at the end now sits where the problem starts.

If you already use AB Method, update it and run the cleanup once. It works on its own branch, commits each phase separately, and shows you every rule it would drop before it writes anything:

npx ab-method
# then, in Claude Code or Codex
/ab-cleanup