The comment paradox: clean code in age of AI agents — uyar.design<br>uyar.design /the-comment-paradox.md<br>⎇ mainUTF-8LF
menu<br>~ / writing / the-comment-paradox.md<br>2026.06.19·5 min read·<br>The comment paradox: clean code in age of AI agents<br>Comments no longer speak only to developers. In AI-assisted coding, every stale TODO, misleading note, and commented-out block becomes part of the prompt. Clean comments help humans understand the code, but they now also protect AI assistants from repeating old mistakes.
Comments used to be a private conversation between developers. A note to the next maintainer. A warning about a strange edge case. A little apology for code that nobody had time to clean up.<br>That changed when AI coding assistants entered the editor.<br>Today, comments are no longer passive. They are part of the prompt. Every stale TODO, every commented-out block, every "temporary" workaround from three years ago gets fed into the model as context. The assistant may not treat it as dead text. It may treat it as intent.<br>That is the comment paradox: comments can explain code to humans, but they can also mislead the machine now helping write it.<br>Code is now read by two audiences<br>Good code still has to serve humans first. A developer should be able to read a function and understand what it does from the names, structure, and tests around it.<br>But AI agents add a second reader. They do not skim like humans. They consume the whole buffer: imports, functions, docstrings, TODOs, commented-out experiments, old examples, broken snippets, and emotional outbursts.<br>To an LLM, the difference between this:<br>ssl.wrap_socket(sock)<br>and this:<br># ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_SSLv3)<br>is not as clean as we want it to be. One executes. One does not. But both shape the model's next suggestion.<br>That matters because commented-out code often contains exactly the things we do not want copied: deprecated APIs, old assumptions, insecure defaults, half-finished refactors, and patterns we meant to delete.<br>Zombie code is no longer harmless<br>Commented-out code used to be mostly embarrassing. Now it is risky.<br>Recent research on "comment traps" found that defective commented-out code can push LLM-generated defect rates sharply upward. In some cases, defect rates reached 58.17 percent when bad commented code appeared in the model's context. [1]<br>The scary part is not that assistants copy bad snippets verbatim. Often they do something worse: they reconstruct the bad idea.<br>Even when defective fragments are incomplete or partially removed, models can infer the pattern and generate executable code that preserves the mistake. A human sees a dead block and thinks, "Ignore that." The model sees another signal.<br>Placement matters too. Some assistants are especially sensitive to code that appears after the cursor because of fill-in-the-middle completion. If stale code sits below the line you are editing, it may pull the generated code toward old behavior.<br>Prompting helps, but only a little. Telling the assistant "do not use the commented-out code" reduces some defects, but it does not erase the poisoned context. Clean input beats clever instruction.<br>Bad comments have types<br>Most teams know comments can rot. AI just makes the cost easier to see.<br>The obvious comment repeats the code:<br>count++; // increment count (pretty stupid example)
It adds noise without adding knowledge.
The lying comment is worse. It describes code that used to exist:<br>// Users are always sorted by signup date<br>return users.sort(byLastActiveAt);<br>This wastes human time and gives the model a false premise.
The hoarder comment preserves old code "just in case":<br>// const legacyClient = new LegacyBillingClient(apiKey);<br>const billingClient = new BillingClient(apiKey);<br>This is the dangerous one in LLM-assisted development. It tells the assistant that the old path still matters.
The rant comment records frustration instead of intent:<br>// This is garbage fix later<br>That may feel honest, but it gives nobody a next step. Open an issue, refactor it, or explain the constraint.<br>Best comment is often a better name<br>Before writing a comment, try to make the code need less explanation.<br># Instead of this:<br>d = 86400 # elapsed time
# write this:<br>SECONDS_PER_DAY = 86400<br># Instead of this:<br>if (status == 4):
# write this:<br>if status == STATUS_PUBLISHED:
Instead of explaining a dense conditional, extract it:<br>// Instead of this<br>if (user.current.isActive && user.current.emailVerified && !user.current.suspended) { sendWelcomeBackEmail(user);
//Write this<br>if(canRecieveWelcomeBackEmail(user)){<br>sendWelcomeBackEmail(user);
The goal is not silent code. The goal is code whose meaning lives in executable structure, not fragile side notes.<br>When comments still matter<br>Some comments earn their place.<br>Use comments to explain why the code is surprising. If a strange branch exists because a payment provider returns the wrong status code on Tuesdays, say so. If a slow-looking algorithm beats the clever one because of...