Keep the Hard Parts

An AI fix for a flaky test comes back as a longer timeout, and the build goes green. Whether you merge it depends on whether you’ve ever chased a race condition yourself: if you have, you can see the timeout only makes the race rarer.
That isn’t a case against AI, and next time it may find the race too. But the call to merge was yours, made with judgment the AI didn’t give you. To me, getting good with AI means handing it as much as you can and keeping a few hard parts for yourself, on purpose, because doing hard things is where that judgment comes from.
Where AI is fastest
The gains are real: AI writes the boilerplate, the tests for edge cases you listed, and the glue code nobody wants to write. It gets you started in a new codebase, and big mechanical refactors stop being something you put off.
They’re biggest where you already know what good looks like. If you’ve written dozens of API handlers, you can check the next one in seconds, so the saving is real. Familiar means the kind of code, not the size of the repo: in METR’s 2025 trial, experienced developers working in their own large, mature projects were about 19% slower with AI tools while believing they were faster, and the time went into steering the tool and checking its work. On unfamiliar ground AI is just as fast, but you can’t tell correct from merely convincing, so the time moves into review or into the bug that ships.
Steering and checking
Using AI well is two jobs. Steering is deciding what to ask for, which approach to push, and when to throw its work away. Checking is deciding whether what comes back is right, which is more than whether the tests pass. Tests, types and invariants catch a lot, and you should lean on them, but somebody still decides which invariants matter and whether a green build means what it claims. Both jobs run on judgment: the debugging intuition that spots a timeout hiding a race, the feel for which layer a change belongs in, the sense that a tidy abstraction will hurt in a year. That comes from doing the work: the bug you chased for a day, the data model you got wrong and had to migrate.
Lisanne Bainbridge made the same point in 1983, about process plants and flight decks, in her paper Ironies of Automation. Automation leaves the operator two jobs, watching it and taking over when it fails. Both need skills that doing the task by hand used to build, and those skills fade without use. In her words, “by taking away the easy parts of his task, automation can make the difficult parts of the human operator’s task more difficult.”
With AI, reviewing a diff is the watching, and taking over is the bug the AI keeps going in circles on. The bugs that come back to you are the ones it couldn’t fix.
The compiler argument
The obvious reply is that we said this about compilers and IDEs, and it turned out fine. Few people write assembly anymore, and it rarely matters, because almost nobody checks the compiler’s output. A compiler follows written, heavily tested rules, the same code and flags give the same program, and when it’s wrong it’s wrong the same way every time. An IDE’s rename in a typed language is similar: a mechanical change you can mostly trust without reading.
You do review AI output, because it can be wrong in ways nothing flags, and the same request can come back different tomorrow. Reviewing code takes the skill that writing code built. Losing assembly didn’t make anyone worse at checking compilers, since almost nobody had to. Losing the habit of writing and debugging code does make you worse at checking AI. If AI output ever gets reliable enough that nobody reads it, the comparison will hold. It doesn’t yet.
Four things to do by hand
Hand AI everything else, but do these by hand:
- Debug a new kind of failure yourself first. The first race condition, memory leak or stale cache teaches you the most. After that, hand them over, and you’ll know a timeout from a fix.
- Write the first version of the data model or core abstraction. The decisions that hurt in a year get made there, and writing it is how you notice what’s awkward before the AI builds on it.
- Write the design note before the prompt. A few paragraphs on what you’re building, what you’re not, and why. If you can’t write it, the AI makes those decisions for you, and you won’t see which ones.
- Read every diff you merge. All of it, not the summary, so your picture of the codebase stays current and taking over doesn’t start cold.
The design note and the first version of the data model matter most. That’s where the decisions get made that would otherwise be made in the first plausible draft.
The slow way
Doing it yourself is slower, and some days it feels like a waste: the AI might have found the leak while you were still adding log lines. On a deadline, or on a team that counts output, that’s hard to defend. So keep the list short, do the hand work when the stakes are low rather than mid-incident, and drop an item once it stops teaching you anything.
Bainbridge also worried that the automated plants of her day, watched by former manual operators, were “riding on their skills, which later generations of operators cannot be expected to have.” On a team, that’s a choice about who gets the hard parts: if every hard bug goes to whoever is fastest with AI, the people still building judgment never chase one.
Takeaways
- AI speeds you up most where you can already tell good output from plausible output.
- Steering and checking AI run on judgment built by doing hard things, and it fades if you stop.
- Compilers are different: you don’t review their output. You review AI’s, with the skill that writing code built.
- Delegate freely, but keep the work that builds judgment: the first of each new kind of bug, the shape of the data, the reasons written down before the prompt, and the whole diff.
I’ll keep handing AI more of my work, as long as I’m still the one who can tell when it’s wrong.