N1: Choose Descriptive Names
Don’t be too quick to choose a name.
Make sure the name is descriptive.
Remember that meanings tend to drift
as software evolves, so frequently
reevaluate the appropriateness of the
names you choose. This is not just a
“feel-good” recommendation. Names in
software are 90 percent of what make
software readable. You need to take
the time to choose them wisely and
keep them relevant. Names are too
important to treat carelessly.
Consider the code below. What does it
do? If I show you the code with
well-chosen names, it will make
perfect sense to you, but like this
it’s just a hodge-podge of symbols and
magic numbers.
public int x() {
int q = 0;
int z = 0;
for (int kk = 0; kk < 10; kk++) {
if (l[z] == 10)
{
q += 10 + (l[z + 1] + l[z + 2]);
z += 1;
}
else if (l[z] + l[z + 1] == 10)
{
q += 10 + l[z + 2];
z += 2;
} else {
q += l[z] + l[z + 1];
z += 2;
}
}
return q;
}
Here is the code the way it should be
written. This snippet is actually less
complete than the one above. Yet you
can infer immediately what it is
trying to do, and you could very
likely write the missing functions
based on that inferred meaning. The
magic numbers are no longer magic, and
the structure of the algorithm is
compellingly descriptive.
public int score() {
int score = 0;
int frame = 0;
for (int frameNumber = 0; frameNumber < 10; frameNumber++) {
if (isStrike(frame)) {
score += 10 + nextTwoBallsForStrike(frame);
frame += 1;
} else if (isSpare(frame)) {
score += 10 + nextBallForSpare(frame);
frame += 2;
} else {
score += twoBallsInFrame(frame);
frame += 2;
}
}
return score;
}
The power of carefully chosen names is
that they overload the structure of
the code with description. That
overloading sets the readers’
expectations about what the other
functions in the module do. You can
infer the implementation of
isStrike() by looking at the code above. When you read the isStrike
method, it will be “pretty much what
you expected.”
private boolean isStrike(int frame) {
return rolls[frame] == 10;
}
N2: Choose Names at the Appropriate Level of Abstraction
Don’t pick names that communicate
implementation; choose names the
reflect the level of abstraction of
the class or function you are working
in. This is hard to do. Again, people
are just too good at mixing levels of
abstractions. Each time you make a
pass over your code, you will likely
find some variable that is named at
too low a level. You should take the
opportunity to change those names when
you find them. Making code readable
requires a dedication to continuous
improvement. Consider the Modem
interface below:
public interface Modem {
boolean dial(String phoneNumber);
boolean disconnect();
boolean send(char c);
char recv();
String getConnectedPhoneNumber();
}
At first this looks fine. The
functions all seem appropriate.
Indeed, for many applications they
are. But now consider an application
in which some modems aren’t connected
by dialing. Rather they are connected
permanently by hard wiring them
together (think of the cable modems
that provide Internet access to most
homes nowadays). Perhaps some are
connected by sending a port number to
a switch over a USB connection.
Clearly the notion of phone numbers is
at the wrong level of abstraction. A
better naming strategy for this
scenario might be:
public interface Modem {
boolean connect(String connectionLocator);
boolean disconnect();
boolean send(char c);
char recv();
String getConnectedLocator();
}
Now the names don’t make any
commitments about phone numbers. They
can still be used for phone numbers,
or they could be used for any other
kind of connection strategy.
N3: Use Standard Nomenclature Where Possible
Names are easier to understand if they
are based on existing convention or
usage. For example, if you are using
the DECORATOR pattern, you should use
the word Decorator in the names of the
decorating classes. For example,
AutoHangupModemDecorator might be the
name of a class that decorates a Modem
with the ability to automatically hang
up at the end of a session. Patterns
are just one kind of standard. In
Java, for example, functions that
convert objects to string
representations are often named
toString. It is better to follow
conventions like these than to invent
your own. Teams will often invent
their own standard system of names for
a particular project. Eric Evans
refers to this as a ubiquitous
language for the project. Your code
should use the terms from this
language extensively. In short, the
more you can use names that are
overloaded with special meanings that
are relevant to your project, the
easier it will be for readers to know
what your code is talking about.
N4: Unambiguous Names
Choose names that make the workings of
a function or variable unambiguous.
Consider this example from FitNesse:
private String doRename() throws Exception
{
if(refactorReferences)
renameReferences();
renamePage();
pathToRename.removeNameFromEnd();
pathToRename.addNameToEnd(newName);
return PathParser.render(pathToRename);
}
The name of this function does not say
what the function does except in broad
and vague terms. This is emphasized by
the fact that there is a function
named renamePage inside the function
named doRename! What do the names tell
you about the difference between the
two functions? Nothing. A better name
for that function is
renamePageAndOptionallyAllReferences.
This may seem long, and it is, but
it’s only called from one place in the
module, so it’s explanatory value
outweighs the length.
N5: Use Long Names for Long Scopes
The length of a name should be related
to the length of the scope. You can
use very short variable names for tiny
scopes, but for big scopes you should
use longer names. Variable names like
i and j are just fine if their scope
is five lines long. Consider this
snippet from the old standard “Bowling
Game”:
private void rollMany(int n, int pins)
{
for (int i=0; i<n; i++)
g.roll(pins);
}
This is perfectly clear and would be
obfuscated if the variable i were
replaced with something annoying like
rollCount. On the other hand,
variables and functions with short
names lose their meaning over long
distances. So the longer the scope of
the name, the longer and more precise
the name should be.
N6: Avoid Encodings
Names should not be encoded
with type or scope information.
Prefixes such as m_ or f are useless
in today’s environments. Also project
and/or subsystem encodings such as
vis_ (for visual imaging system) are
distracting and redundant. Again,
today’s environments provide all that
information without having to mangle
the names. Keep your names free of
Hungarian pollution.
N7: Names Should Describe Side-Effects
Names should
describe everything that a function,
variable, or class is or does. Don’t
hide side effects with a name. Don’t
use a simple verb to describe a
function that does more than just that
simple action. For example, consider
this code from TestNG:
public ObjectOutputStream getOos() throws IOException {
if (m_oos == null) {
m_oos = new ObjectOutputStream(m_socket.getOutputStream());
}
return m_oos;
}
This function does a bit more than get
an “oos”; it creates the “oos” if it
hasn’t been created already. Thus, a
better name might be
createOrReturnOos.