Comments are the cheapest debugging tool you have: a well-placed comment tells the next reader why a decision was made, and a badly placed one tells them nothing. This guide covers the two JavaScript comment syntaxes, when a comment earns its place, and the everyday hygiene habits — naming, small functions, no dead code — that keep a script readable six months after you wrote it.
What are the two comment syntaxes?
JavaScript has exactly two. // starts a comment that runs to the end of the line. /* starts one that runs to the matching */, and can span lines.
// A single-line comment. Everything after // is ignored by the engine.
/*
A block comment.
It can cover many lines,
which is useful for temporarily disabling a chunk of code.
*/
const taxRate = 0.18; // trailing comments work too
/* console.log("this line is ignored"); */
console.log("only this line runs");
Two rules of thumb. Prefer //, because most editors treat it as a single unit you can toggle with a shortcut, and because a block comment swallowing the end of a line silently can change what your code does. And never leave a block comment open by accident: an unterminated /* makes the parser treat everything after it as a comment, so the whole rest of your file stops running with no error message at all.
Should a comment say what the code does, or why?
Why. The code already says what it does — that is what code is. A comment that restates the code is noise, and it becomes a lie the moment the code changes and the comment does not.
const price = 24950;
const quantity = 2;
// Useless: the code already says this.
const total = price * quantity;
// Useful: the code cannot say why.
const payable = price * quantity; // Prices are in paise, so divide by 100 before display.
console.log(total, payable);
The comments worth writing fall into a few recognisable categories. A why comment records a decision and its constraint: why the tax is applied before the discount, why the request is retried three times, why this edge case is handled separately. A warning comment flags a trap for the next person: // Do not use === here; the API returns a string. A marker comment records intent you have not finished yet: // TODO: move this into the shared formatter once the API settles.
Two comment types are worth removing whenever you spot them. Commented-out code is dead weight that readers assume is load-bearing, and it tends to go stale. And a comment that contradicts the code is worse than no comment, because the reader has to work out which one to trust.
What is JSDoc for?
JSDoc is a comment convention that starts with /** and describes a function, a class or a variable in a structured way. Editors read it and show you the parameter names, the types and the description while you type, and type-checking tools can use it to check your code.
/**
* Formats a price in paise as a rupee string.
*
* @param {number} paise - The amount in paise.
* @param {string} [currency = "INR"] - ISO currency code.
* @returns {string} A formatted price such as "₹249.50".
*/
function formatPrice(paise, currency = "INR") {
return `${currency} ${(paise / 100).toFixed(2)}`;
}
console.log(formatPrice(24950));
The @param, @returns and @optional tags are the ones you will use most. Square brackets around a parameter name, as in [currency = "INR"], mean it is optional. This comment costs four lines and saves every future reader from opening the function body to find out what the arguments are.
What makes code hygienic rather than merely working?
Most hygiene problems are naming problems. A good name says what a value holds and nothing more, and a bad name costs a reader a lookup every single time they meet it.
// Hard to read: what are x, d and t?
function calc(x, d, t) {
return x + d * t;
}
// Readable: each name carries its meaning and its unit.
function addShipping(basePaise, shippingPaise, taxRate) {
return basePaise + shippingPaise * taxRate;
}
const TOTAL_IN_PAISE = 24950;
const TAX_RATE = 0.18;
console.log(addShipping(TOTAL_IN_PAISE, 5000, TAX_RATE));
Three habits make up most of it. Name variables for what they hold, with units in the name when the unit matters: timeoutMs rather than timeout. Use const unless a value genuinely changes, so that a reader can see mutability without reading the body. And give constants a name in UPPER_SNAKE_CASE when they are module-level settings, which makes them easy to spot and easy to change in one place.
The other half of hygiene is structure. A function that does one thing is a function you can name, test and reuse; a function that does four things is a function nobody wants to touch. The most reliable way to shrink a function is to pull its early exits out into guard clauses:
// Nested: the happy path is buried at the bottom.
function priceFor(user, cart) {
if (user) {
if (cart.length > 0) {
if (user.member) {
return cart.subtotal * 0.9;
} else {
return cart.subtotal;
}
}
}
return 0;
}
// Flat: each rule states itself and exits early.
function priceFor(user, cart) {
if (!user) return 0;
if (cart.length === 0) return 0;
return user.member ? cart.subtotal * 0.9 : cart.subtotal;
}
The second version has no nesting at all and you can read it top to bottom in one pass. This is the same shape as the if/else decision lesson, and it is worth internalising early because it is the single biggest readability win available to you.
What should you delete before you commit?
Three things, every time, and all three are easy to miss because they do not look wrong.
- Debug logging.
console.logcalls sprinkled through working code leak implementation detail and slow the page down. Remove them, or route them through a helper you can silence. - Dead code. Unused variables, functions nobody calls, commented-out experiments, and the second argument you changed your mind about. If you are not sure whether something is used, search for its name in the whole project before you decide.
- Redundant comments. Comments that repeat the line below them, or that describe a variable whose name already says it. A comment that adds nothing is an invitation to read nothing.
There is one more category: stale comments. A comment that was true when written and is false now is the most expensive kind, because a reader has no way to know without checking. When you change the behaviour of a line, update its comment in the same commit, or delete it.
A short checklist before you save
- Every comment explains a decision, a constraint or a warning — never the syntax below it.
- Use
//for one line,/* */only when you genuinely need several lines or you are temporarily disabling code. - Names carry meaning and units:
timeoutMs,cartSubtotal,TAX_RATE. - Functions do one job and use guard clauses instead of nesting.
- No leftover
console.log, no commented-out code, no comment that contradicts the line it sits above.
Try it yourself: take a script you wrote earlier in this tutorial and improve it in three passes. Pass one: rename every variable so its name states its meaning and its unit. Pass two: replace every nested if with a guard clause. Pass three: delete every comment that does not explain a why, and rewrite the rest as one-line notes. Count the lines before and after — the file usually gets shorter even though it gained comments, because clearer names remove the need for whole comment blocks.