Meaningful Names
Naming is one of the few things every programmer does constantly, in every language, in every file. Variables, functions, classes, modules, files - all of it gets named, and because naming happens so often, getting a little better at it pays off everywhere at once. This chapter collects a set of concrete rules for picking names, not as style preferences but as a direct lever on how easy code is to read.
Reveal intent
A name should answer the questions a reader would otherwise have to ask: why does this
variable exist, what does it hold, how is it meant to be used? If understanding a name
requires a comment next to it, the name has already failed. d for "elapsed time in
days" tells you nothing; elapsed_time_in_days tells you everything the comment was
trying to say, without needing the comment.
The effect compounds across a whole function. A loop over the_list that checks
x[0] == 4 is technically simple - three variables, one condition - but it's completely
opaque, because none of the domain knowledge is visible in the code. Once you know this
is a minesweeper board and the cells are being filtered for a "flagged" status, renaming
things to say so turns opaque code into obvious code, with no change to the underlying
logic.
Avoid disinformation and false distinctions
Some naming problems come from names that actively mislead, and some come from names that merely fail to distinguish anything.
Disinformation is a name that carries an entrenched meaning different from the one
you intend. Calling a variable account_list implies a specific data type - if it's
actually a set or a generator, the name is now lying to anyone who reads it. Similarly,
naming two very similar things with almost-identical spellings (...ForEfficientHandlingOfStrings
vs. ...ForEfficientStorageOfStrings) creates a trap for a skimming reader, and a single
lowercase l next to an uppercase O in the same expression is close to unreadable.
Meaningless distinctions happen when two names need to differ only because a
language forces uniqueness in scope, not because the underlying concepts differ. Number
suffixes (a1, a2) tell a reader nothing about intent - source and destination
would say instantly what each argument is for. Noise words are the same problem in a
different shape: ProductInfo next to Product doesn't distinguish anything, because
"info" doesn't mean anything on its own. If two names can't be told apart without reading
the implementation, the names have failed at their one job.
Make names pronounceable and searchable
Code is a social activity - it gets discussed out loud, in reviews, in stand-ups, in
pairing sessions. A name you can't say without sounding ridiculous is a name that quietly
discourages discussion. genymdhms (generation year-month-day-hour-minute-second) forces
everyone who mentions it into an awkward, made-up pronunciation. generation_timestamp
needs no explanation and no funny voice.
Single letters and bare numeric constants share a related problem: they're nearly
impossible to search for. Grepping for MAX_CLASSES_PER_STUDENT finds every use
immediately; grepping for the digit 4 finds every unrelated 4 in the codebase too. The
rule of thumb is that the length of a name should match the size of its scope - a loop
counter named i inside five lines is fine, but any value with more than a trivial
lifetime deserves a name specific enough to find.
Avoid encodings and mental mapping
Older languages forced type or scope information into names because the language itself
couldn't track types - Hungarian notation, m_ prefixes for members, an I prefix for
interfaces. Modern languages, IDEs, and type systems already track this information, so
encoding it into the name is pure overhead: it makes renaming harder, makes the name
uglier, and eventually goes stale when the type changes but nobody updates the name that
promised it.
A related trap is mental mapping - naming something r because you personally
remember it's "the lowercased URL with the scheme stripped." That's not cleverness, it's
a tax on every future reader (including you, in six months) who now has to reconstruct
your private convention before they can read the line at all. Single-letter names are
acceptable only for very short-lived loop counters where tradition (i, j, k) already
carries the meaning.
Classes, methods, and picking one word
Classes and objects should be named with nouns or noun phrases - Customer, Account,
AddressParser - never verbs, and never vague catch-alls like Manager, Processor, or
Data that describe nothing about what the class actually holds. Methods, in the
opposite direction, should read as verbs - postPayment, deletePage, save - with
accessors and predicates following a consistent get/set/is convention so a reader
never has to guess whether something is an action or a question.
Interfaces deserve plain names too - ShapeFactory, not IShapeFactory. Callers should
just see "a ShapeFactory," not be reminded on every use that they're holding an interface.
If one of the two needs an ugly marker, put it on the concrete class (ShapeFactoryImpl)
instead. And when a class needs several ways to construct it, named static factory methods
Complex.from_real_number(23.0)- describe what each one builds far better than a pile of overloaded constructors ever could.
Once a class and method vocabulary exists, stay consistent with it. Don't let fetch,
retrieve, and get coexist as different names for the same operation across different
classes - pick one word per concept and use it everywhere that concept appears.
The opposite mistake, using the same word for two different concepts (calling both "add
two numbers" and "insert into a collection" add), is a pun, and puns in code cost a
reader time they didn't need to spend.
Cleverness is its own trap here too. A function named HolyHandGrenade might be funny to
the person who wrote it, but DeleteItems tells the next reader what actually happens.
Say what you mean; mean what you say.
Solution domain vs. problem domain
Readers of your code are programmers, so computer-science vocabulary - algorithm names,
pattern names, data-structure terms - is fair game and often the clearest choice available.
JobQueue or AccountVisitor communicate immediately to anyone who knows a queue or the
Visitor pattern. But when there's no clean technical term for what you're modeling, reach
for the vocabulary of the problem itself, so a maintainer can go ask a domain expert what
a name means instead of reverse-engineering it from code.
Add context, but only as much as it needs
Very few names are self-explanatory in isolation. first_name, last_name, state, and
zip_code read clearly as a group, but state used alone, three functions away from that
group, tells the reader nothing about what it's short for. The cheapest fix is a shared
prefix; the better fix is a class - wrapping the related fields in an Address gives the
compiler, not just the reader, a reason to know they belong together.
The same problem shows up inside a single function: three related string variables built up across a long conditional chain, with no name tying them together, force the reader to infer their shared purpose by tracing the whole function. Once you notice that shared purpose, giving it a name - even as a small class - both documents the relationship and gives you a natural place to split the logic further.
The opposite failure is gratuitous context: prefixing every class in an application
called "Gas Station Deluxe" with GSD doesn't add clarity, it adds noise that fights
autocomplete and makes every name ten characters longer than it needs to be. Add exactly
as much context as a name needs to be unambiguous, and no more.
The real difficulty
None of these rules is mechanical. Choosing a name well requires understanding what a thing actually is and does, which is a design skill, not a vocabulary trick. That's also why renaming shouldn't be scary - a name that turns out to be wrong once you understand the code better isn't a failure, it's information, and modern tooling makes fixing it cheap. The names you end up with are one of the main things a reader has to go on, so it's worth treating every one of them as a small, deliberate decision.