skip to content

What makes a variable, function, or class name "intention-revealing", and why is renaming usually a better fix than adding an explanatory comment?

level: juniorimportance: must knowfreq 80%

answer

  1. name answers why / what / how-used
  2. d → elapsedTimeInDays
  3. magic literal → named constant
  4. comments rot silently; names are tool-checked
  5. comment for *why*, name for *what*

basics

~20 s

An intention-revealing name says what the thing is, why it exists, and how it is used — so no comment is needed. Comments drift out of date silently; a name is re-read every time the code is read.

solid answer

~40 s

A name is intention-revealing when a reader can answer three questions from the name alone: why does this exist, what does it hold or do, and how is it used. `d` fails; `elapsedTimeInDays` passes. Unnamed literals ("magic numbers") fail the same test: `if (status == 4)` becomes `if (status == STATUS_CANCELLED)`. Prefer renaming over commenting because a comment is a second, unverified copy of the truth: nothing forces it to be updated when the code changes, and it is only visible at the declaration, whereas the name travels to every call site. Renaming is also a mechanical, tool-assisted, compiler-checked refactor. Comments are still valuable for what a name cannot carry: rationale ("why this odd threshold"), warnings, legal headers, links to a spec, or an explanation of a genuinely non-obvious algorithm.

code

pseudocode · 9 lines
pseudocode
// before: needs a comment to be readable
result = []
for (x in items)
    if (x[0] == 4) result.add(x)   // 4 = cancelled

// after: the comment is unnecessary
cancelledOrders = []
for (order in orders)
    if (order.status == STATUS_CANCELLED) cancelledOrders.add(order)

go deeper

for a junior

State the three questions a good name must answer, give a concrete before/after rename, and mention replacing magic numbers with named constants.

for a middle

Add why comments rot (unverified, invisible at call sites) and which comments survive: rationale, warnings, public API docs, links.

for a senior

Discuss name length vs scope, searchability, using domain vocabulary, and treating a hard-to-name thing as a design smell rather than a naming problem.

for a principal

Frame naming as a cheap, high-leverage design and communication control: shared glossary/ubiquitous language, naming reviewed at API-design time, and the cost curve of renaming once a name crosses a published boundary.

## The idea Code is read far more often than it is written. A name is the smallest and most frequently consumed piece of documentation in a system: it appears at the declaration *and* at every single use. "Intention-revealing" means the name alone answers three questions: 1. **Why does this exist?** (its purpose) 2. **What does it hold / what does it do?** (its content or effect) 3. **How is it used?** (its role in the surrounding logic) If answering any of those requires reading the implementation, scrolling to a comment, or asking a teammate, the name is not intention-revealing. ## Worked example ``` // version A list1 = [] for (x in theList) if (x[0] == 4) list1.add(x) return list1 ``` Every symbol here is opaque. There is no way to know what `theList` is, what index `0` means, or what `4` means. Now rename with no structural change: ``` // version B flaggedCells = [] for (cell in gameBoard) if (cell.status == FLAGGED) flaggedCells.add(cell) return flaggedCells ``` The algorithm is byte-for-byte identical. Only the names changed, and the code became self-explanatory. This is the canonical demonstration (from Robert C. Martin's *Clean Code*) that naming alone carries most of the readability. ## Terms defined - **Magic number / magic literal** — a bare literal (`4`, `86400`, `"US"`) whose meaning is not stated anywhere. The fix is a *named constant* (`STATUS_CANCELLED`, `SECONDS_PER_DAY`, `DEFAULT_COUNTRY_CODE`). Named constants are also *searchable*: you can find every use of `MAX_RETRIES`, but grepping for `3` returns noise. - **Extract variable / extract constant** — a refactoring that gives a name to an existing expression or literal so the name documents it. - **Refactoring** — changing structure without changing behavior. Rename is the safest refactoring there is, since IDEs perform it symbolically rather than by text search. ## Why a rename beats a comment | Property | Good name | Comment | |---|---|---| | Visible at every call site | yes | no — only at the declaration | | Verified by tooling | yes (compiler/linter resolves it) | no — free text, never checked | | Cost of going stale | rename is atomic across all uses | silently becomes a *lie* | | Cost to apply | one IDE action | must be written and maintained by hand | A stale comment is worse than no comment: readers trust it, and it actively misleads. Nothing in any toolchain fails a build because a comment stopped being true. This is why the rule of thumb is "a comment explaining *what* the code does is usually a failed name." ## When a comment is still right Renaming does not subsume all comments. Keep comments for: - **Rationale / why** — "we retry 3 times because the upstream gateway drops the first request after a cold start". No identifier can carry that. - **Warnings and consequences** — "this test takes 90 seconds", "not thread-safe". - **Legal / licence headers**, and **links to specs or tickets**. - **Genuinely non-obvious algorithms** — a bit-twiddling trick or a numerical-stability workaround. - **Public API documentation** (doc comments), which serve external consumers and generated reference docs; those are a contract, not a crutch. ## Edge cases and trade-offs - **Do not over-name.** `theCustomerObjectVariableForProcessing` is longer but not clearer. Add words that add *information*, not bulk. - **Very small scopes tolerate very short names.** A loop index `i` inside three lines is a universal convention and renaming it to `currentIndexPosition` adds noise. Name length should scale with how far the name travels. - **Renaming is not always free.** Inside a function it is free; on a published API, a database column, an event field, a metric, or a URL path, a rename is a breaking change and needs a migration strategy. - **Domain terms beat invented ones.** If the business says "settlement", do not name it `paymentFinalizer`. Mirror the language the domain experts use. ## How to practise it When you write a comment, first ask: could a better name make this comment unnecessary? When you read code and pause to work out what something is, that pause is the signal — rename it right then. Reviewers should treat "I had to read the body to understand the name" as a legitimate review comment.

  • Give an example of a comment that a better name cannot replace.
    Rationale and external constraints — e.g. "retry 3 times because the upstream gateway drops the first request after a cold start", a licence header, a link to the RFC being implemented, or a warning that a helper is not thread-safe. Names carry *what*; comments carry *why* and *beware*.
  • When is a very short name like `i` or `n` acceptable?
    When the scope is tiny and the convention is universal — a loop counter used within a few lines, or a math-style local in a short function. The further a name travels from its declaration, the more descriptive it must be.
  • Your rename would touch a public API. What changes?
    Inside a module a rename is a free, compiler-checked refactor. Across a published API, a wire schema, a DB column, a metric or a URL, it is a breaking change: you need an expand–migrate–contract rollout (add the new name, support both, migrate consumers, then remove the old one).

A name is the label printed on the jar; a comment is a sticky note on the shelf beside it. Move the jar and the label comes with it — the sticky note stays behind, now describing a jar that isn't there.

saying these in an interview costs you the question

  • "Just add a comment explaining the variable" — treating a comment as equivalent to a rename.
  • Believing longer always means clearer (`theProcessedCustomerDataObject`).
  • Leaving magic literals in place because "everyone on the team knows what 4 means".
  • Claiming comments are always bad — rationale, warnings, and public API docs are legitimate.
  • Renaming a public/wire/DB identifier in place and calling it a refactoring.

context