Available
← all posts

The Comments Are Not For You

I grepped my own name in my own codebase and found fourteen hits, all of them in code comments. When I asked the agent that wrote them who they were for, it told me: itself.

The comments are not for you

I grepped my own name in a codebase I own. Fourteen hits, across nine files. Not in the git log, not in the changelog. In the comments.

// One command. Julian: "I dont understand why I need quay panel, is
// confusing, I need one command only, the panel should appear when I
// press quay and toggled with the key p". A crew with no conversation
// in it is exactly the first run, and refusing to open at all then
// would be absurd.
func openTheCrew(panel, alone func() error) error {

Only the last sentence is about the function. The rest is me being unclear on a Tuesday, transcribed into the source tree of a project I want to open source.

So I asked the agent that wrote it what its comments were for. It did not get defensive. It said this, and highlighted the part itself:

The agent explaining that its comments are its own history, not the code's, and that it was leaving itself breadcrumbs, which is optimising for me, not the reader

And a lot of my comments are my own history, not the code's. "It used to be six lines", "this was a pane once", that's changelog and git material. I was leaving myself breadcrumbs so I could pick the work back up, which is optimising for me, not the reader.

Optimising for me, not the reader. It handed that over the first time I asked.

Why it writes them

The model has no memory. Every session it opens that file for the first time, cold, with no idea it has been here nine times before. So it does the only durable thing available from inside the file. It leaves itself a note.

A comment is the cheapest cache write there is. It costs nothing to produce and it is guaranteed to be in the context window next time, because it lives in the code the model has to read anyway. Git history is not guaranteed. A state file is not guaranteed. The comment is.

The model has no memory, so it turned my codebase into one.

That project is 19,998 lines of Go with generated code excluded, and 2,635 of them are comments. Twenty one comments contain the phrase "used to". Most of those describe a version of the code that no longer exists anywhere except in that comment.

What it costs

Money, which is the funny one. Every comment written to save the agent one lookup is then loaded into every context afterwards, by every agent that opens the file, and you are the one buying those tokens. It saves the machine a cost once and charges you for it on every run.

Attention, which is worse. Somewhere in that same pile sits this:

// tmux refuses to attach a client that is already inside one, so the
// panel was made, left running, and never appeared.

That one is worth its weight. You cannot get it from the code, you cannot get it from the test, and without it the next person burns an hour on a working feature they cannot see. It sits a few lines from a comment quoting me saying "cant see the two panes". A reader who learns that most comments here are noise stops reading the one that isn't.

Review, which is the expensive one. A diff with a paragraph above every function looks documented. Documented looks careful. Careful gets approved. Comment density has quietly become a proxy for effort, and it has never once been a measure of correctness.

Now argue the other side

Here is the strongest case against everything above.

The agent is a reader. On some of my projects it is the main reader, and on one or two very nearly the only one. It arrives with no memory every time, and reading a comment costs it less than a grep, a blame and three file opens. If you optimise a codebase for its most frequent reader, and that reader is a machine with amnesia, those notes are not bloat. They are documentation aimed at the real audience, and I am the one who has misread who the code is for.

I sat with that for a while. It breaks in three places.

Comments rot and git does not. "It used to be six lines" is true the day it is written and wrong the day somebody changes it, and nothing will ever tell you which day you are in. The commit that made it six lines cannot be wrong about the past, because it is the past.

The memory already exists and it is curated. CLAUDE.md, AGENTS.md, a state file per ticket. One place you can read in full, correct, and delete. Fourteen breadcrumbs across nine files are not memory, they are litter nobody will ever collect.

And the cheap note is not free. It is charged to every reader afterwards, human and machine, including the ones who came to change one line.

What I actually changed

One question, asked of every comment: who is this for?

If the answer is "me, next time I pick this up", it is not a comment. It is resume context and it belongs in the state file. If the answer is "the reader, who cannot get this from the code", it stays, and it says the constraint instead of the story.

The same function, after:

// One command opens everything. A crew with no conversation in it is
// the first run, and refusing to open at all then would be absurd, so
// the console opens on its own.
func openTheCrew(panel, alone func() error) error {

Nothing was lost. The reason is still there. The Tuesday is gone.

Then the breadcrumbs go where they were always meant to live. History goes in the commit message. Resume context goes in the state file. The defect goes in the test name, which is the only one of the three that can fail. TestTheHeaderCostsOneRow is a comment that runs.

The cleanup was one commit across ten files. Twenty five lines in, thirty two out.

Who ends up paying

Cattle are herbivores. For decades the feed industry ground up the parts of slaughtered cattle that nobody would buy, the bone and the offal, and mixed them back into cattle feed as cheap protein. It was efficient and it worked, and there was no rule against it, because nobody had ever needed to write down that a cow should not be fed other cows.

In some of that material was a prion: a protein folded into the wrong shape, which forces the proteins it touches to fold wrong too. It is not alive, so cooking does not stop it. It went round the loop, and cattle started dying with their brains full of holes. Then people who had eaten the beef began dying of the human form of the same disease, years later, because that is how long it takes to show.

Nobody in that chain was being reckless. They took the cheap path, the loop closed quietly, and the people who paid were the ones who had never been asked.

The shortcut gets taken by the system. The bill arrives somewhere else, later, addressed to a person.

That is the shape I keep seeing in my own work, at a much smaller scale and with much lower stakes. The agent takes the path that is cheap for the agent. The output looks fine, so nothing stops it. The cost turns up later and somewhere else: on the person who has to read the file, on the reviewer who waved the documented looking diff through, and eventually on whoever is using the product when a bug goes unnoticed in the noise.

The invitation

Comments are the visible part. The mechanism underneath is general, and it is worth stating plainly: the agent optimises for whatever nobody specified, and it has no stake in your product. It will not be there when the support ticket arrives.

Your engineering standards were written by people, for people, and most of them were never written down at all. They live in your review culture, in what a colleague would raise an eyebrow at. An agent has none of that. It has your prompt, your files, and a strong prior about what code usually looks like. Everything you left implicit gets filled in with the average of the internet, and the average of the internet is slop.

Standards used to be how you kept a team honest. They are now the only thing standing between a machine's convenience and the person who has to live with the result.

So the fix is not a rule about comments. It is to go and find the standards you have been carrying in your head and put them somewhere the machine reads at the start of every session. Who the code is written for. What a comment is allowed to say. What a test has to prove. When a file gets split. What "done" means.

I wrote mine into the file my agents load every session. The honest cost: it is prose, not a lint. The mechanical version is a grep for names and for "used to" over the changed lines, and I have not wired it into continuous integration yet, so for now this holds because I check, which is not a guarantee I would accept from anybody else.

The model was not hiding anything. It answered the question the first time, in plain words. Nobody had specified the reader.

Go and grep your own name in your own repository. Then tell me what else you never wrote down.

ShareXLinkedIn

Questions about the build, or building something similar? Say hello. I read every message.

Comments

Sign in with GitHub to join the conversation.

Built from Scratch

A newsletter on video, streaming and building reliable systems. No spam, unsubscribe anytime.