I read more code than I write now

Bars of attention per reviewed change, shrinking across a working day

Somewhere in the last year the ratio flipped. I write less code than I used to and I read far more of it, and the reading is now the part of the day that decides whether the work is any good.

I have already written about what I let agents do and where my hands stay on the wheel. That post is about policy: blast radius, what gets delegated, what never does. This one is about the thing that policy quietly assumes, which is that I will review the output properly. That assumption deserves more scrutiny than I was giving it.

Reviewing a machine is not reviewing a colleague

I have reviewed colleagues’ code for years, and almost every instinct I built doing that transfers badly.

With a person, you accumulate a model of them. You learn that someone is meticulous about error handling and careless about naming, that someone else writes beautiful abstractions and forgets the empty case. That model is most of what makes review efficient: you know where to look hard and where to skim. It took years to build per person, and it was worth it.

An agent gives you nothing to build a model out of. It is not consistently strong anywhere or consistently weak anywhere. It can produce an elegant, correct, well-tested module and then, in the next file, hand you something subtly broken with exactly the same confident tone. There is no personality to learn, so there is no map of where to concentrate. Every diff is a stranger.

You also lose the conversation. Asking a colleague “why did you do it this way?” produces a reason, and the reason is often more informative than the code. Asking an agent produces an explanation, which is a different thing: fluent, plausible, generated after the fact, and equally available whether the code is right or wrong. You cannot use the answer as evidence.

And then there is volume. A colleague sends you a couple of hundred lines on a good day. An agent will hand you that before you finish your coffee, and the queue does not get tired.

Human bugs look like mistakes, generated bugs look like decisions

This is the part I would put on a wall.

When a person makes an error, it usually looks like an error. The variable name is wrong, the branch is obviously unfinished, the shape of the code does not quite fit the shape around it. Your eye snags. The snag is the mechanism: you notice something is off before you have consciously worked out what.

Generated code does not snag. It is idiomatic, consistently formatted, plausibly named and confidently structured, and when it is wrong it is wrong in a way that looks deliberate. A missing case does not read as an oversight, it reads as a considered scope decision. A subtly incorrect condition reads as a rule you have not yet understood. Fluency reads as intent, and intent disarms suspicion.

So the failure mode is not that I cannot understand the code. It is that understanding it feels complete when it is not. I have read a diff, followed every line, agreed with every line, and still missed that the whole thing quietly did not handle a case that no line mentioned.

Which points at the real difficulty: absence is invisible in a diff. You review what is present. The dangerous thing is usually what is not there, and nothing on the screen is going to snag your eye about a case nobody wrote.

What actually catches things

The techniques that survive contact with volume are the ones that do not rely on my eye being sharp.

Review against the invariant, not against the code. Before reading, I say what must stay true: money moves at most once, this list is never empty here, this identifier is unique per connection. Then I read looking for violations of that specific sentence. Reading a diff on its own terms means grading it against its own logic, which is exactly the logic in question.

Read the tests first, and ask whether they could fail. Generated tests have a habit of asserting what the code does rather than what it should do. A test suite that passes against a broken implementation is worse than no tests, because it converts your uncertainty into false confidence.

Look for what is missing, deliberately, as a separate pass. Not “is this right” but “what case would break this,” asked before I have been persuaded by how tidy it looks.

Run it. Reading is not verification, and I have to keep relearning that, because reading feels like verification when the code is this articulate.

The part nobody wants to say

Here is the uncomfortable half.

Writing code and reviewing code are not the same cognitive activity. Writing is generative. It has momentum, it tells you when you are confused, and being stuck is informative. Reviewing is vigilance: sustained attention to material that is usually fine, hunting for the rare case that is not.

Humans are famously bad at vigilance. This is not a moral failing or a personal one, it is a well-documented property of attention, and it is exactly why monitoring mostly-reliable automation is a known hard problem in aviation and driving long before it arrived in software. When something is right nine times out of ten, your scrutiny on the tenth is not what it was on the first. The tenth diff of the day gets less than the first. I have watched myself do it.

Skill helps with the first diff. It does not help much with the tenth, because the tenth is not failing on knowledge, it is failing on attention. And the volume that makes agents valuable is precisely what pushes you toward the tenth.

There is a second cost, slower and harder to notice. Writing code is how you find out what you do not understand. You cannot type your way through a design you have not grasped; it stops you. Reviewing lets you approve a design you could not have produced. Do that for long enough and you become the person who signs off on systems he could not rebuild, and that erosion is invisible right up until the day the thing breaks and understanding it is your job.

So, skill or erosion?

Both, and pretending otherwise is the mistake.

There is real skill in it, and I am better at it than I was: faster at recognising shapes, quicker to find the seam where a change meets old assumptions, more disciplined about stating invariants before reading. Those are genuine, transferable improvements.

But it is also erosion if you let volume set the pace, because vigilance does not scale and familiarity is bought with your hands.

What I do about it is unglamorous. I cap how much I will review in a sitting, and I stop when I notice I am skimming, because a skimmed review is worse than a deferred one: it produces the approval without the check. I keep writing things myself, including things an agent could have written faster, on the grounds that fluency is a muscle and not a credential. And for anything I would have to defend later, I derive it rather than approve it, which mostly means rebuilding the reasoning until I would have arrived at the same design.

The tell I have come to trust most: when I catch myself thinking “I could not have written this,” I stop. Sometimes that means it is good and I have learned something. Often it means I have stopped evaluating and started admiring, and admiration is not review.

No account, no tracking. One vote per reader.