Skip to content
aipatterns.dev

Code smellDispensables

Comments

Comments that explain what unclear code does are a sign the code should be renamed or restructured so it explains itself.

Also known as
Deodorant comments

Signs

  • A comment restates the next line: // increment retry count above retries++.
  • A block of code is introduced by a comment that names it: // compute shipping, followed by 20 lines.
  • A comment explains what a cryptic variable or method name means.
  • A comment states a precondition ("items must not be empty here") that nothing enforces.
  • Comments and code disagree, because the code changed and the comment did not.

Not every comment is a smell. Comments that explain why (a regulatory rule, a workaround for a vendor bug, a performance trade-off) carry information the code cannot. Public API docs are not this smell either.

Why it happens

The author knew the code was hard to read and patched the reader's experience instead of the code. Comments are cheaper to write than a good name or an extracted function, and they feel responsible. They then rot, because nothing tests them.

Example

A loyalty program computes a discount.

loyalty.ts
function discount(c: { y: number; o: number; t: string }, total: number): number {
  // c.y = years as member, c.o = orders this year, c.t = tier
  let d = 0;
  // long-time members get 5%
  if (c.y >= 3) d += 0.05;
  // frequent buyers get another 5%
  if (c.o > 12) d += 0.05;
  // gold tier doubles it
  if (c.t === "gold") d *= 2;
  // never more than 25%
  if (d > 0.25) d = 0.25;
  // total must be positive
  return total * d;
}

After renaming, extracting and asserting, the comments have nothing left to say:

loyalty.ts
interface Member {
  yearsAsMember: number;
  ordersThisYear: number;
  tier: "standard" | "gold";
}

const MAX_DISCOUNT_RATE = 0.25;

function loyaltyDiscount(member: Member, orderTotal: number): number {
  if (orderTotal <= 0) throw new RangeError("orderTotal must be positive");
  return orderTotal * discountRate(member);
}

function discountRate(member: Member): number {
  const isLongTime = member.yearsAsMember >= 3;
  const isFrequent = member.ordersThisYear > 12;
  let rate = (isLongTime ? 0.05 : 0) + (isFrequent ? 0.05 : 0);
  if (member.tier === "gold") rate *= 2;
  return Math.min(rate, MAX_DISCOUNT_RATE);
}

Treatment

  • If a comment explains a complex expression, use Extract Variable and give the variable the comment's meaning.
  • If a comment introduces a block of code, use Extract Method and name the method after the comment.
  • If a comment explains what a method does because its name does not, use Rename Method.
  • If a comment states an assumption about state, use Introduce Assertion so the assumption is checked.
  • If a comment explains a magic value, use Replace Magic Number with Symbolic Constant.
  • Keep, or add, comments that explain why. Delete comments that only explain what.

Payoff

  • Names and structure stay in sync with behavior because the compiler and tests check them.
  • Less text to read and nothing to go stale.
  • Assertions turn silent assumptions into failures you can see.

When to ignore

  • The comment explains intent, a non-obvious constraint, a link to a bug tracker or spec, or why a simpler approach was rejected.
  • The comment is API documentation (/** ... */, docstrings) consumed by tooling or library users.
  • The code is irreducibly complex (a numerical algorithm, a bit-twiddling hack), and the comment cites the source or derivation.
  • License headers and generated-code markers.

Agent checklist

  • For each comment you removed, its meaning now lives in a name, a type, a constant or an assertion.
  • You kept every comment that explains why, cites a ticket, or documents a public API.
  • You did not add new comments that restate the refactored code.
  • Assertions you introduced do not change behavior on valid inputs, and tests pass.
  • Any comment that disagreed with the code was resolved by checking which one is correct, not by deleting it blindly.

Treatments

See also

Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.