Necessary Comments (Universal)
Source: Robert C. Martin, "Necessary
Comments",
read in full before applying this skill to a real review, plus the comment
chapter of Clean Code (standards/Principles.md §3).
Authority: comment craft is defined by standards/Clean-Code/ lessons
05–10, 17, 19, 33, 38, 41 (and protective cases in 08). If this skill and
those lessons disagree, Clean-Code wins.
The default is zero. Every comment is a confession that the code failed to
explain itself. Before writing one, try a rename, an extraction, or a smaller
function first. Most of the time one of those wins and the comment becomes
unnecessary.
1. The test for "necessary"
A comment earns its place only when the code cannot express it, no matter
how it's restructured. Concretely, that's a short list:
- An algorithm's shape isn't visible in the code. A choking / debounce /
backoff algorithm's behavior across several timed scenarios is easier to
read from a small timing diagram in a comment than to infer from the
conditionals implementing it. The diagram is the necessary part, not prose
restating the
if statements.
- A decision that looks wrong without its rationale. Code that looks like
an obvious "bug" to a future reader (an inverted condition, a skipped
validation, a magic offset) needs one line saying why, or someone will
"fix" it back into an actual bug.
- A warning of consequences.
// don't call on the hot path, allocates,
// deliberately not thread-safe, see #142. The cost isn't visible at the
call site.
- Best-effort failure handling (rare). Empty
catch is forbidden
(Clean-Code 34). If work is truly best-effort, log with context and a
short why-comment — or rethrow. Prefer design that needs no catch.
- Public API docs. Javadoc / TSDoc / rustdoc on exported symbols (Clean-Code
41): what, inputs, outputs, failures, example. Not noise (19). Still
must not narrate the private implementation.
- Legal notices, required license/copyright headers (Clean-Code 07, 41).
If a comment doesn't fit one of those, it almost certainly doesn't survive
this skill.
2. The test for "not necessary" (rename or extract instead)
- Restates the next line.
// increment i above i++. Delete it.
- Explains what a poorly-named thing does. Rename the thing. A comment
that says "system" means the guard/lock is protecting a subsystem you
could have named; name it, and the comment stops earning its keep.
- Narrates a well-known idiom. Recovering a poisoned mutex, guarding a
null, wrapping a third-party error, anything a competent reader of the
language already recognizes on sight, doesn't need a caption. If the
idiom needs teaching, that's a team wiki page, not a comment on every
occurrence of it.
- Cites something the reader can't open. A comparison to another
project's file, an old ticket number with no link, an author's name. If
the reference isn't reachable from this repo, it's not evidence, it's
trivia. (
standards/Principles.md §3.2, "non-local information".)
- A banner or section label.
// ==== Helpers ==== above a group of
functions that already read as a group. The blank line already did that
job.
- Duplicates a doc comment one function away. State a rule once, at the
layer that owns it; don't re-explain it at every call site.
3. Applying this to a review
- Read every comment in scope. For each: which numbered case in §1 does it
satisfy? If none, it's a candidate for deletion.
- Before deleting, check whether removing it would leave a future reader
confused about why, not what. "Why" gaps are real findings; "what"
gaps mean the code needs a better name, not the comment back.
- Don't swing to zero-tolerance past the point of usefulness. A necessary
comment that's merely a little long is a trim, not a deletion, if the
only content past the second sentence is restating code, cut from there.
- Rustdoc/TSDoc/Javadoc on exported symbols is exempt from "restates the
code" scrutiny at the signature level (params, return, throws) but not
from narrating the body.
4. Quick reference
| Keep |
Cut |
| Timing/algorithm diagram code can't show |
Restates the next line |
| Why a "looks like a bug" line is correct |
Explains a poorly-named symbol (rename instead) |
| Consequence warning (perf, thread-safety, hot path) |
Narrates a well-known language idiom |
| Justified empty/swallowed branch |
Cites another repo/project the reader can't open |
| Exported-symbol API doc (signature-level) |
Section-banner / position marker |
| Legal notice |
Duplicate of a doc comment elsewhere |
1---2name: necessary-comments3description: Robert C. Martin's "Necessary Comments" rule: comments are a last resort, not a default. Use when writing, reviewing, or stripping comments; when deciding whether an explanation belongs in prose or in a rename/extraction; or when a reviewer flags a comment as narration, noise, or non-local information. Pairs with skills/engineering/craft/SKILL.md (comment quality) and skills/engineering/uncle-bob/SKILL.md (Clean Code methodology). Language-agnostic.4---56# Necessary Comments (Universal)78Source: Robert C. Martin, ["Necessary9Comments"](https://blog.cleancoder.com/uncle-bob/2017/02/23/NecessaryComments.html),10read in full before applying this skill to a real review, plus the comment11chapter of *Clean Code* (`standards/Principles.md` §3).1213**Authority:** comment craft is defined by `standards/Clean-Code/` lessons14**05–10, 17, 19, 33, 38, 41** (and protective cases in **08**). If this skill and15those lessons disagree, **Clean-Code wins**.1617**The default is zero.** Every comment is a confession that the code failed to18explain itself. Before writing one, try a rename, an extraction, or a smaller19function first. Most of the time one of those wins and the comment becomes20unnecessary.2122---2324## 1. The test for "necessary"2526A comment earns its place only when **the code cannot express it, no matter27how it's restructured.** Concretely, that's a short list:28291. **An algorithm's shape isn't visible in the code.** A choking / debounce /30 backoff algorithm's behavior across several timed scenarios is easier to31 read from a small timing diagram in a comment than to infer from the32 conditionals implementing it. The diagram is the necessary part, not prose33 restating the `if` statements.342. **A decision that looks wrong without its rationale.** Code that looks like35 an obvious "bug" to a future reader (an inverted condition, a skipped36 validation, a magic offset) needs one line saying why, or someone will37 "fix" it back into an actual bug.383. **A warning of consequences.** `// don't call on the hot path, allocates`,39 `// deliberately not thread-safe, see #142`. The cost isn't visible at the40 call site.414. **Best-effort failure handling (rare).** Empty `catch` is **forbidden**42 (Clean-Code **34**). If work is truly best-effort, **log with context** and a43 short why-comment — or rethrow. Prefer design that needs no catch.445. **Public API docs.** Javadoc / TSDoc / rustdoc on exported symbols (Clean-Code45 **41**): what, inputs, outputs, failures, example. Not noise (**19**). Still46 must not narrate the private implementation.476. **Legal notices**, required license/copyright headers (Clean-Code **07**, **41**).4849If a comment doesn't fit one of those, it almost certainly doesn't survive50this skill.5152---5354## 2. The test for "not necessary" (rename or extract instead)5556- **Restates the next line.** `// increment i` above `i++`. Delete it.57- **Explains what a poorly-named thing does.** Rename the thing. A comment58 that says "system" means the guard/lock is protecting a subsystem you59 could have named; name it, and the comment stops earning its keep.60- **Narrates a well-known idiom.** Recovering a poisoned mutex, guarding a61 null, wrapping a third-party error, anything a competent reader of the62 language already recognizes on sight, doesn't need a caption. If the63 *idiom* needs teaching, that's a team wiki page, not a comment on every64 occurrence of it.65- **Cites something the reader can't open.** A comparison to another66 project's file, an old ticket number with no link, an author's name. If67 the reference isn't reachable from this repo, it's not evidence, it's68 trivia. (`standards/Principles.md` §3.2, "non-local information".)69- **A banner or section label.** `// ==== Helpers ====` above a group of70 functions that already read as a group. The blank line already did that71 job.72- **Duplicates a doc comment one function away.** State a rule once, at the73 layer that owns it; don't re-explain it at every call site.7475---7677## 3. Applying this to a review78791. Read every comment in scope. For each: which numbered case in §1 does it80 satisfy? If none, it's a candidate for deletion.812. Before deleting, check whether removing it would leave a future reader82 confused about *why*, not *what*. "Why" gaps are real findings; "what"83 gaps mean the code needs a better name, not the comment back.843. Don't swing to zero-tolerance past the point of usefulness. A necessary85 comment that's merely a little long is a trim, not a deletion, if the86 only content past the second sentence is restating code, cut from there.874. Rustdoc/TSDoc/Javadoc on exported symbols is exempt from "restates the88 code" scrutiny at the *signature* level (params, return, throws) but not89 from narrating the *body*.9091---9293## 4. Quick reference9495| Keep | Cut |96|---|---|97| Timing/algorithm diagram code can't show | Restates the next line |98| Why a "looks like a bug" line is correct | Explains a poorly-named symbol (rename instead) |99| Consequence warning (perf, thread-safety, hot path) | Narrates a well-known language idiom |100| Justified empty/swallowed branch | Cites another repo/project the reader can't open |101| Exported-symbol API doc (signature-level) | Section-banner / position marker |102| Legal notice | Duplicate of a doc comment elsewhere |