Six months of agentic development: directed autonomy
Six months ago I approved everything. Every plan, every file, every change, one at a time - probably out of a lack of trust in the output. Today I run Claude Code unattended for most of a piece of work, with sub-agents handling tests, security and review before anything reaches me.
As with everything I went on a bit of a journey to get to this point. Early on I discovered I could get a pretty good product with minimal oversight, but would easily lose control of the codebase doing it. The output was fine, genuinely fine. It worked, it was tested, it demoed well. But I could no longer tell you why it was built the way it was, and I hadn’t decided most of it beyond giving it some features to build. Nothing in review caught that, because no individual change was wrong. The product was fine but the codebase was no longer mine.
I’ve had the room to explore this at pace, and to get it wrong in ways I wouldn’t want a development squad to. None of it is settled, the ground is still moving, and I’d expect some of this to look naive in a year but the ways of working teams settle on will come from people experimenting now.
Three posts, never planned as a series
This is the third time I’ve written about this, and it wasn’t meant to be a series. Each time I’ve tried to draw a line under the topic, the next exploration has been more interesting than the last.
The first post argued the role was shifting, but that the tooling wasn’t quite good enough yet. Experienced engineers guarding every step, and not something I’d have recommended adopting wholesale.
The second followed directly from that: if the answer was tighter control, spec-driven development was the obvious thing to try. Constraints beat guidelines. Automation copies precedent. It was better but I still hit enough friction that I wasn’t ready to commit to the shift.
Six months on I am much more confident. What’s changed is partly the tooling, and partly that a lot more people are now experimenting with this seriously, which brings its own kind of maturity:
- shared principles
- guardrails that have been tried somewhere other than your own machine
- patterns that hold up because more than one person has leaned on them
The pace of change here only seems to be increasing, and I can’t cleanly separate “I got better at this” from “the models and harnesses got much better.” Both moved at the same time.
Better tools let you get away with being sloppy
When the models were weak, a poor requirement produced poor code. Visibly poor. You caught it in review because it looked like what it was. Now a poor requirement produces convincing code… well-structured, commented, but tested against the wrong thing. This is now a smell that’s very hard to catch without experience.
A thin spec still works, so you write the next one thinner. Nothing pushes back, because the output keeps looking fine.
The capability is real and worth being clear-eyed about. Anthropic reported that Stripe used Fable 5 to perform a codebase-wide migration across a 50-million-line Ruby codebase in a day, work Stripe estimated would otherwise have taken a team over two months. That’s a genuine step change. But look at what made it possible: Stripe had the test coverage to know when it worked, the engineering depth to define what success looked like, and people who could validate the result. The AI just compressed the execution.
The part that’s still yours
Tell the agents, don’t ask them.
Ask, and they will come up with things on your behalf. That isn’t a quirk of prompting style - it’s how the tool behaves. What I’ve repeatedly found is that if I leave a gap, the agent fills it rather than exposing it. From the surrounding code, from what’s conventional, from the last thing that worked.
So what you end up with is an architecture nobody chose, assembled from a hundred locally defensible decisions. That is precisely how to lose the codebase. I supplied the tickets. The agent supplied everything else, competently, one small decision at a time.
An agent working a ticket optimises for that ticket. It can read the system it’s landing in, but it has no view of where that system is meant to go unless you tell it. Direction, boundaries, what this should look like in six months, whether we should be building it at all. That’s the work nothing else in the loop is looking at.
Specs that carry outcomes, not just technical detail. This is the part I’ve had to consciously push on. An agent given a technical instruction will build that thing, and nobody in the loop is representing the user. If the spec says what someone is actually trying to achieve and what good looks like for them, the thousand small decisions the agent makes bend in roughly the right direction. If it only describes the implementation, they bend towards whatever is easiest to implement. In public service delivery, that might be the difference between a service that just satisfies the ticket and one that works for the person using it.
Instructions. Without explicit standards from you, an agent falls back on generic conventions and whatever precedent it can see. That’s a reasonable starting point and a poor standard, because it isn’t yours.
In my setup I think about this in two layers, and it’s worth keeping them straight. A CLAUDE.md carries the always-on context: your stack, your conventions, and the standards that apply to everything you build. Consistent, observable, secure, testable, maintainable, understandable as instructions the model works from rather than qualities you hope it happens to produce. Skills are the on-demand half: task-scoped workflows that load when they’re relevant eg a review process, a commit format, a scaffolding routine.
Between them, the model does less guessing at how your team builds, because you’ve told it. Standards get applied while the code is written instead of discovered in review.
I’d been building my own spec-driven agents and skills from scratch, and swapped to Addy Osmani’s open-source ones instead. These don’t bring anything new to software, and that’s the point: test-driven development with a red-green-refactor loop, thin vertical slices that stay small enough to roll back and review, contract-first interface design, Chesterton’s Fence before anything gets deleted. Proven best practice, applied at the point of generation rather than hoped for at review. They were solving the same problems I was, and solving them better, because they’d been used by more people against more problems than I’d ever get to on my own. This is the value of open source after all.
One caution: use skills you trust, and read and understand them before you use them. A stale or careless skill is a bad standard applied with consistency, and it will be applied to everything you build. Curating and maintaining skills is senior work.
Create human gates

If the problem is that nothing pushes back any more, the fix is to put the pushback in deliberately. Humans, humans, humans. What I ended up with is closer to directed autonomy than delegation, and it comes down to two decisions I keep: what gets built (and how), and what ships.
Gate one is the spec. Everything downstream is generated from it, so a plan that’s 80% right is more dangerous than one that’s obviously wrong. It’s coherent, plausible but wrong in ways that only surface later.
So interrogate it thoroughly. Show me the schema. Show me the flow you’re proposing. Where does this sit against the existing boundaries? What are you assuming that I haven’t told you? If the architecture or the paradigm is wrong, correct it there and keep correcting until you’re happy. This is the one place where asking is the right move, and it doesn’t contradict the point above on “tell, don’t ask”, you’re not asking the agent to decide anything, you’re asking it to show you what it has already assumed, so you can decide.
Once I’m happy, the ticket moves to a ready state, and that’s the only thing an agent will pick up. The queue is the permission model.
Then the work comes back, and this is where most of the machinery sits. A reviewer agent reads the diff against the original spec, with none of the implementing agent’s context to sway it. It cares less about bugs than about divergence: has this built what was specified, or something adjacent to it. By the time a change reaches me, the obvious gaps have already been argued out.
That design is deliberate, and needs to exist because something other than a human is writing the code.
The same way a developer shouldn’t approve their own pull request applies here. The agent that implements a ticket shouldn’t have access to the tooling that would let it mark its own homework. More reviewers only means more scrutiny if they fail differently, which is why the reviewer agents (acceptance criteria, code review, security) should each run in an isolated worktree and get only the diff and the spec.
Security belongs inside the loop, not bolted on after. The reviewer works the OWASP Top 10 ground you’d expect, injection, XSS, broken access control, sensitive data exposure, security misconfiguration, dependency auditing, but the important part is that it only reads the code and judges it against the spec and your standards.
None of that means skimping on the traditional checks. Linting, formatting, a real test suite against a real database, CI that has to go green. Those were good practices before agents and nothing has changed. Though don’t just trust a green suite: an agent that’s free to edit its own tests can make them pass by loosening the assertion rather than fixing the code, so “tests pass” and “spec met” can quietly diverge.
Gate two is the merge. Approving a merge means you own the code going in: that you’ve read it, that it does what the spec said it would, and that the pull request is what you expected to see. Not that it looked fine. If something goes wrong afterwards, “the agent wrote it” isn’t available to you, you merged it! On a run of any complexity I want to be able to say I’ve read this and I understand it, because in regulated delivery who signed off a change is not a rhetorical question, it’s an ownership one.
Autonomy is earned per piece of work. Where the outcome is well-defined and the verification is strong, I’ll let it run. Where the change is riskier or the spec is fuzzier, it drops back. This loop has held up well enough that it’s become the basis for how we’re thinking about building internally.
The fatigue nobody warns you about
Reviewing and spec writing creates a real fatigue, because the agents generate code so fast that it’s easy to accept it and move on. It bites hardest at gate one, where interrogating a plan properly is slower and duller than reading generated code, and the first draft is always plausible enough to wave through.
Sprint planning, ticket creation and PR review aren’t new. It’s the same review discipline we’ve always given the work, under conditions that make it considerably harder to sustain. What I’ve found is that throughput is now bounded by how fast I can specify and review, not by how fast code appears.
The signal I watch for is drift in the diff. When it’s too far gone, back it out and respecify. Negotiating with the agent from a drifted starting point is a sunk cost, you end up in a frustration loop, patching something built from a misunderstanding. Discard, respecify, regenerate the code. Rework is cheap now. Ambiguity is the expensive thing.
Somewhere in the middle of all this the unit of work changed. I stopped reviewing changes and started designing the system that produces them.
Handing this to a team
Nobody arrives in F1 (yes, I like F1) without years in the junior formulas. Agentic tooling hands over the car and skips the ladder, so the rigour has to stand in for the experience until it’s earned.
How you actually build that experience in people is the question I’m most interested in and least able to answer from six months on my own. It’s the thing I’d want to get right before moving a delivery team to work this way.
There’s a team-shape question underneath this too. If one engineer can now deliver what used to take several, a large sprint team starts to look less like capacity and more like complexity and coordination. My guess is that smaller teams are how you keep tight control at this velocity, though I’d want to see that hold on a real delivery before I’d say it with any confidence.
Where I’ve landed
I’m more confident than I was in either of the last two posts, and everything above is the reason why. The discipline turned out to be the set of things I refused to let the agent infer: what we’re building, who it’s for, how we build it, and what good looks like. Everything else I was willing to hand over, and the more I handed over, the more those four things mattered.
With the gates in place, sometimes it feels like the only thing holding me back is my own ability to dream up the next feature.
Six months ago I was asking whether this way of working could be trusted. I’ve stopped asking that. The question now is how you build the engineering system around it.
