Better naming can make this quick, easy, and unambiguous to read.
There are several factors that contribute to this problem.
The first is that code does not provide the same context that human languages convey.
Words like “the” and “of” are often omitted in code, and discerning a word’s part of speech — a preliminary step in inferring its meaning — is often impossible without a deeper understanding of the code surrounding it.
Sometimes this is virtually unavoidable, but when the surrounding code contains even more examples of the same ambiguity, readability suffers greatly.
If you look at the following two phrases:Get the parsing parametersParse the parametersAgain, everything is clear.
But in our example above, we simply see parseParams, which doesn’t disambiguate between the two.
The second factor, which compounds the first, is that engineers are likely to noun verbs, verb nouns, and adjective everything — even when there are already alternate forms of the word in question.
For example, we use the word “fetch” as both a verb and a noun in close proximity.
This is often useful when speaking about code.
If I have a function called “fetch”, it may be clearer for me to say “when fetch times out…” than to say “when fetching times out…”.
It’s natural to refer to functionality by the names of its pieces, even when that bends the rules of grammar.
But if I translate the resulting ideas directly into code as fetchArgs, I’ve put an unnecessary cognitive burden on future readers.
The context is coming from inside your headWhat makes this problem so difficult to proactively identify is that while your code lacks the structural context provided by ordinary speech, your brain, as the author, has all the context you need.
And as far as our minds are concerned, it doesn’t really matter where the context came from.
Once we know what something is supposed to mean, that’s how we’ll see it.
When you look at your code, you already know that fetchData is the data that you fetched, that failRequest handles requests that failed, and that backoffDelay is the number of seconds you should delay for backoff.
So it usually won’t occur to you that these names are ambiguous.
If you read them aloud, you’ll automatically stress the right syllables and insert the right pauses to fit with your original intent, and unless you specifically concentrate on the problem, you won’t even realize you’re doing it.
But when someone else reads your code (or you, three months later), they won’t have that benefit.
And the problem increases with multiple ambiguities in close proximity.
If, in understanding the context behind one name, the reader hits another ambiguous name, they now have to rule out every possible combination of meanings.
Given that we have to do this multiple times for every class, function, or other context, we should take care to minimize this effort.
Keep in mind that names propagate.
A badly chosen name will not only make your code harder to read, but will also make others’ code harder to read when they reference it.
And it will never be easier to rename a method, class, or variable than when it is first introduced.
Solving the problemThere are several things you can do to clarify ambiguous names.
Let’s look at some techniques, with examples:1.
Add more wordsThis is often the easiest way to clarify a name.
Sometimes all you need is a connective.
In other cases, you can add words that pull in additional context.
In the example above, retry can be rewritten as shouldRetry, and backoff can become increaseBackoff.
Use different formsThis is a way to borrow from what works in human languages.
In the example, retryError is clearer as retriableError — and clearer still as isRetriableError.
Use different wordsA name like backoffDelay is a little tricky to clarify.
If we used delayForBackoff, then “delay” still may look like a verb.
Instead, we can switch to a word that removes the ambiguity entirely, e.
Here’s the original example, with all the ambiguous names cleared up, and no other changes.
Even without comments, this code is now far easier to comprehend, because we never have to guess at the general meaning of the names used.
Noticing the problemSolving this problem is usually not difficult.
But as I already mentioned, noticing it often is.
One technique I’ve used with some success is to read back over my code and randomly stress different syllables.
Sometimes that will jar me into noticing a different interpretation.
It’s much easier, however, to notice these ambiguities as a reviewer.
When you review code, you don’t have all of the implicit context the author had.
So the next time you’re reviewing code, and you notice that it seems opaque, take a look at how things are named and see if they can be interpreted more than one way.