Gabriel Malenowitch
March 15, 2026 · Gabriel Malenowitch

Comments and code

clean code
best practices
development
readability
architecture
documentation

Comments and code

Everyone already knows the basic part: don’t write comments explaining what the code does, because the code already does that. An outdated comment is worse than no comment.

But there’s a set of alternatives that go beyond “use descriptive names” — and that most people don’t consider.


Write the business rule in the code, not above it

When you feel the urge to write a comment explaining a condition, consider whether you can turn that condition into readable code.

// before — the comment carries the context
// legacy plan users created before 2022 don't pay a fee
if (user.plan === 'legacy' && user.createdAt < new Date('2022-02-01')) {
  return basePrice;
}

// after — the code carries the context
const LEGACY_PLAN_CUTOFF = new Date('2022-02-01');
const isExemptFromConversionFee =
  user.plan === 'legacy' && user.createdAt < LEGACY_PLAN_CUTOFF;

if (isExemptFromConversionFee) {
  return basePrice;
}

isExemptFromConversionFee says exactly what’s happening. If the rule changes, the variable name will look wrong — which is better than a silently outdated comment.


Tests are documentation with a guarantee

A well-written test name describes behavior better than any comment — and has an advantage that comments don’t: it fails if the behavior changes.

it('does not charge a conversion fee for legacy plan users created before January 2022', () => {
  ...
});

This is executable documentation. If someone changes the charging logic without updating the test, CI lets you know. A comment never does.


The “why” lives in the commit, not in the code

Comments explaining decisions — “we chose X instead of Y because of Z” — become outdated along with the code. The right place for that information is the commit message.

fix: use polling instead of a webhook for payment

The provider's webhook endpoint is unstable in the staging environment.
We switched back to polling until support confirms it's resolved.
Ref: #4521

git blame on the line takes you straight to the commit with the full context. A comment in the code doesn’t have that traceability.


A region comment is a sign of a function that’s too large

function processPayment(order) {
  // ── validation ─────────────────
  ...

  // ── calculation ────────────────
  ...

  // ── persistence ────────────────
  ...
}

If you’re using comments to divide sections within a function, the function is too large. Each section is probably a separate function waiting to exist. The region comment is the symptom — the large function is the problem.


Commented-out code isn’t a backup

// const result = oldCalculation(order);
const result = newCalculation(order);

This code shouldn’t exist in any branch. Git history keeps the old version. Commented-out code accumulates, nobody knows whether they can delete it, and it becomes permanent noise in the codebase.


TODO is a promise nobody will keep

// TODO: refactor this later
// TODO: add validation here
// TODO: understand why this number is 42

If it’s important, turn it into a ticket. If it’s not important enough to become a ticket, it probably won’t get done. The TODO stays forever, loses its context, and nobody knows whether it’s still relevant.


When a comment really is necessary

With all that said, there are cases where a comment is the right tool — when none of the alternatives above can carry the context.

Workaround for an external bug:

// Working around a react-pdf@3.1.2 bug that duplicates headers in multi-page tables.
// https://github.com/diegomura/react-pdf/issues/1234
// Remove when upgrading to 3.2.x
const headers = isFirstPage ? columns : [];

A business rule whose origin is external to the code — a legal requirement, contractual agreement, legacy system limitation — where you can’t refactor the context into the code.

Nontrivial mathematics with a reference to the formula or source.


The difference between a necessary comment and an unnecessary one is simple: a necessary comment explains something the code can’t express on its own. An unnecessary one explains the code itself — and that’s the code’s job.

06
Comments

Leave a comment

Feedback, questions, opportunities — feel free.