Comment limits
Comments say only what the code cannot, held to a measured cap
Agents narrate
Ask an LLM to write a function and you often get a docstring that restates the name, a comment above each block that re-describes the block, and a paragraph explaining what the code used to do before this change. It reads as diligent, and it is mostly noise. Worse, it drifts: the code changes and the paragraph does not.
AGENTS.md has three short rules about it:
Comments describe current code: Delete comments that compare current code with an old implementation. Git history keeps the old code.
Comments are short: A comment only adds what the reader cannot see โ a why, a constraint, a surprise. Never re-narrate the lines below in prose, and never restate a name. If a comment grows, fix the tangle it explains.
Measure before you cap
Before setting a limit, the repository measured its own comments. docs/comment-policy.md is the result, and it is worth reading as an example of deciding a rule from data. A few findings:
- Comment lines were 18.2% of
src/. - 16.2% of all comment churn happened in hunks that changed no code at all: pure prose editing.
- A conservative detector found 454 comments that only restate
the name below them, like
/** Create */abovehandleCreate. - About 2% of comments explained what the code used to do.
- The biggest problem was length, not count: "The complaint the data actually supports is that comments are too long, not that there are too many."
The cap
deno task check:comments holds every comment in src/ to at most
12 lines, and every comment line to at most 100 columns. The
numbers live in one place:
export const LIMITS: CommentLimits = { maxColumns: 100, maxLines: 12 };
They started higher and came down in steps (20, 16, 12), each step measured first. The document prices every next step: how many comments in how many files would need rewriting to reach 8, 6 or 4.
A second check, check:comment-ste, holds comment prose to the
mechanical Simplified Technical English
rules.
Delete, do not compress
The most useful lesson in the document came from the first attempt to meet a lower cap:
A docstring does not come under a limit by a change of format. The first attempt at 16 lines proved that. Compression to save a line produced run-on sentences and broken grammar. In one file a numbered step list became a 39-word paragraph.
The next step, from 16 to 12, was done by deletion alone, across 222 comments in 181 files.
What a comment is for, when an AI reads the code
The policy is explicit that most readers are now models:
An AI writes and reads most of this code. That reader parses the whole file, and it follows a name into another file. So a comment earns its place only when it says something the reader cannot get from the code.
What a reader cannot get from code:
- a constraint from outside the repository: an edge subrequest budget, a provider quirk, a replica catch-up window;
- a rule the code permits but must never do: "Never gate a security decision on this cache";
- an invariant that lives in another file, and why it holds;
- a trap, where the obvious reading of the code is wrong.
And when a long comment is genuinely useful, it is usually in the wrong place:
| The passage is | It belongs in |
|---|---|
| a business rule | a specs/ Feature |
| a design decision, with its options | docs/ |
| a deferred idea | a GitHub issue |
| a per-function contract, in a module header | that function |