Photo by Waldemar Brandt on Unsplash
There are only two hard things in Computer Science: cache invalidation and naming things.
– Phil Karlton
Naming is one of those unglamorous things about programming that is mostly in our peripheral vision during the learning phase. Our attention is focused on more exciting things like algorithms and graphics. This apathy usually spills over into the professional phase. It takes some catastrophe, some inexplicable behavior that gives us sleepless nights, traceable to some obscure method whose name isn’t remotely indicative of things it is actually doing, to bring naming into sharp focus as a topic worthy of attention.
Naming and abstraction are closely intertwined. Names are how we identify abstractions - those black boxes, those legos that we can click together without needing to know how they were manufactured. A good abstraction represents a single concept and a good name helps us identify that concept instantly with no further elaboration required. Good abstractions aided by good names minimize cognitive overhead and help us read code like prose says Grady Booch:
Naming things properly - so that they reflect their semantics - is often treated lightly by most developers, yet is important in capturing the essence of the abstractions we are describing. Software should be written as carefully as English prose, with consideration given to the reader as well as to the computer. - Object Oriented Design With Applications
According to The Pragmatic Programmer, avoiding misleading names is even more important than avoiding inane names:
Variable names of course, should be well chosen and meaningful. Even worse than meaningless names are misleading names. Have you ever had someone explain inconsistencies in legacy code such as, “The routine called
getData
really writes data to disk”? The human brain will repeatedly foul this up - its’ called the Stroop Effect… Names are deeply meaningful to your brain, and misleading names add chaos to your code.”
Naming system artifacts well is also a form of documentation. It lets us hone in on an area of code quickly, be it to understand and extend it or to fix an urgent problem. Using the same consistent terminology in the user interface and back end code is a huge time saver. Too many person hours and brain cells have been lost trying to map user interface elements to a backing object.
Names are not just a programming language requirement that we must grudgingly assign and be done with. They also form a compressed language that users and developers use to communicate elaborate concepts with each other. The more precise this language, the less scope for ambiguity and misinterpretation of requirements. Eric Evans recommends creating a glossary of terms at the outset, which he calls Ubiquitous Language, so that users and developers can be assured they are talking about the same things.
Names can also help identify code smells. For example, a method named FetchAndLoadData
smells of a violation of Single Responsibility Principle. A method or class shouldn’t have multiple responsibilities. Of course, not all code smells are likely to be so conscientiously labeled. It is more likely that a method named FetchData
will also load it into the database. We need to watch out for both of these smells and opportunistically refactor whenever possible. In the first instance, by spinning off a LoadData
method and renaming the original method to FetchData
, and in the second instance by spinning off a LoadData
method.
Design Patterns are another kind of ubiquitous language for developers to communicate common solution archetypes with each other. Naming classes using the pattern vocabulary (factory, adapter, singleton, visitor, etc.) helps efficiently and clearly communicate their purpose, object structure and relationships using a single word instead of resorting to diagrams and lengthy explanations. It also enables reuse of solutions that might not have been evident but for the shared vocabulary.
Conventions I usually follow in C# code are:
private int _count;
public int Length { get; set; }
OrderManager
ConfirmOrder
IOrderManager
orderManager
Looking at .NET framework code and following the same conventions shouldn’t be a bad idea. Neither is formalizing these kind of conventions in a coding guidelines document for a project, and even better, automatically enforcing them using linters. Future maintainers, which might include your future self, will be ever so grateful.
Finally, it is OK to take a little time upfront naming things. It might seem that this time is better spent writing code. But given that language shapes our thought, writing code using the wrong abstractions is a recipe for problems down the line that will be harder to fix without requiring significant design changes. Naming doesn’t come easily to even someone as accomplished as DHH. His inner dialogue, as he obsesses over a method name is both fascinating and insightful to observe.