For AI coding to work well, three things have to line up
The model, the engineer driving it, and the codebase underneath it. Get one wrong and the other two can't save you - notes from reviewing a few terabytes of real coding sessions.
This is from reviewing a few terabytes of coding sessions helping teams with their AI coding setup. There’s a pattern that shows up constantly: three things have to line up for AI coding to actually work well, and teams that struggle are almost always weak in one of them, not all three.
The model
The recent models are genuinely good. Use a frontier coding model and you get a tick in this box, more or less by default.
But “we have AI coding tools” doesn’t mean much on its own. I was talking to a CTO recently whose team had only been given the cheap tier of GitHub Copilot access - which in practice means access to the small models, not the frontier ones. Why? Because the team was already sceptical of the value, and hadn’t pushed him for access to anything better.
I get the instinct. This stuff isn’t cheap, and everyone starts out half-wanting it to be hype so they can stop thinking about it. But you can’t actually assess whether AI coding works for your team on a budget model. The gap between a small model and a frontier one isn’t a rounding error - it’s most of the difference people are arguing about when they say “AI coding doesn’t work here.”
The engineer
The most effective “harness” for a powerful model is still an experienced engineer working in close contact with it. The models are smart, but they’re also dumb in specific, recognisable ways. Two real examples, paraphrased from actual sessions:
“Tests can’t cover that because there’s no testing set up for this project, so we’ll just have to fix it.”
- maybe the answer is to set up tests, not route around their absence.
“We’re using 0.7.9 of
<library>, which doesn’t support that parameter, so we’ll add workarounds at each call site.”
- maybe just bump the library to 0.8.0.
There are countless variations on this. The model will reliably take the shortest path to green, and the shortest path is very often the wrong one architecturally. “Experienced” here doesn’t just mean traditionally-experienced - it means experienced with the specific nuance of working with an agent, which is its own skill and takes time to build.
The codebase
This is the biggest one, and the hardest to summarise in a few lines. Fundamentally you’re trying to make the codebase more predictable and give it better correctness guarantees, because the agent’s effectiveness is bounded by how legible the codebase is to it. Some concrete pieces:
- tight enough files and modules that the relevant context actually fits in the model’s window
- predictable, consistent naming and structure, so the agent can find what it’s looking for
- enough tests that the agent can tell when it broke something
- few enough tests that it isn’t spending all its time waiting on them
- static checking - lots of it
- minimal side effects and global state
- minimal metaprogramming
- one consistent way of doing any given thing
- idioms native to the language, rather than house conventions invented from scratch
- documentation, especially diagrams that show how pieces connect
Each of these deserves its own deep dive - there are plenty of concrete examples behind every bullet - but the short version is: the agent is reading your codebase the same way it reads a prompt, and codebases that are ambiguous to a new human hire are ambiguous to an agent too, just faster and more expensively.
The bottom line
If AI coding tools aren’t working for your legacy setup, that doesn’t mean they won’t. It’s absolutely possible to fix all three: get the right model, skill the team up on how to actually work with it, and refactor the codebase into something more legible. And it doesn’t have to be a big-bang rewrite - one small step at a time compounds into a genuinely different codebase within a few weeks.
But you do have to work on all three. Fixing only the model, or only the codebase, or only the engineer, and expecting the other two to catch up on their own, is the most common way teams end up disappointed.