When Reading AI Code Is Not Reviewing It
Listen to this article (experimental) · 08:31

A developer I can picture clearly, because a version of him is everywhere right now, has a workflow that looks responsible. He lets the model write the code, and he reads the diff before he accepts it. He is reviewing. Except the diffs keep getting longer, the reading keeps getting faster, and one afternoon he notices he has been accepting code he could not now change if he had to. The projects the machine mostly built are the ones he can barely navigate. A project he wrote by hand three years ago still feels like his own house. Somewhere in there, reading stopped being reviewing, and nothing announced it.
Sometimes reading is the whole job
Here is what the strong version of this warning gets wrong. For a large share of what you accept in a day, reading the diff is the entire job, and running it yourself would be a performance. A rename the compiler already proved. A dependency bump. A config value, a copy change, a comment. Boilerplate you have written a hundred times and can check by its shape. A block of generated tests, where reading is the more valuable pass anyway, because reading is what catches the test that asserts nothing and still goes green.
Careful reading is not a weak instrument. Long before any of this, code inspection found more defects per hour than testing did, and it still does. So the point is not "always run the code." The point is that reading and running are blind to different bugs, and most of the argument people have is really an argument about which blindness they are living inside.
The real test is whether you can predict it
The question was never reading against running. It is whether you can predict what the code does without running it.
For one category of change you can, and reading is enough. For another the correctness is not in the text in front of you. It lives between the lines, in the interleaving of two threads the diff can only show you one at a time. It lives under load, in the query that is fine on ten rows and fatal on ten million. It lives at the boundary, in the off-by-one and the error path the happy case walks straight past. It lives across the wire, in whether the new piece is even wired to the thing that calls it. It lives in the negative space, in the authorization check that is not there and the exception that is quietly swallowed, which is to say in a line that never appears in the diff because it was never written. You cannot read your way to a bug that only exists at runtime.
AI makes "looks right" mean less
AI sharpens this in one specific way. A reviewer approves code using signals that stand in for correctness. It reads as idiomatic. It looks confident. It resembles code that worked before. A language model is a machine tuned to produce exactly those signals. It is very good at looking right.
So the oldest shortcut in review, "this looks fine," has never been weaker evidence that the thing is fine. The polish is not reassurance. It is the risk, because it is aimed precisely at the part of you that was going to wave the change through. There is research showing that people given a capable assistant wrote less secure code and felt more sure it was secure. The confidence moves the opposite way from the danger, and clean generated code is the most disarming input a reviewer can be handed.
The machine can run it too
Running the code is not only something you do by hand now. The agent can run it for you and hand back the logs, a screenshot, a paragraph explaining what it changed and why it is confident. That is real, and for a lot of changes it is the whole of it: the tests went green while you watched, the screenshot shows the button where it belongs, and you are finished sooner than you would have been alone.
But look at what happened there. That evidence was produced by the same kind of system that wrote the code, aimed at the same target, which is looking right. A passing log you did not watch run is a claim, not a fact. You can even put the agent on watch, over the logs, the database, the calls that went out, and ask whether everything lined up, and that is real leverage. It is also where I have caught a model not truly looking, answering with what it expected to find. So handing the run to the agent does not remove the review. It moves it up one level, from the diff to the evidence. When it tells you the logs show X, open the logs and find X. Treat "it worked, here is the proof" as a claim to confirm, the same way you would check a number before you let a model's paragraph keep it. It is the same check a researcher skips when a made-up citation ends up in a published paper.
Which leads to the part that is almost funny. For plenty of small changes, the fastest and most trustworthy thing you can do is run it yourself. Reading a wall of logs and a confident summary can cost you more than opening the thing and looking with your own eyes. Running it is not the slow, dutiful option you fall back on when reading fails. Half the time it is the quick one.
The failure most people hit is volume
The failure most people actually hit is not subtle at all. It is volume.
Past a few hundred lines, any human review decays into a scan, and an agent will give you a few hundred lines whenever you ask. Review-only work is the most tiring shape the job comes in, because you are reconstructing someone else's intent, in a style that is not yours, at a pace you did not set. So it collapses, quietly, into acceptance. Nobody decides to stop reviewing. You start seeing the code instead of understanding it, and from the inside those two feel identical. The constraint that matters is not how much the model can write. It is how much you can actually hold. Let it write past that and you are not reviewing, whatever the screen says you are doing.
Match the depth to the blast radius
That turns the discipline into something more useful than "read harder." Match the depth of the review to the blast radius, and to how reversible the change is.
A small, contained change that is easy to roll back deserves a fast read and a merge. A migration, a public interface, a deletion, anything that touches money or access, earns the slow kind of review: run it, trace it, try to break it, whether you do that yourself or make the agent show its work and then check what it actually did, no matter who or what wrote it. Reading is enough exactly when the ways the change can fail are the ways your other gates already catch: the compiler, the types, the tests that run in CI whether or not you run them by hand. It stops being enough the moment the failure that would actually hurt you is one that only running the thing would reveal. Spending the same scrutiny on every diff is not diligence. It is a refusal to decide where the risk is.
What you hand away
There is a quieter cost under the technical one. Accepting code you have not understood moves you, one click at a time, from the author of your product to its consumer. You keep the credit and you keep the responsibility, the pager still rings for you, but you have given away the understanding that both of those rest on. People describe building an app in an afternoon and feeling strangely flat, then spending two weeks making it actually work and feeling like themselves again. That is not nostalgia. That is the difference between owning the thing with your name on it and holding a copy you cannot open.
I build tools that lean all the way into generation. One turns a sentence into a plan and opens its own pull requests. Another governs whole fleets of agents. I am not here to tell anyone to write less AI code. But every one of those tools assumes, at the far end, a person who reviews at the depth the change deserves, and a gate you only glance through is not a gate. The machine can produce the diff. It cannot produce the part where you understood it.
So review by sight all you like, for the changes where sight is all there is to it. Just keep a clear line between the diff you have understood and the diff you have only seen. The first one is yours. The second one becomes yours the moment it breaks, and it will not care that it looked fine.
Questions this article answers
Is reading the diff enough to review AI-generated code?
For a large share of what you accept in a day, yes, and running it would be a performance: a rename the compiler already proved, a dependency bump, a config value, a copy change, boilerplate you can check by its shape. Generated tests are the clearest case, because reading is what catches the test that asserts nothing and still goes green. It stops being enough when the correctness is not in the text in front of you: the interleaving of two threads, the query that is fine on ten rows and fatal on ten million, the error path the happy case walks past, or the authorization check that never appears in the diff because it was never written.
The agent ran the tests and gave me passing logs and a screenshot, why check any further?
Because that evidence was produced by the same kind of system that wrote the code, aimed at the same target, which is looking right. A passing log you did not watch run is a claim, not a fact. Handing the run to the agent does not remove the review, it moves it up one level, from the diff to the evidence: when it tells you the logs show something, open the logs and find it. I have caught a model not truly looking and answering with what it expected to find, which is the same check a researcher skips when a made-up citation ends up in a published paper.
How much AI code can I actually review before it stops being review?
Less than an agent will hand you, since it will produce a few hundred lines whenever you ask and any human review decays into a scan past that point. Review-only work is the most tiring shape the job comes in, because you are reconstructing someone else's intent, in a style that is not yours, at a pace you did not set. Nobody decides to stop reviewing. You start seeing the code instead of understanding it, and from the inside those two feel identical.
Is it not safer to review every change at full depth?
No. Spending the same scrutiny on every diff is not diligence, it is a refusal to decide where the risk is. Match the depth to the blast radius and to how reversible the change is: a small, contained change that rolls back easily deserves a fast read and a merge, while a migration, a public interface, a deletion, or anything touching money or access earns the slow kind, where you run it, trace it and try to break it. Running it yourself is not the dutiful fallback either, since opening the thing and looking is often quicker than reading a wall of logs and a confident summary.
Read next
AIWhen the AI Has to Film Its Own Review
When a pull request is raised, mine or one I have to review, I now ask the agent to run it on my machine, try to break it and film itself doing it. A paragraph can claim a test happened. A recording has to show it, and deciding what it means is still my job.
AIThe Picture I Am Holding. What a Year of Building and Writing Adds Up To
Twenty-six essays, six products, one consulting practice, and a season of measuring things I could have merely claimed. Read back as one trace, they turn out to run a single mechanism: AI makes the cheap half of everything free, and the expensive half changes address. This is the map of the addresses (judgment, recourse, attention, the person), and why my faith recognized the territory before I did.