Comments: When Needed, When They Lie
What This Concept Is
Comments are a second channel of information that runs alongside code. They are unverified, unchecked by compilers or tests, and free to drift out of sync with the code they describe. Every comment carries a maintenance cost.
The Clean Code stance is not "comments are bad"; it is "comments are a necessary evil." Good comments earn their place by telling the reader something the code cannot easily say.
Categories of comments that usually earn their place:
- Intent / why. Why this approach was chosen when several would work.
- Warnings. Non-obvious hazards, thread-safety caveats, ordering requirements.
- External contracts. Why a method signature or data shape exists for reasons outside the file.
- Legal / licensing. Required by policy.
- TODO / FIXME. Linked to an issue, owned by someone.
Categories that usually fail:
- Redundant. Restating what the code already says.
- Misleading. Describing old behavior after the code was changed.
- Commented-out code. Version control exists for this.
- Noise. Banners, dividers, autogenerated
@param x the x. - Naming-failure comments. A comment explaining what a function does because the function is named badly.
Why It Matters Here
This module teaches you to spot naming failures, cohesion problems, and coupling leaks. Many of them hide behind apologetic comments. When you learn to read comments as evidence of something unsaid, design problems become visible earlier.
The corollary: the act of deleting a comment often reveals a rename or extraction that should have happened.
Concrete Example
A redundant comment hiding a naming failure:
// returns the list of users who are currently active
public List<User> getUsers() { ... }
Rename the method and the comment is no longer necessary:
public List<User> activeUsers() { ... }
A comment that earns its place:
# Retry budget is 3 because the upstream provider's rate-limiter
# drops bursts above 4 req/s and we already consume 1 r/s baseline.
RETRY_BUDGET = 3
The number 3 is not self-explanatory, the constraint is external, and no rename could encode it. This comment is useful.
A misleading comment is worse than no comment:
// sorts users by name
public List<User> sortedUsers() {
return users.stream().sorted(Comparator.comparing(User::createdAt)).toList();
}
A reviewer who trusts the comment will be wrong about behavior.
Common Confusion / Misconception
"Good code is self-documenting, so comments are always bad." No. Self-documenting code covers what and how well; it rarely covers why this was chosen over the alternative. When the reason is non-obvious, a comment is the cheapest way to record it.
A second misconception: "docstrings and comments are the same thing." API documentation (docstrings, Javadoc, Rustdoc) is part of an external contract and deserves different rules -- it describes the promise the module makes, not the internal decisions.
How To Use It
When reviewing a comment:
- Ask what the comment is claiming. Intent? Warning? Contract? Redundancy?
- If the comment repeats the code, try a rename or extract first and see if it goes away.
- If the comment is out of date, either fix it or delete it -- do not leave a lie.
- If the comment marks commented-out code, delete the code; history is in git.
- Comments explaining "why this number" or "why this order" should link to a ticket, spec, or external system when possible.
Check Yourself
- Why is a redundant comment often a naming failure in disguise?
- Why is a misleading comment worse than no comment at all?
- What kind of information cannot be encoded in code and legitimately needs a comment?
Mini Drill or Application
Pick any file with at least five comments. Do all four:
- Classify each comment:
intent / warning / contract / redundant / misleading / commented-out. - For
redundant, try a rename or extract that makes the comment disappear. - For
misleading, fix or delete. - For
commented-out, delete after confirming git history keeps it.