RizTech Academy logo
RizTech Academy
Best Practices: Code Others Can ReadLesson 3 of 525 min

Comments that say why, and JSDoc that earns its place

The instinct most beginners are taught is "comment your code". It produces this:

// loop through the orders
for (const order of orders) {
  // if the order is delivered
  if (order.delivered) {
    // add to the total
    total += order.pieces;
  }
}

Every comment restates the line under it. None of them tells you anything the code does not. And they will rot: somebody will change pieces to amountPaise and leave "add to the total" sitting there, now slightly wrong.

Code says what. Comments say why. That is the whole rule, and the rest of this lesson is what it looks like in practice.

The comments worth writing

Why, when the why is not obvious.

// Debounced at 300ms, not less. Below about 250ms a mid-range Android on mobile
// data still has the previous request in flight and the results arrive out of
// order.
const search = debounce(runSearch, 300);

Nobody can recover that from the code. The next person will try 100ms, see it work on their laptop on office wifi, and ship it.

A decision you rejected.

// Stored as paise, an integer. Rupees as a float gives 0.1 + 0.2 === 0.30000000000000004,
// and a bill that is one paisa wrong is a bill the customer does not trust.
const amountPaise = Math.round(rupees * 100);

A warning.

// Order matters: the auth check must run before this, because it sets
// req.user, which the rate limiter keys on.

A workaround, with a reference.

// Safari fires `resize` on scroll because the URL bar collapses, so a pure
// resize listener re-renders the list on every scroll. Compare the width.
// https://bugs.webkit.org/show_bug.cgi?id=170595

A regular expression. Always. Nobody reads one of those at speed.

// Six digits, never starting with zero: no Indian pincode does.
const PINCODE = /^[1-9]\d{5}$/;

The comments to delete

Ones that restate the code. Ones that have gone stale — worse than none, because a reader trusts them. Section banners inside a long function (// ---- validation ----) — that is a function asking to be extracted. Change history, which git already has, in more detail, with the author.

And commented-out code. It sits there for two years and nobody dares remove it because nobody knows whether it matters. Delete it; git remembers.

TODO comments

A TODO with nothing attached is a wish.

// TODO: handle the empty state
// TODO(RTA-214): handle the empty cart. Right now it renders "0 items" and the
// Checkout button is live, so a customer can submit an empty order.

The second has a ticket and says what is broken. If it is not worth a ticket, it is not worth a TODO — either fix it now or delete the comment.

JSDoc: the type system you get for free

JavaScript has no types. JSDoc is a comment format that editors and TypeScript both understand, so you get autocomplete and red squiggles without changing a single line of runtime code.

/**
 * Bill for a month of tiffin deliveries.
 *
 * @param {number} ratePaise - price of one tiffin, in paise
 * @param {number} count - deliveries actually made
 * @returns {number} amount in paise
 * @throws {RangeError} if count is negative
 */
function billPaise(ratePaise, count) {
  if (count < 0) throw new RangeError("count must not be negative");
  return ratePaise * count;
}

Hover billPaise in VS Code and you now see that. Pass a string and you get a warning. The units are in the parameter names and stated — because "amount" alone has bitten every team that has ever handled money.

Define a shape once and reuse it:

/**
 * @typedef {object} Order
 * @property {string} id
 * @property {string} customer
 * @property {number} pieces
 * @property {boolean} delivered
 */

/** @param {Order[]} orders @returns {Order[]} */
function pending(orders) {
  return orders.filter((o) => !o.delivered);
}

Now orders[0]. autocompletes, and o.delivred is caught as you type.

Turn it on for a whole file with one line at the top:

// @ts-check

Try it on your capstone. Expect ten or fifteen complaints, and expect two of them to be real bugs — usually a value that can be undefined in a path you had not thought about.

What not to document

/**
 * Gets the customer.
 * @returns {string} the customer
 */
function getCustomer() {
  return this.customer;
}

Four lines to say what one line said. Document what is not obvious: units, ranges, what happens on failure, whether the argument is mutated, whether the function is safe to call twice.

Naming beats commenting

// check if the order can still be cancelled
if (order.status === "PLACED" && Date.now() - order.placedAt < 30 * 60 * 1000) {
if (isCancellable(order)) {

The comment became a name, and the name cannot go stale — if the rule changes, the function changes with it. Reach for a name before you reach for a comment. That said, the rule itself — why thirty minutes — still deserves a comment inside isCancellable.

Check your work

The rule: code says what, comments say why.

Why a restating comment is worse than none: it rots, and readers trust it.

Five comments worth writing: a non-obvious why, a rejected alternative, a warning about ordering, a workaround with a link, and any regular expression.

What makes a TODO useful: a ticket and a description of the harm.

What JSDoc buys with no runtime cost: autocomplete and type errors in the editor.

What // @ts-check does: turns on type checking for that file.

Why a getter needs no docblock: it says nothing the signature does not.

Why a name beats a comment: a name cannot go stale.

Practice

  1. Open your capstone and delete every comment that restates its line.
  2. Find one decision you would have to re-derive in a month. Write the why.
  3. Find a magic number. Comment why that number, or make it a named constant.
  4. Turn one comment into a function name.
  5. Add // @ts-check to your largest file. Count the complaints; fix two.
  6. Write a @typedef for your main data shape and use it in three functions.
  7. Type a deliberate typo in a property name and watch the editor catch it.
  8. Find a TODO — yours or in any open-source repo — and rewrite it with the harm it causes.
  9. Write a regex with no comment, close the file, and come back tomorrow.
  10. Delete any commented-out code, then recover it from git log -p to prove you did not lose it.

Next: code that survives bad input.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship