Code is written for humans to read
Stop Writing and Reading Code
You're never going to maintain the code again, so stop spending your day typing it and reading it line by line.
"The most important thing to learn when learning to code is the absolutely astonishing beauty of code."
I wrote that, in an essay years ago about why we taught Ruby first. I said programming is an art, like poetry or music. I said students should fall deeply and profoundly in love with code.
I meant it. You fall in love with code by writing it and reading it.
I've mostly stopped doing both.
You're never going to maintain the code you ship this year. An agent is. So why are you still typing it?
The same feature, two ways
Last month I added series support to this blog, the thing that lets this post belong to a collection with a part number and prev/next links.
The old way. Open the editor. Generate the migration. Write the Series model and think hard about whether the scope should be ordered or by_part. Write model specs for the ordering and the draft filtering. Write the controller, the views, the partial for the "Part 3 of 9" label. Refactor twice because the first version was ugly. Open a PR. Someone reads every line, leaves comments about naming, and we go back and forth for a day.
The new way. I wrote a plan. It's a markdown file in plans/, and it reads like a spec because it is one:
## Behavior
- A post can belong to one series, with a part number
- Series page lists parts in order, drafts hidden from public
- Each post in a series shows prev/next links and a "Part 3 of 9" label
- Admin can reorder parts without touching each post
## Verify
- System spec: visitor reads part 1, clicks next, lands on part 2
- System spec: draft part never appears for a logged-out visitor
- Screenshot series page, light and dark, desktop and phone widthThen three agents worked in parallel: one on the data layer and admin, one on the public views, one writing system specs straight from the behavior section. When they finished, the specs were green and an agent had driven a real browser through the flow and handed me screenshots.
I looked at the screenshots. I clicked through it on localhost. I tried to break it by publishing a part out of order and unpublishing the middle one. I read the specs to make sure they tested what I actually cared about.
I never opened app/models/series.rb. I still haven't. I don't know what the scope is called, and I don't care. If it needs to change, I won't be the one changing it.
Where the day went:
| The old way | The new way | |
|---|---|---|
| Deciding what it should do | A little, mostly in my head | Most of it, written down in the plan |
| Typing code | Most of the day | None |
| Reading code | Hours of line-by-line PR review | The specs, plus anything touching auth or data |
| Verifying behavior | A quick click-through before merge | System specs, screenshots, and me trying to break it |
| Waiting | A day of review back-and-forth | Agents running while I plan the next thing |
The old way was genuinely better at one thing: I knew every line. When something broke at 2am, the map was in my head. But when something breaks now, an agent reads the whole codebase and finds it faster than my memory ever did.
Reading diffs is the new bottleneck
When agents write the code, the slowest thing in the loop is me reading. An agent can produce a working feature in the time it takes me to carefully review 400 lines of diff. If I insist on reading every line, I've taken a system that runs many workstreams in parallel and throttled it to the speed of one guy squinting at a monitor.
And what am I catching when I read line by line? Mostly taste. "I'd have named this differently." "This could be extracted." Those mattered when a human was going to open this file in six months and need to understand it fast. That human is not coming. The next reader is an agent that reads the whole file in a second and has no opinion about my method names.
So the review moved. I review the plan before any code exists, because that's where the real decisions are. I review the tests, because they're the contract. And I review the outcome: does the app do what I said, in the browser, with real data.
"But security. But correctness."
Everyone has shipped a bug that passed code review. Line-by-line review was never the safety net we pretended it was. Humans skim. Humans get tired on file 14 of 20.
What I verify now is behavior and boundaries, not syntax.
Correctness is behavior. If the system spec says a logged-out visitor can't see a draft, and that spec passes in a real browser, that's stronger evidence than me reading a where(published: true) and nodding. If I don't trust the spec, I read the spec. It's short, and it's the thing that actually matters.
Security is boundaries. What can an unauthenticated request reach? What params get accepted? What gets rendered unescaped? What does the migration drop? I have agents whose whole job is to audit a diff for exactly those things, and I run a security scan before anything ships. When something touches auth, payments, or deletes data, I absolutely open the file. That's a small, known set of places. I go look at them on purpose instead of reading everything by default and hoping I notice.
If you're writing cryptography, or firmware, or anything where one wrong line kills someone, read the code. Read it twice. Most of us are building web apps, though, and we've been treating every CRUD form like it was a pacemaker.
Who is the programmer now?
In that same essay I said Ruby was the only language invented for the programmer's happiness, that it "values you, the programmer, over the machine." That's why beautiful code mattered. The reader was a person, and the person's experience was the point.
The reader changed. The code still has to run, be secure, be fast enough, and be something an agent can work with easily (more on that in refactoring for the agent). What stopped mattering is whether I find it pleasant to read. I'm not the one reading it.
I haven't stopped loving the craft. I moved it. The beauty I care about now is in the plan: a clear statement of what should exist and how I'll know it works. Every hour I spend reading a diff is an hour I'm not spending on that, or on whether the feature should exist at all. Those were always the better questions. I never had time for them because I was busy typing.
This is part one of Rethink Everything. Next up: why I think a rewrite beats a massive refactor now, another thing I used to argue against loudly.