Comments and prose
A wrong type fails the typecheck lane and a broken link fails the docs lane. No lane reads a comment, so a comment can contradict the line beneath it for a year while every lane stays green.
These rules are written for source files, and they apply to a commit message, a documentation page and a pull request description too, since nothing checks the prose in those either. Markdown here is never hand-wrapped, and Code style names the setting that enforces it.
Comments that earn their line
Section titled “Comments that earn their line”Two things are worth a comment: a constraint the code cannot show, and a fact that must not get lost. The rest is noise. A file thick with restatement teaches the reader to skim, and the one comment that mattered is skimmed with the rest.
- Never retell the code. If the comment paraphrases the statement under it, delete the comment. The paraphrase goes wrong the moment the statement changes, and the next reader believes the paraphrase.
- Never narrate a platform fact the line already states.
clearTimeoutneeds no note saying that it clears a timer. A reader who does not know that has documentation; a reader who does has been stopped for no reason. - Write a cross-cutting fact down once, in the module that owns it. Two copies drift and no tool reports the drift, so every other site links to that module or stays silent.
Weak, because the sentence paraphrases the statement and goes wrong the moment the statement changes:
// Clamp the delay to the maximum supported timeout.const delay = Math.min(remaining, MAX_SUPPORTED_TIMEOUT)Strong, because the platform behaviour behind the clamp is the part the line cannot show:
// Node sets the delay to 1 ms when it is larger than 2147483647, so an// unclamped timeout becomes no timeout at all.// https://nodejs.org/api/timers.html#settimeoutcallback-delay-argsconst delay = Math.min(remaining, MAX_SUPPORTED_TIMEOUT)MAX_SUPPORTED_TIMEOUT says where the ceiling is. Only the comment says why there is one, and it names the source a reader can open.
Every fact must be checkable
Section titled “Every fact must be checkable”The reader is the only verifier a comment will ever get. Naming the source turns a claim taken on trust into one anybody can check against library documentation, a standard, or this repository.
- Say where a behaviour claim comes from. A comment reading
isolated-vm kills the synchronous portion with the bare message Script execution timed out.sends the reader to a source they can open.The engine sometimes cuts execution shortsends them nowhere, and a claim with no source behind it sits in the file uncorrected. - Keep internal process facts out. No session history, no decision minutes, no task or plan references, no reviewer names. Nobody outside the conversation that produced them can check any of it.
- State a finding at the strength the evidence supports. One code path is an observation. Written as a law it is read as one, and the first counter-example gets blamed on the code and not on the comment.
Every fact must be derived
Section titled “Every fact must be derived”Being true is not enough. A fact belongs here when it is the cause of what it describes, when it survives the next edit to that thing, and when it changes what the reader does. Text assembled from every detail that happened to be true reads as thorough on the day it is written and comes apart one edit at a time afterwards.
- Trace a fact back to what makes it true, and state that instead.
enginesinpackage.jsonnames the Node range, andpnpm installrefuses a Node outside it. A sentence about version managers documents a habit where a constraint belongs, and it goes wrong the day somebody changes their habit. - Ask what happens when the next person deletes the thing tomorrow, changes it, or adds another beside it. Lint rules and formatter options are entries in files anybody can open. Listing one in prose makes a second copy of a fact, and no lane compares the two. The copy in prose goes wrong in silence, so name the file the setting lives in and let the reader open it.
- Leave out what the reader cannot act on. Knowing a lint rule’s identifier does not change the code somebody writes, and the rule stated plainly is both shorter and still true after a rename.
- Soften a claim instead of qualifying it into an enumeration. An absolute rescued by naming its one exception is exact today and wrong the moment a second exception appears, and the exception belongs wherever the rule it qualifies lives.
One execution per worker, unless executionsPerWorker raises itgoes wrong in silence the day a second setting lands, andOne execution runs per worker by defaultoutlives both.
Write for the stranger who opens the file first
Section titled “Write for the stranger who opens the file first”Assume the reader arrived from a search result, knows nothing about the project, and reads this file and no other.
- No fragment stands where a sentence belongs. A label with a colon after it, or a shorthand note to self, means whatever the reader decides it means. But a colon following a complete clause is ordinary punctuation, and it introduces the explanation or the enumeration that clause promised.
- A comment states a constraint. It never defends a decision and never answers a reviewer: the argument that convinced the reviewer ended when the branch merged, and the constraint is the part that outlived it.
- No parenthetical explanation appears anywhere: not in a name, not in a comment, not in documentation, not in a commit message. A parenthesis is where a writer puts the piece of a thought they declined to finish, so finish it and give it its own sentence. This rule has no exceptions.
- The em dash and the en dash are out for the same reason. Each one brackets a qualifier the writer declined to give a clause of its own. A qualifier worth keeping becomes a sentence, and one that is not was never worth the mark.
- No first person appears in any text.
weandourname a group the reader was never in, and a year later they name nobody at all. - Write English throughout, in code, comments, documentation and commits. Decorative emoji stay out of all four.
Headings, quotation marks and emphasis
Section titled “Headings, quotation marks and emphasis”The settings below apply across the tree, so that no file decides them again. Review is the only check on any of them, and each one shows up first as a page that reads as though a second author wrote it.
- A heading is sentence case. Words that carry a capital of their own keep it, so a capital past the first word belongs to the word and not to the heading.
- Quotation marks are straight. Curly marks come from an editor that substitutes them, and they break a copied command the moment somebody pastes it into a shell.
- Emphasis comes from the sentence, never from bold type. Bolded lead-ins are labels standing where a subject belongs, and on a page with several of them a reader learns to read the bold and skip the rest.
Anti-patterns
Section titled “Anti-patterns”Each row is a shape to avoid, with one example of it. None of them looks like a problem on the line it sits on.
| Anti-pattern | Example to avoid |
|---|---|
| False absolute | // V8 always releases the isolate on dispose. |
| Meta-commentary | // This file was reworked to keep the parser small. |
| Flowery phrasing | // A delicate dance between the host and the isolate. |
| Tacked-on speculation | // Possibly related to how libuv batches timers. |
| Test cross-reference | // See the "rejects after dispose" test for the details. |
| Label in place of a sentence | // Why: the setter doubles as the completion signal. |
| Semicolon-chained fragments | // Host side only; cleared in finally; never leaks. |
| Rhetorical justification | // This is not over-engineering; the alternative would be far worse. |
| Code-structure narration | // Walk the tree with an explicit stack instead of recursing. |
| Restated config entry | // Forbidden by the no-underscore-dangle rule in the lint config. |
The replacement is almost always smaller than the comment it replaces. A false absolute narrows to the claim the evidence supports. Speculation gets verified and stated as fact, or dropped. A test cross-reference becomes the behaviour itself, written where the behaviour lives, because a test name is not a stable address. A restated config entry becomes the rule the code has to follow, stated without the entry, because the file the setting lives in is the only copy anything checks. Structure narration becomes the reason the structure is required, or it is deleted, since the code already shows the structure to anyone reading it.
Before you commit a comment
Section titled “Before you commit a comment”Put it through the audit. A no anywhere means it is not finished.
- Is every sentence whole, with no fragment and no note to self?
- Does it stand on its own for someone opening this file with no other context?
- Is every fact in it checkable from library documentation, a standard, or this repository?
- Does it state a constraint, without defending a decision or narrating the code?
- Does every fact in it name a cause the reader can act on, and not a detail that moves the next time somebody edits a tool?