| name | comment-code |
| description | Write clear, useful in-code comments and documentation that capture what the code cannot say for itself. Use this skill whenever writing or revising comments, docstrings, interface/API documentation, function or class headers, or inline explanations — and whenever reviewing code for comment quality or being asked to "document," "add comments," "explain in comments," or improve existing comments. Apply it even when the request only mentions writing the code, since fresh code is the moment to document it well. |
Writing Good Code Comments
Comments exist to capture information that was in the writer's mind but cannot be expressed in the code itself. Code states what happens mechanically; comments supply intent, contracts, rationale, units, invariants, and the higher-level picture. Good comments make a system understandable to someone reading it for the first time and let abstractions hide complexity. The cost is small — typically a fraction of development time — and it pays back every time someone (including the original author, weeks later) needs to read or change the code.
The one rule that governs everything
Comment things that aren't obvious from the code. Before keeping any comment, ask: Could someone who has never seen this code reconstruct this comment just by reading the code next to it? If yes, the comment adds nothing — delete it or replace it with something that does add information. "Obvious" means obvious to a first-time reader, not to you.
This single test eliminates most bad comments and motivates most good ones.
Don't repeat the code
The most common worthless comment simply restates the line below it, at the same level of detail.
i = i + 1
count = len(items)
A close cousin is the comment that just recycles the words already in the name:
String[] getNormalizedResourceName(Request req) { ... }
The only word here not already in the signature is "from." This tells the reader nothing they couldn't read off the declaration. The fix is to use different words than the name, words that explain the meaning the name only hints at: What is a normalized resource name? What are the elements of the returned array? When in doubt, the giveaway is one-comment-per-line at the same granularity as the code.
Comments work at a different level of detail than the code
A comment at the same level as the code repeats it. Useful comments go either lower (more precise) or higher (more abstract) than the code.
Lower-level comments add precision
Most valuable on declarations — fields, arguments, return values — where a name and type leave real questions open. Fill in what the type can't say:
- Units — pixels? milliseconds? bytes?
- Boundaries — is the range inclusive or exclusive of the endpoints?
- Nulls — is null allowed, and what does it mean?
- Ownership — for a resource that must be freed/closed, who is responsible?
- Invariants — properties always true, e.g. "always contains at least one entry."
uint32_t offset;
uint32_t offset;
When documenting a variable, think nouns, not verbs: describe what it represents, not how various pieces of code modify it. A comment that narrates "set to true here, reset to false there" mirrors the code structure and grows stale; a comment stating the meaning ("true means a heartbeat has arrived since the timer was last reset") is shorter, more stable, and lets the reader infer the manipulation.
Higher-level comments enhance intuition
These omit detail to convey overall intent — what the code is for and how it fits the surrounding method. Common for blocks inside methods, loops, and interface comments.
With the high-level version a reader can predict almost everything the loop does and can judge whether the code is correct — something a detail-echoing comment never enables. Higher-level comments are harder to write because they require stepping back from detail. Ask: What is this code trying to do? What is the simplest thing that explains all of it? What is the most important thing about it?
A specially useful higher-level form is "how we get here" — the conditions under which a block or method runs, especially for code that only executes in unusual situations.
Comment categories — and which matter most
| Category | Where | Priority |
|---|
| Interface | Block immediately before a class/method/function declaration; defines the abstraction | Highest |
| Data-structure member | Next to a field/variable declaration | Highest |
| Implementation | Inside a method body | Often unnecessary |
| Cross-module | Describes dependencies spanning module boundaries | Rare but critical |
Every class should have an interface comment, every class variable a comment, and every method an interface comment. The occasional declaration is so self-evident (a trivial getter/setter) that nothing useful can be added — but that is rare, and it is cheaper to comment everything than to deliberate each time.
Interface comments define the abstraction
Interface documentation is what someone needs to use a class or method without reading its body. Keep it strictly separate from implementation detail.
For a class: describe the abstraction it provides, what each instance represents, and any limitations users must know (e.g. "not safe for concurrent access").
For a method, cover:
- A sentence or two on the behavior as the caller perceives it (the abstraction).
- Each argument and the return value, precisely — including constraints and dependencies between arguments.
- Side effects — any consequence affecting future behavior that isn't the return value (mutating internal state, writing a file).
- Exceptions it can raise.
- Preconditions the caller must satisfy first (minimize these, but document any that remain).
uint32_t Buffer::copy(uint32_t offset, uint32_t length, void* dest);
The reader never has to open the method body to call it correctly, and the comment says nothing about how the copy is implemented.
Red flag: implementation detail leaking into the interface
If a method's interface comment has to describe how it works internally, that's a warning sign — usually that the method is shallow (its interface is nearly as complex as its implementation). Move the how inside the method body, where it's clearly separated from the contract. Things users never need — private config parameters, names of internal RPCs, internal data structures, recovery that happens invisibly — do not belong in interface comments.
Implementation comments: what and why, not how
Most methods are short enough to need no implementation comments. When a method is long, help the reader by stating, at a higher level, what each major block accomplishes:
For a non-trivial loop, describe what one iteration does:
Also comment the why when it isn't visible: a tricky workaround, a hardware quirk, or a bug fix whose necessity isn't apparent. For a tracked bug, point to the issue instead of restating it:
Comment important local variables only when their uses are spread across a large span; if every use is visible within a few lines and the name is good, let the code speak.
Cross-module comments
Some design decisions span several classes (a protocol touching both sender and receiver; an error code requiring edits in many files). These are subtle and bug-prone, so they need documentation — but the hard part is putting it where developers will find it.
- If there's a natural central point everyone must touch (e.g. an enum that all changes pass through), put the cross-cutting notes right there, listing every other place that must change.
- If there's no obvious home, a central
designNotes file with clearly labeled sections works well: keep one authoritative copy, and from each relevant spot leave a short pointer (// See "Zombies" in designNotes). The trade-off is that notes living away from the code are harder to keep current — but a single findable copy beats duplicated or buried documentation.
Avoid duplicating the same explanation in many places; duplication is what makes documentation go stale.
Keeping comments fresh
Comments drift out of date only in proportion to how much the code changes, and large code changes already cost more than the matching comment edit. Two habits keep drift small: don't duplicate documentation, and keep each comment next to the code it describes. Code review is the natural place to catch stale comments.
Workflow when writing or revising comments
- Write the comment, then apply the test: could a first-time reader produce this from the adjacent code alone? If yes, rewrite or delete.
- For declarations, add the precision the type omits (units, bounds, null meaning, ownership, invariants). Describe what it represents, not how it's changed.
- For methods/classes, write the interface comment first — behavior, parameters, return, side effects, exceptions, preconditions — keeping implementation detail out.
- Inside long methods, label major blocks and complex loops at a higher level, and explain any non-obvious why.
- Use words different from the entity's name; reach for the level of detail above or below the code, never the same level.
- If you can't write an interface comment without describing the implementation, treat that as a design smell, not just a writing problem.