# Comments

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

- kind: smell
- category: dispensables
- aliases: Deodorant comments
- canonical: https://aipatterns.dev/refactoring/smells/comments
- inspired-by: https://refactoring.guru/smells/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.

```ts title="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:

```ts title="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](https://aipatterns.dev/refactoring/techniques/extract-variable.md) and give the variable the comment's meaning.
- If a comment introduces a block of code, use [Extract Method](https://aipatterns.dev/refactoring/techniques/extract-method.md) and name the method after the comment.
- If a comment explains what a method does because its name does not, use [Rename Method](https://aipatterns.dev/refactoring/techniques/rename-method.md).
- If a comment states an assumption about state, use [Introduce Assertion](https://aipatterns.dev/refactoring/techniques/introduce-assertion.md) so the assumption is checked.
- If a comment explains a magic value, use [Replace Magic Number with Symbolic Constant](https://aipatterns.dev/refactoring/techniques/replace-magic-number-with-symbolic-constant.md).
- 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.
