Skip to content

Chapter 14: Choosing Names

Names are a form of documentation and abstraction. Good names are precise and consistent, so a reader’s first guess about what a name refers to is correct. Because complexity is incremental, thousands of slightly vague names add up to real complexity, and a single bad name can cause a severe bug.

  • Create an image: a name should create an image in the reader’s mind of what the entity is, and just as important, what it is not.
    • Test: “If someone sees this name in isolation, without seeing its declaration, its documentation, or any code that uses the name, how closely will they be able to guess what the name refers to? Is there some other name that will paint a clearer picture?”
    • Limit: names become unwieldy beyond two or three words, so find the few words that capture what matters most.
  • Two properties of good names:
    1. Precision: avoid generic or vague names. Also avoid names that are too specific for what the entity really is.
    2. Consistency, which has three requirements:
      • Always use the common name for its purpose.
      • Never use the common name for anything else.
      • Keep the purpose narrow enough that every variable with that name behaves the same way.
  • Distinguishing prefixes: when you need several variables of the same kind, keep the common name and add a prefix, such as srcFileBlock and dstFileBlock.
  • Distance rule (from Andrew Gerrand, which the author agrees with): “The greater the distance between a name’s declaration and its uses, the longer the name should be.”
  • Red Flag: Vague Name: a name broad enough to refer to many different things conveys little and invites misuse.
  • Red Flag: Hard to Pick Name: if it’s hard to find a simple name that creates a clear image, the underlying entity may not have a clean design. Perhaps one variable represents several things and should be split.
  • Boolean names should be predicates: cursorVisible, not blinkStatus.
  • Loop variable convention: if you use i and j, always use i for outer loops and j for nested ones. Short names are fine when the whole range of use is visible.
  • result: reasonable in a method that returns it; misleading in a method with no return value.
  • Think of a name as an abstraction: it should focus attention on the most important aspect of the entity and omit the rest.
  • “Readability must be determined by readers, not writers.” If people find your short names cryptic, lengthen them; if they find long names hard to read, shorten them.
  • Treat naming skill as an investment. It is slow and frustrating at first, then becomes almost free.
  • Don’t settle for names that are “reasonably close”; take a little time for names that are “precise, unambiguous, and intuitive.”
  • Generic names: getCount() (count of what?) → numIndexlets or getActiveIndexlets. x, y for a character’s position in a file (they suggest pixel coordinates) → charIndex, lineIndex.
  • Names that say a value is special without saying how: VOTED_FOR_SENTINEL_VALUE → NOT_YET_VOTED.
  • Too specific: void delete(Range selection) suggests only selected text can be deleted; range is right.
  • One short name for several things: the Go practice of ch for character or channel, or d for data, difference, or distance. The author expects this to cause the same confusion as the block bug.
// Blink state: true when cursor visible.
private boolean blinkStatus = true;
// Controls cursor blinking: true means the cursor is visible,
// false means the cursor is not displayed.
private boolean cursorVisible = true;
  • What it demonstrates: “status” says nothing about what true means, and “blink” doesn’t say what blinks. cursorVisible is a predicate, so true and false are obvious. Why the cursor blinks is less important, so it moves to the comment.

The Go style debate. Gerrand argues that “long names obscure what the code does” and prefers the first version below:

func RuneCount(b []byte) int {
i, n := 0, 0
for i < len(b) {
if b[i] < RuneSelf {
i++
} else {
_, size := DecodeRune(b[i:])
i += size
}
n++
}
return n
}

The second version uses buffer, index, and count in place of b, i, and n. The author finds it no harder to read, and count saved him from working out what n meant. He accepts n if the whole system uses it only for counts.

The Sprite file system bug, “The most challenging bug I ever fixed.”

  1. Symptom. In the Sprite distributed operating system (late 1980s to early 1990s), files occasionally lost data: one block suddenly became all zeroes though nobody had modified the file. It happened rarely, and several graduate students gave up on it.
  2. The hunt. The author, who considers “any unsolved bug to be an intolerable personal insult,” spent six months on it.
  3. Cause. The file system used the name block for two things: a physical block number on disk and a logical block number within a file. In one place, a block variable holding a logical block number was used where a physical one was needed, so an unrelated disk block was overwritten with zeroes.
  4. Why reviewers missed it. Several people read the faulty code. Seeing block used as a physical block number, they assumed it held one. Only instrumentation that narrowed the corruption to one statement got the author past “the mental block created by the name.”
  5. The fix in naming terms. Distinct names such as fileBlock and diskBlock would almost certainly have prevented the bug, since a programmer would know fileBlock couldn’t be used there. This is a violation of the third consistency requirement: one name covered two different behaviors.
  1. Test each name in isolation: could a reader guess what it refers to?
  2. Prefer precise names, but not names narrower than the entity’s real role.
  3. Pick one name per recurring concept and use it everywhere, for that concept only.
  4. Use predicates for booleans, and longer names for variables used far from their declaration.
  5. If a good name won’t come, rethink the variable or method’s design.
  • Ch 2: vague names are a form of obscurity; complexity is incremental.
  • Ch 3: good names are an investment.
  • Ch 13: names and comments share the work of precision.
  • Ch 17: consistent naming is one form of consistency.
  • Ch 18: good names make code obvious.