How I shipped a production app by managing AI agents like a team
I have played tennis my whole life. I wrote almost none of the code behind the app I built for it. What I did instead is the real story.
I have been playing tennis since I was a kid, and I never grew out of it. The serve you spend a winter rebuilding. The opponent you have lost to four times and finally read. The particular quiet of a match that comes down to one point. I still play every week, I still compete, and I still care more than a grown man probably should about a Tuesday-night league result.
What I never loved was everything that happened off the court. Because a community tennis league does not actually run on tennis. It runs on a WhatsApp thread nobody can follow and a spreadsheet somebody quietly regrets owning. Standings lag a week behind the truth. Two players book the same court. One mistyped score and the ladder is wrong for a month. Someone has to hold all of it together, and when that someone is you, the sport you love starts to feel like unpaid admin. I did not want a better spreadsheet. I wanted the boring version of a miracle: a league that just runs itself, so the only thing left to think about is the tennis.
So I built one. It is called League60, and it does the unglamorous things properly, live standings, brackets, and scoring that takes seconds to enter between games. But the app is the easy part to describe. The part worth telling is how someone like me got to build it at all.
Because ten years ago, I could not have. To ship something like this back then you had to be fluent in a stack of separate crafts, each a career in its own right: the front end, the state, the offline layer, the deployment. I have eleven years in analytics and strategy, not software engineering. A decade ago the honest answer to "could you build this yourself" was simply no, and no amount of wanting it would have changed that.
What changed is not that I finally became an engineer. It is that the job itself changed shape. AI agents can now write production-grade code. The whole catch, the only catch, is that they will just as happily hand you something confidently, beautifully wrong, and they do their best work only inside a structure that treats them as what they are: a team. Fast, tireless, talented, and in need of management. So I stopped trying to become the coder I am not, and started being the thing eleven years actually made me, someone who runs delivery. Specs, routing, verification, the discipline of shipping. Six weeks after the annoyance, the league was live.
Here is how that worked, and why it is really a management story wearing a tennis shirt.
The org chart
Two agents, two roles, and half the craft is knowing which one gets the ball.
The senior is the surgeon with the full medical record. It holds the whole codebase in its head, remembers the architectural calls we made three weeks ago, and can change many files at once without breaking the ones it is not looking at. Anything that touches the data model, security, or several screens together goes to the surgeon. So does anything where taste matters.
The junior is fast and exact on clean, mechanical work: a single function, types from a settled schema, tests for something that already exists. The escalation rule is mechanical too. The moment a task touches more than one file, or any shared state, it is no longer a junior task. And when the junior's first attempt is wrong, you do not talk it round. You reassign the work. Anyone who has ever managed people will recognise far more of this than they expect to.
The spec, written so a machine can build it
Every piece of work goes out as a brief precise enough for a machine to build from, which turns out to be nothing more exotic than a genuinely good spec. Three rules do most of the lifting.
One verifiable outcome per brief. Two outcomes is two briefs. Small and in order beats big and clever, because small is what keeps the checking honest.
Name every file the work is allowed to touch. That list is a contract. If the job turns out to need a file that is not on it, then the scope was wrong, so I stop and rewrite the brief rather than let it quietly sprawl.
Put the do-not-touch list in writing. Some files hold correctness that was painful to earn and is easy to wreck, the scoring engine, the standings tiebreakers, the logic that decides who actually won. Agents love to "improve" those in passing, the way a keen new hire loves to reorganise the shared drive. So they are fenced off: named at the top of any brief that goes near them, untouchable without an explicit instruction.
And every brief ends the same way: walk me through what changed and why, before you save a thing.
Gates, not vibes
Here is the uncomfortable truth about managing AI. The work always comes back looking finished. Confidence is free, and endless. So the entire system rests on one rule:
Every batch comes back with proof.
gate B, the test suite's numbers, delta in words: 1,191 → 1,197, six new, zero regressions
gate C, the targeted test or check that proves this outcome
gate D, grep proofs, both directions: the old path is gone and the new one exists
And then I go and look at the real files anyway, because assuming the agent finished the job is precisely how the confidently-wrong version slips into production. Each batch closes with a written report, and those reports are the memory of the project. You should be able to trust the work without re-running it.
If that sounds like bureaucracy, it is the opposite of bureaucracy. The loop is kept deliberately tiny, one brief, one outcome, one report, exactly so the checking stays cheap and nothing has to ride on faith.
Deploys are boring on purpose
If the gates pass, it ships. That is the entire ceremony. Boring deploys are the whole point, because all the drama is meant to happen earlier, in the spec and at the gates. Production discipline for a team of one looks identical to production discipline for a team of forty, with the meetings deleted.
What managing machines teaches you
The failure mode has a name: building faster than you can prove anyone needs what you built. Agents make creating things feel free, and free is a trap. The counterweight has to be structural. One outcome per batch. Every tempting side-quest, the adjacent bug, the refactor that would be so easy right now, goes into the report instead of into the code. Features get cut after real use rather than added on a hunch. Cutting scope is progress.
The other lesson is about plain words, and tennis taught me this one before AI did. The quality bar is binary: it works, it is safe, it is understandable, it is presentable. And "understandable" gets enforced with a humbling rule, a seven-year-old should be able to read every word on a main screen. Tennis jargon out. One obvious next action per screen. Errors written as sentences with verbs in them. The machines never needed that rule. The player who kept seeing beginners bounce off clever interfaces did.
Where this leaves the day job
For eleven years I produced analysis that ended where analysis usually ends, at the recommendation. A deck, a model, a memo that somebody else might one day build. This method quietly removes that excuse. The same discipline that runs an AI build team, spec, route, verify, ship, is exactly what carries a recommendation the rest of the way to a working, deployed thing.
That is the whole thesis of this site, and the exhibits are the receipts: a break-even model you can argue with, a Eurostat dashboard that writes its own analyst's notes, a tennis study that overturns its own opening assumption, each one built by the same pipeline that built the league I play in. Most analytics careers end at the recommendation. Mine ends at deployment, and then I go play the Tuesday-night match the whole thing was for.