Comments
Back in Code structure you met the two comment syntaxes: // runs to the end of the line, and /* ... */ can wrap several lines. The mechanics take a minute to learn. Knowing when to reach for a comment takes longer, and it’s where most beginners go wrong.
The instinct is understandable. You write something tricky, you feel it might confuse someone, so you narrate it. But a comment is not free. It sits next to the code, ages independently of it, and quietly lies the moment someone edits the code and forgets the comment. So the goal isn’t “more comments” or “fewer comments” — it’s the right comments.
Bad comments
Beginners tend to write comments that describe what is going on in the code. Something like this:
// This routine handles the edge cases (...) and the main path (...)
// ...plus a few things nobody remembers...
very;
tangled;
logic;
In well-written code, this kind of “explanatory” narration should be rare. The code itself should read clearly enough that the narration adds nothing.
A useful rule of thumb: if a piece of code is so tangled that it needs a comment to be understood, that’s a signal to rewrite the code, not to annotate it. The comment is treating a symptom. Fix the cause.
Recipe: factor out a function
Often the cleanest way to delete a comment is to give the commented block a name. Look at this perfect-number printer:
function showPerfect(n) {
for (let i = 2; i < n; i++) {
// check if i equals the sum of its proper divisors
let sum = 0;
for (let d = 1; d < i; d++) {
if (i % d == 0) sum += d;
}
if (sum != i) continue;
alert(i);
}
}
That inner loop needs a comment because, at a glance, it’s not obvious what it decides. Pull it into its own function and the comment becomes the function’s name:
function showPerfect(n) {
for (let i = 2; i < n; i++) {
if (!isPerfect(i)) continue;
alert(i);
}
}
function isPerfect(n) {
let sum = 0;
for (let d = 1; d < n; d++) {
if (n % d == 0) sum += d;
}
return sum == n;
}
Now the outer loop reads almost like English: for each i, skip it if it’s not perfect, otherwise print it. The function name isPerfect carries the meaning the comment used to carry. Code like this is called self-descriptive — it explains itself.
Recipe: split a long “code sheet” into functions
The same idea scales up. Suppose you have one long stretch of code, broken into sections by comments:
// here we plant the trees
for(let i = 0; i < 10; i++) {
let sapling = getSapling();
inspect(sapling);
plant(sapling, plot);
}
// here we sow the flowers
for(let t = 0; t < 3; t++) {
let seed = getSeed();
examine(seed);
let sprout = germinate(seed);
plant(sprout, plot);
}
// ...
Each // here we … comment is really a heading for the block below it. When a comment is naming a section, that section usually wants to be a function:
plantTrees(plot);
sowFlowers(plot);
function plantTrees(area) {
for(let i = 0; i < 10; i++) {
let sapling = getSapling();
//...
}
}
function sowFlowers(area) {
for(let t = 0; t < 3; t++) {
let seed = getSeed();
//...
}
}
The top of the file now reads as a recipe: plant trees, sow flowers. The functions carry their own explanation in their names, so there’s nothing left to comment. You also get a structural bonus — each function has a clear job, clear inputs (its parameters), and a clear result. Reading the two-line summary is enough to follow the flow, and you drop into a function only when you need the detail.
Good comments
If comments that narrate what the code does are usually noise, which comments pull their weight? The good ones tend to answer questions the code physically cannot answer on its own.
- Describe the architecture
A high-level map: what the major components are, how they talk to each other, what the control flow looks like in different situations. The bird’s-eye view. Individual lines of code can never show you this — you’d have to read the whole system and assemble the map in your head. There’s even a dedicated notation, UML, for drawing high-level architecture diagrams. Worth knowing.
- Document a function’s parameters and usage
There’s a widely used convention called JSDoc for documenting a function: what it’s for, what arguments it takes, and what it returns.
For instance:
/**
* Returns a string repeated a given number of times.
*
* @param {string} text The string to repeat.
* @param {number} times The number of copies, must be a natural number.
* @return {string} text joined together that many times.
*/
function repeatStr(text, times) {
...
}
A block like this lets you understand the function’s purpose and call it correctly without reading a single line of its body. That’s the whole value: the caller reads the contract, not the implementation.
Many editors understand this format too. Tools like WebStorm and VS Code parse JSDoc blocks to power autocomplete and light type-checking as you type. And generators such as JSDoc 3 can turn these comments into browsable HTML documentation. The reference lives at https://jsdoc.app.
- Why is the task solved this way?
What the code does is visible in the code. What’s often invisible is why it does it that way — and that can matter more. If several approaches were possible, why this one, especially when it isn’t the obvious choice?
Skip that explanation and you set up a familiar trap:
- You (or a teammate) open code written a while ago and notice it looks “suboptimal”.
- You think, how naive I was back then, and rewrite it in the “obvious, correct” way.
- Partway through, you discover the obvious way is broken — and you half-remember that you already hit this exact wall once before. You revert to the original, but the afternoon is gone.
A short note explaining the reasoning would have saved that whole detour. Comments that record why keep future development pointed in the right direction.
- Any subtle or counter-intuitive behavior? Where is it relied on?
If a piece of code does something subtle, or depends on a non-obvious quirk, say so. Flag the surprise before it bites the next reader — including you.
Summary
A telling sign of a strong developer is how they handle comments — both the ones they write and the ones they deliberately leave out. Good comments make code easier to maintain, easier to return to after months away, and easier for others to reuse.
Comment this:
- The overall architecture and high-level design.
- How to use a function — its parameters, return value, and purpose.
- Non-obvious decisions, especially why one approach was chosen over another.
Avoid comments that:
- Narrate how the code works or what it does line by line.
- Exist only because the code is hard to read. Reach for them only when you truly can’t make the code simple and self-descriptive on its own.
And remember that comments feed auto-documentation tools like JSDoc 3, which read them and emit HTML (or other formats) — so a good comment can outlive the source file it started in.