We rebuilt our agents five times so you don't have to — here's what we learned

Over the last few years we've rebuilt our test-authoring agent five times. In normal software, rewriting a core feature that many times is a flashing red light; when the models shift under you every few months, it's mostly just what staying current costs. The useful part is what survived every rewrite — five things that held no matter which model we were running on.

The agent flywheel — the five lessons as a cycle, each rebuild spinning it faster


Work with the model's grain, not against it. Back in 2023 I tried to get PaLM to pick the single most similar word to a target in the DOM, for smarter auto-healing. It failed every way I framed it — but the outputs showed it clearly understood the task. It was bad at picking one similar word and surprisingly good at grouping words by meaning. So we rebuilt auto-healing around semantic grouping instead of arguing with it, and a hard limitation turned into a reliable feature. These models have a grain, like people do; you get further shaping the system around what they're already good at.

An agent is only as good as its input. Most of the agent failures I've chased turned out to be upstream of the model — a planning session stuffed with base64 screenshots the model can't read, a tool returning a vague error the agent couldn't act on, the right context buried under noise. The model usually wasn't the problem; the signal we handed it was. We've gotten far more mileage out of cleaning up what goes in — tighter tool definitions, scoped context, error messages written for the model to actually use — than out of swapping the model itself.

Embrace non-determinism on purpose. Testing is supposed to remove uncertainty, so deliberately putting a probabilistic model in the middle of it felt like a category error. It wasn't. When we leaned into letting a model judge whether an application state was right — in plain language, the way a person would — it expanded what automated testing could even cover. The trick was using the model where judgment beats a brittle assertion, not everywhere.

Measure trust with data, not vibes. For a while we tested our AI features with small hand-curated sets and spot-checks, and it left us blind to silent regressions. The fix was building evaluator suites — the same judge idea, pointed inward — that grade our agents across many runs. That's how we caught a regression hiding inside an upgrade: moving to Gemini 3 cut reasoning loops by something like 2–4x, but the same report flagged hardcoded values going up, because the model was now finishing harder tests that older ones gave up on. I'd never have spotted that by eye.

AI amplifies whatever foundation you've built. When we scaled coding agents across our repos this year, the biggest thing slowing them down wasn't the AI — it was build times, shaky CI, thin test coverage. The same fundamentals that slow people down. Fixing them helped the agents and the humans in equal measure. A non-deterministic tool doesn't make error handling, reusability, and tests matter less; it runs your weak spots over and over until they show.

What strikes me looking back is how little of this was about the model getting smarter. In 2023 we couldn't get a model through a login screen; today the model is rarely the bottleneck. The harder questions now are fit and cost — whether an agent has the context about what you care about, works inside the tools you already use, and earns its keep on latency and token spend instead of taxing the team. The bar I keep coming back to is whether it behaves like a good teammate.
share X LinkedIn

More posts

Deleting 400 stale i18n keys and the Bootstrap we'd carried for years

I've been deleting a lot of code lately and I cannot tell you how good it feels. I cleared out about 400 stale translation keys — the kind that accumulate over years and that nobody dares touch, because who still knows what references them? The agent knows; it checks every reference in seconds, so the sweep I'd always filed under "someday" turned into an afternoon. And Eugene and Jeff finally pulled out Bootstrap (BOOTSTRAP! the framework we've been trying to get rid of for years!) — thank you both, truly. I re-ran our UI library market share report afterward and that's when the impact really landed: Bootstrap is finally gone.

What surprised me is how the agents change the math. Migrating class components to function components with hooks, ripping out an old framework, pruning dead i18n — these were always "we should do this eventually" jobs that lost every prioritization fight. The cost of removing code and cleaning up our codebases dropped enough that now we just... do it. I keep catching myself opening diffs hoping they're mostly red.

My ideas died in markdown files, so now one skill interviews me and another ships them

My ideas used to end up as markdown notes in a folder, which is a polite way of saying they ended up nowhere. Filing a real ticket is more effort than I want to spend in the ten seconds I actually have the idea.

Now I say /new-idea and describe it in a sentence. It interviews me — what's the problem, what does done look like, what's explicitly out of scope — and it stops as soon as the ticket is unambiguous, then files it under my epic and assigns it to me.

At the same time, another session — [on a stable worktree, remember?](https://fab.mabl.com/posts/2026-05-06-dario-i-had-20-worktrees-and-no) — is running /idea-loop. It picks the oldest idea up, moves it to in progress, and we build it: plan, tests, review, PR. From there the ticket walks its own states, and a morning check only marks it shipped once a production tag actually contains the commit.

Six AI reviewers improved a PR that shouldn't have existed

A PR landed on our CLI with ~550 lines of new probe code. Its entire product effect was one debug log line, and the number it existed to measure was one the PR's own limitations section admitted it couldn't measure. My six-lens /deep-review panel read it and did good work: safer, quieter, better named. Not one of the six asked whether it should ship at all.

The reviewers weren't the problem. Every mechanism in that panel is anchored — a finding needs a file:line, a fix needs a before/after. "This file shouldn't exist" has no anchor, so there's nowhere to file it, so nobody says it. Each lens does the one thing the harness makes easy: improve the code in front of it.

So every lens now returns a scope verdict — ship-as-is, split, or cut — and that gets settled before any fix is applied. Order matters: hardening code that's about to be deleted makes the deletion look expensive. And a cut doesn't authorize anything. The panel rules; I still press delete.

Chug that data lake

For years the pundits said "data is the new oil," so we hoarded it: API logs, cluster utilization, billing exports, error traces — all in hope that one day we'd have time to actually make use of it. That day is here, and it belongs to the agents. Want to optimize application opex? You could clear your calendar, check into a remote alpine retreat, and spend a week reading the code, the 27-page cloud vendor pricing page, and the 7 pages of "limits" and gotchas — then build a massive spreadsheet crossing all of that with your usage data. But that smells like 2024 thinking. It's 2026: wire up the proper MCP servers and ACLs, offer your agent the code, docs, and data with precise marching orders, and go to bed. Do your vendor's billing docs include set theory notation and lots of linear algebra? Perfect — agents love math. In the morning, review the LLM's citations, pat yourself on the back for saving the operation a few hundred grand — all while you slept.

You're no longer limited by what you know how to do with your data lake, but simply by your own initiative and imagination. LFG.

We started shipping more PRs than we could review, so I wrote /review-with-me.

823 PRs merged across our repos this month. Same 28 days last year: 218. Same number of people.

Everyone got faster once we leaned into the AI tooling, and the review queue got the bill. I stopped being able to keep up with mine, and I don't think I was alone. Writing the code wasn't the slow part anymore. Reviewing it was.

What made review slow wasn't the hard parts, it was getting to them. Most of a big diff is a rename, a function moved between files, a regenerated snapshot. The two hunks that decide whether this ships are buried in the middle of it.

So I wrote /review-with-me. It tells me what the change is for and why, picks the hunks carrying real risk, walks me through them one at a time, and turns whatever I say into inline comments on the PR.