Continuing my chapter-by-chapter review of A Philosophy of Software Design By John Ousterhout, today I’m going to review Chapter 14.

Chapter 14: Choosing Names

Good names are a form of documentation: they make code easier to understand. They reduce the need for other documentation and make it easier to detect errors.

I covered a lot of this in my previous post about comments, and I want to reiterate my support for the idea here. Naming is extremely important because any given symbol may be used in code far from the place where it is defined and documented. It should be obvious for what purpose a symbol is employed from looking at the name of it. Obviousness in the name of the symbol reduces the need to hunt down the declaration and accompanying comments and documentation.

Aside: A Pattern Language

I’ve mentioned patterns a few times, both in previous review posts on this book and in other posts besides. When talking about patterns, people often wrongly assume patterns are just about having pre-made technical solutions to common technical problems. I thought this at first. The biggest benefit of patterns is the pattern language which they introduce. The pattern language allows developers to effectively communicate in a simpler and higher-level way about the code. Naming classes according to pattern conventions conveys a huge amount of information about what the class is, what it is supposed to do, and how it can be used by the rest of the system. If I have some random class which is described as “instantiates and initializes widgets” it might not be clear what the point of the class is at all. Why use this instead of the trusty new operator? What kinds of initialization is it doing? How do I use it? Why would I use it? But if I name my class WidgetFactory suddenly most of those questions get answered because the Factory pattern definition includes and implies a lot of information that developers should be familiar with.

Not all classes are going to implement patterns and won’t be named for them, but when you have an opportunity to refactor a bit of code to become a pattern, the amount of information about purpose and usage you gain from simply using the name of the pattern in the name of your class or in the comments of it is enormous.

There are the “common” patterns from the Gang of Four book and other sources, but teams should feel free to create their own patterns to add to the pattern language of the team. So long as everybody knows what the pattern is and how to use it, it brings value. The pattern language is very similar in purpose to the Ubiquitous Language of the project, but this language is purely technical and has nothing to do with the business domain.

Bad names cause bugs

I mentioned in my last post a problem I had with our RabbitMQ code where some of the variables were in seconds and some were in milliseconds, which created bugs. The problem was solved by appending an “S” or “MS” suffix to these variables to indicate units. Some people see this kind of annotation and reflexively say things like “OMG Hungarian Notation is terrible” because they’ve seen stupidity like integers being prefaced with “i” or strings being prefaced with “sz”. You don’t need to tell me the type of the variable because the declaration and your IDE will do that for me. You need to tell me the purpose of the variable, which includes the units.

If different variable names had been used for the different kinds of blocks, such as fileBlock and diskBlock, it’s unlikely that the error would have happened.

Sometimes naming can make things more obvious, a technique I use all the time. Other times, and I find this is a much more powerful though a much less frequently used option, is to actually have separate types for these. Consider something like this, in C#:

public struct FileBlock
{
    public int Block { get; }

    public FileBlock(int block) { Block = block; }
}

public struct DiskBlock
{
    public int Block { get; }

    public DiskBlock(int block) { Block = block; }
}

Value Object types like these shouldn’t have a big impact on performance and the compiler will catch all sorts of bugs for you where one is attempted to be used in place of the other. Adding an implicit cast operator to convert these types to int will help with usability and integrating these new types into old code. The Immutable Object pattern is optional depending on how these values are used but is probably an improvement in many cases.

Even in C, where John seems to be writing all his examples, would allow you to define a struct like this and either pass the struct by value or easily cast the struct contents to int with no loss of performance, but lots of opportunities for the compiler to catch bugs long before they got into runtime.

Create an image

“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?”

This is, I think, a pretty good heuristic to use. I might say that this mental image should include type information as well, after all any good IDE or code editor will immediately tell you the type of the symbol wherever you are, and sometimes the declared type along with the name can give a heck of a lot of information as a pair:

Product first;

In this case, the name “first” doesn’t tell us much, but by including the type information Product we can infer that there’s a collection of products somewhere. If there were multiple collections of Products around, we might want to extend this name to tell which collection the product is first in, but if we have only a single collection in the surrounding code we can infer that this is the first one from that collection. My philosophy, and I’ll reiterate this further down, is that the type is part of the symbol, and often times the name of the symbol only makes sense (and only needs to make sense) in the context of the type information. If you want to run John’s exercise with just the name and without the type information, which is a perfectly fine exercise to run, you’ll likely come up with different naming schemes than I use.

names become unwieldy if they contain more than two or three words

Maybe. I’ve seen cases where variable names do get really crazy long and that becomes a headache. I’m not sure I’d ever put in a strict word limit though. Keep variable names short by leveraging type information and usage context where possible.

Names should be precise

John shows this code and description:

int IndexletManager::getCount();

The term “count” is too generic: count of what? … A more precise name like getActiveIndexlets or numIndexlets would be better.

First, I’d argue that the name IndexletManager implies that we’re counting “indexlets”, though renaming it to something more descriptive like IndexletCollection would be more clear that we’re talking about a collection of indexlets and the count would be the count of the collection. His first suggestion, “getActiveIndexlets” isn’t semantically equivalent to “getCount”, the first method name implies a count of all indexlets while the replacement name implies use of a filter. The name “numIndexlets” doesn’t strike me as an improvement over the original, “num” is no more descriptive than “count”. The only real improvement from “getCount” to “numIndexlets” is that we’ve added the word “Indexlets” to the name of the method, but again I suggest that the fact we’re counting indexlets is obvious from the name of the IndexletManager class. What else could we possibly be counting? John gives a better example below:

A project building a GUI text editor used the names x and y to refer to the position of a character in a file. … they might also represent the coordinates (in pixels) …

The names x and y don’t obviously give any real information, but when referenced in an appropriate context they might. For example, a type CharacterFilePosition with X and Y properties makes it more clear that we’re talking about a character’s position, while a type PixelPosition with X and Y properties is clear that we’re dealing with pixels. When you think about all the relevant information you might need when talking about the position of items on a screen, it’s hard to even imagine better variable names that say enough without becoming hugely long! If we’re talking about character position on a screen, that can be very different when using a fixed-width font or a variable-width font, and when you include kearning things get even more crazy. And then you have to ask where exactly is the origin? Top-Left corner of the screen? Bottom-Left corner? Or is the position relative to the corner of the document, which might be off screen because of scrollbars? A variable name pixelsFromTopLeftCornerOfDocument has all this relevant information, but definitely violates John’s preference of variable names not being more than two or three words. If we’re talking about character position in a line, John’s suggestion of charIndex does seem to work well enough, because we can assume the index is from the left.

Next John talks about a blinking cursor in a document:

The name blinkStatus doesn’t convey enough information … The name cursorVisible conveys more information. … The word “blink” is no longer in the name so readers will have to consult the documentation if they want to know why the cursor isn’t always visible.

Why not something like “blinkingCursorVisible” to include all that relevant information without delegating to some other source of information? Why not just tell the developer what the value is instead of sending them on a goose-chase to some other source of information?

I do agree, however, that “status” is one of those words which is over-used for dubious value. The use of the word “status” in your code, like “Manager” and several others, is frequently an anti-pattern.

it’s fine to use generic names like i and j as loop iteration variables, as long as the loops only span a few lines of code.

And here I think we finally come to an inflection point. In the first couple chapters of my review John talked about how modules should be “deep” with relatively short interfaces but relatively long implementations. Longer methods and bigger classes were often an improvement, because they decrease the total number of modules which leads to a decrease in complexity. I, on the other hand, suggested exactly the opposite: a larger number of smaller methods and classes was often better for complexity. This exact point is one of the biggest reasons why I have come to my conclusion.

A small method allows you to limit the scope of a variable or a concept, allows you to keep the entire thing in your mind at once, and allows you easy access to the complete context information of an operation. The variables i and j in a loop are obvious because their entire context is laid out before you. The names first or next by themselves don’t include enough information, but in a 5 line method which operates on a collection of products, they do. A method GetCount doesn’t describe enough in isolation, while the same method on a class named WidgetCollection implies much more than it says, if the WidgetCollection class is sufficiently single-purpose that the idea of a count would be unambiguous.

We can’t look at these ideas in isolation. It’s all part of a combined whole: Classes need to give context to methods. Methods need to provide scope and context to variables and operation concepts. By keeping our modules simple and focused, we reduce the effort to write comments and find descriptive variable names.

If you find it difficult to come up with a name for a particular variable that is precise, intuitive and not too long, this is a red flag.

Whole-hearted agreement. We could expand this point out into an entire chapter by itself and I feel a little disappointed that this chapter on naming is so short.

Conclusion

It’s been said that there are only two hard problems in computer science: cache invalidation and naming things. Naming things is certainly difficult, and it’s hard to do the topic any justice in a single short book chapter or a single short review of that chapter. Symbol names are almost always shorter by necessity than we want them to be, so we need to rely on other sources of information to make their use obvious: The pattern language and the project’s Ubiquitous Language can help to offload a lot of meaning to a shared glossary, and contextual clues should help to fill in the rest.