Skip to content
← Blog Blog

AI unleashed

In August, AI to me was autocomplete and a chat window, and I wasn't impressed. Now I hand plans to up to thirty agents at a time, on a backend merge that isn't allowed any downtime. The agents only run loose because the plan is tight: how I decide what to hand over, why a couple of hours of planning is the expensive part, and what the review caught when the agents wrote Java in Go.

A uniformed commander on a balcony pulls glowing strings attached to rows of robot agents working at desks in a vast control hall

I wasn’t AI native when I started at GolfNext in August. I had used GitHub Copilot before, both the autocomplete that suggests the next few lines while you type and the chat where you can prompt it, and I wasn’t impressed. It couldn’t do my tasks for me. At the time I was building an integration against a system whose documentation was private, behind a login, and there was no way to make Copilot read it. So whether it was completing a line or answering a prompt, it was working against an API it had never seen, and I did the actual work myself.

My boss told me I’d be amazed by Claude Code. I thought I’d believe that when I saw it. Then I saw it.

Claude Code isn’t autocomplete, and it isn’t a chat window either. It’s an agent: you give it a task, and it reads the code, runs commands, makes changes and checks its own work. It can even use my browser when I let it, so documentation behind a login is something it can go and read, the same way I would. The wall I hit with Copilot simply isn’t there anymore. In the two months since, the way I work has changed shape around that. These days I’d call it agentic engineering. I spend my time deciding what should be built and checking that it was, and agents do most of the building in between.

Here’s what that looks like on the biggest thing I’m working on.

We have two admin portals. Each has its own backend, its own login and its own database, and club staff who use both log in twice. Merging them into one is on the roadmap, and I’ve written before about the order we chose: a bridge between the two logins first, then one frontend over both backends, then the backends themselves.

The first two steps are done. The frontend of one portal now lives inside the other, the two platforms talk to each other, and feature development carried on in both the whole time. Nobody got a maintenance window. Golf clubs run on this software every day, so there was no planned downtime, and there won’t be any for the rest of it either.

The next step is harder. One of the backends is written in Java, and we’re rebuilding it in Go. Once that’s done, one backend gets ported into the other, and we end up with a single application. Again without stopping feature work, and again without a window.

Most of the code for that won’t be typed by a person. That’s the part people ask about, and it’s the least interesting part. The first decision on any piece of work is its size, because I use agents in three different ways. The backend merge is the large case, and I’ll get there by way of the small and medium ones. What changes between the three is how much of the thinking I hand over, and when I take it back.

Small: can you do this on your own?

For a small task I ask the agent one question before anything else: can you do this on your own?

I don’t say “do this”. I ask whether it can. It reads the code, looks at what the task touches, and tells me whether it’s confident or whether there’s something it doesn’t know. Most of the time the answer is yes, and it does it. Sometimes the answer is “this depends on how X behaves and I can’t see that from here”, which is exactly the sentence I want to hear before it starts rather than after.

A typical one: a bug report about a printer that doesn’t print. The software knew perfectly well something had gone wrong, because it wrote the error to the logs. It just never told the person using it, who got no warning at all and was left waiting for a printout that wasn’t coming. The fix is small and well-bounded: find where the error is caught, and show it to the user instead of only logging it. I asked the question, got a yes, and the agent fixed it on its own.

The question costs nothing, and it changes the failure mode. An agent told to do something will do something. An agent asked whether it can is allowed to say no.

Medium: research first, options second

For a medium task the agent goes first, but it doesn’t build anything. It evaluates the task, does the research (the code involved, the API documentation, how the same problem is already solved elsewhere in our repositories) and comes back with what it found and what it would suggest.

A typical one: a feature request from a club that uses a specific integration. The agent can read the integration, see what it already supports and work out where the request would fit. But it comes back with questions rather than an answer. Is this feasible at all with what the integration exposes? If it is, which part of the integration should be expanded? Or is this one club’s need different enough that it deserves a custom module of its own, rather than bending a shared integration around it?

Those aren’t questions the code can answer. They depend on how many other clubs use that integration, what we’re willing to maintain, and where the product is heading, and none of that is written down anywhere the agent can read. So it can’t complete the task without me, and it shouldn’t pretend it can. What it can do is turn a vague request into a short list of real choices with the trade-offs worked out. Then I start shaping: I pick one, combine two, or throw them all out because the research changed the question.

Large: a couple of hours, before any code

For a large project, and merging two backends while both of them keep shipping features is a large project, I spend a couple of hours planning with the agent before anything gets built.

Part of that is brainstorming. Most of it is me supplying context the agent cannot get on its own:

  • Architecture. What talks to what, which boundaries have to hold, what the target shape is and which compromises are allowed on the way there.
  • Technology. What we’re standardising on, what we’re moving away from, and what is off the table.
  • Purpose. Why we’re doing this at all. Here: club staff should log in once, and we should maintain one system instead of two.
  • User experience. What the person at the other end will see during every step of the migration, including the steps that are only halfway done.

The plan starts its life in the agent’s context, the running conversation we’ve just spent two hours filling. That’s a fragile place to keep anything. Close the session and it’s gone, and nobody else on the team can see it while it lasts.

So the last thing the planning session does is write it out, as tasks in our task system rather than a long document. Each task gets its subtasks, and the agent records the dependencies between them: what has to exist before a task can start, and what is waiting for it to finish. By the time I close the session, the plan has stopped being a conversation and become something the rest of the team can see, pick up and track.

Before anything runs, the team hears it. We talk it through briefly, and they hear the suggested solution. Those talks are short, because we already agree on most of the direction. Sometimes I also ask a colleague to look at the plan itself for a second opinion, since a plan that only one person and one agent have seen has had exactly one human check.

This is the expensive part, and it should be. Every question answered here is a question an agent won’t have to guess at later. Every dependency written down is a collision that won’t happen when thirty things are running at once.

Execution is the boring part, on purpose

Once the plan exists, executing it is almost an anticlimax. I hand it back to the agent, and it starts subagents (separate agents it launches and coordinates itself, each with its own task) on every subtask whose dependencies are met. Usually somewhere between ten and thirty of them at a time. When one finishes, whatever was waiting for it can start.

Thirty agents in one codebase sounds like a recipe for them tripping over each other. What keeps them apart is the plan. The dependencies written down during planning decide what can run at the same time and what has to wait, so two agents don’t end up building on something that doesn’t exist yet.

This works because nothing that matters is left for the agents to decide. An agent running a well-defined subtask with its context written down next to it is doing exactly the kind of work agents are good at. An agent running a vague task guesses fluently, and you find out which parts were guesses afterwards.

That’s also why I don’t trust the word “unleashed” when people use it about AI. The agents only run loose because the plan is tight.

Did they build what I planned?

When all the agents are done, I first check whether they built what I planned. Did every task actually land, and does the whole add up to the thing we designed, rather than a set of subtasks that each pass their own tests?

Then I review from three directions:

  • Architecture. Did the boundaries hold? Did anything reach across a seam it shouldn’t, or quietly add a dependency the plan didn’t have?
  • Code quality. Is this code someone on the team can own next year? Consistent with the rest of the repository, tested where it matters, no clever parts nobody asked for.
  • User experience. Does it feel right to the person using it, including mid-migration, when half of the system is old and half is new?

Two of those, architecture and user experience, are context I gave the agent at the start, so the review is really checking the work against what I supplied. If it keeps finding the same kind of problem, the fix usually belongs in the next plan.

The clearest catch so far came from the backend rewrite itself, a port from Java to Go. On paper, the agents delivered what was planned: the endpoints were there and did what the old ones did. But the first version was Java written in Go. Where the Java code leaned on a library, the agents rebuilt that library in Go by hand, even when Go’s own standard library already did the job. Dates were the obvious case. Instead of using Go’s time package, which handles them perfectly well, we got a home-made copy of how Java does date and time.

None of that fails a test. It compiles, it runs, it returns the right answers. It’s also code nobody on the team should have to maintain: more of it than needed, shaped like a language it isn’t written in, and unfamiliar to anyone who knows Go. That’s a code quality problem and an architecture problem at once, and you only catch it by asking “is this how we’d build it?” as well as “does it work?”.

The fix went back through the same loop as everything else. I asked Claude how we could get rid of the home-made library and use Go’s standard one instead, without losing anything. That last part was the real constraint. Clients already call those endpoints, and every timestamp they get back has to come out in exactly the same format after the port as before it. A port that is cleaner on the inside and breaks somebody’s date parsing on the outside isn’t finished. It got fixed: the home-made library went, and the timestamps stayed the same.

Where my time went

Add it up, and on a large project the typing has nearly disappeared from my day. What’s left is the two ends: hours of planning at the start, and a careful review at the end. It’s different work, and there’s no less of it. But I get far more done with it.

I think that’s the right way to see AI coding: as a compiler, not as something coming for our jobs. Nobody writes machine code by hand anymore. The compiler took that over decades ago, and it didn’t make programmers redundant. It moved the work up a level, to deciding what the program should do. Agents are the next step up, with one big difference: a compiler doesn’t guess. That’s why the review at the end isn’t optional.

So far, here, the productivity hasn’t turned into fewer people. It’s turned into bigger ambitions. Every team has a list of things clearly worth doing that never beat the next customer request: better internal tooling, features customers would enjoy but never asked for loudly, easier onboarding for the next person who joins. Now we have time for them.

In August, AI to me was autocomplete and a chat window, and I wasn’t impressed. Two months later I’m planning the merge of two backends that can’t be taken down, and handing the plan to 10, 20 or 30 agents at a time.

My boss was right. I was amazed. But the speed of the typing isn’t what amazed me. It was finding out that once the typing stops being the job, the part that’s left, the plan and the review, is the part I’d always rather have been doing.