Once you set aside dad jokes, there is one main universal problem remaining in software: naming things. Naming is modeling. It is core to our job. It is difficult, and subject to endless discussions. Thankfully, because it is what makes us hardly replaced by robots. I’ll list a few heuristics I use, that I rarely see in the wild.
We name things to differentiate them. If there was only one thing in the universe, naming wouldn’t exist.
We name things to differentiate them in a context. We don’t need to differentiate things beyond the context we consider them. In a room, we can talk about the table without differentiating it from all the tables in the world. The discussion also helps us know we are talking about furniture, and not the table of contents of the book sitting on it, or Mendeleev’s table hanging on the wall.
Speaking about code, it means we only need to differentiate names within a given name space. The variable i is perfectly valid inside a small loop. We don’t need to differentiate a variable of a function from all variables of all functions.
I’ll introduce here the concept of scannability, that I got from Geepaw Hill. To make it short, we spend most of our time reading code. In particular, finding which piece of code has the impact we’re looking for. Among all concepts readability covers, I try to optimize code so that devs can quickly find where code does this or that. One must be able to skim through code, enter its sub parts recursively in an effective and efficient way, in order to find the impact they’re looking for.
In particular, one must be able to read code as naturally as possible. Code must tell a story, with simple words and sentences, short paragraphs, in the business language.
Because I like scannable code, and thus short names, I try to avoid details that are not necessary to differentiation in the context.
Corollary : the smaller the context of a name, the shorter the name. The bigger the context, the longer the name.
Corollary : when the context of a name grows bigger, the name should grow longer as well. When the context shrinks, the name should shrink as well.
That is why I try to reconsider the content of a piece of code when its scope changes. In particular, I reevaluate all names the code contains, exposes, or interacts with.
For example, if a variable i is OK in a small loop, it’s relevant to rename it as rowIndex and columnIndex inside 2 nested loops. It can even become fields of a Coordinates instance when variables reach the class fields.
For example, I can call a class RandomLoadBalancingStrategy because I need to differentiate it from RoundRobinLoadBalancingStrategy or RandomNamingStrategy globally. But locally, in the load balancer, naming the variable strategy is enough, once the strategy was selected.
We all like names allowing us to guess what things correspond to, through cultural or synesthetic association. But many names are arbitrary. For example, we all know what a dog or cat is, despite the fact that these names have no relationship with the aspect or the sound of these animals.
DDDers try to use business words in the code. Still, we can need additional words. In particular, we often map words like group, category, space, set, etc… on very precise dimensions of the business or the technical solution we implement. But in my experience, no objective criteria helps us differentiate those words outside of a given context. And that’s OK, because jargon has great value in mutual understanding within a group.
However, beware of jargon. When a group’s jargon is used with the rest of the world, conventions can be harmful when they are implicit.
For example, each team has its own definition of the role of a BA. Let’s suppose team tiger considers BAs as testers, and team lego considers them as domain experts participating in use case distillation. Each team has its jargon. If these teams need to collaborate, and thus agree on a collaboration protocol, there will be much misunderstanding if they don’t make these central roles explicit to each other.
Naming by contract or content
We can name a piece of code by
- What it does, i.e. it’s content. For instance, saveIfNotExists or persistIfValid
- The reason why we want to use it. For instance, persist or save.
I prefer the second option by far. First because it makes names shorter, as my dishonest example proves undoubtedly. We can also invoke a more theoric point of view, like the fact that reducing coupling encourages encapsulation, so that the caller doesn’t need to know the interns of the called code.
But I prefer option 2 for more concrete reasons:
- Names based on why instead of how tend to be more stable. They don’t change, or less, when the content of the code evolves.
- By focusing on the caller when naming things, we favor story telling, and thus scannability.
I’m not the only one to prefer that option, but I can’t find which hero gave me these arguments. Clues are welcome.
Hungarian notation from frameworks
We often see suffix conventions in code bases. For example, in spring, REST servers are suffixed with Controller, services, as in DDD services, are suffixed with Service JPA entities with Entity, json DTOs with DTO, and so on. But these suffixes make humans stumble when they try to scan code.
From my point of view, such suffixes look like the Hungarian notation we had in the 2nd millennium. In this notation we used to prefix variables with single letters to make their type explicit (e.g. s for string, i for integer…). Though changing people is hard, teams got rid of this habit, as languages, development environments, culture, and teams setup, evolved.
Similarly, I think the benefits of framework suffixes are very limited, typically in an IDE. We could choose to highlight other dimensions of things in their names, like their reliability or performance. And not repeat information we may already have in folders, name spaces, class hierarchies, tags or annotations, or any other source of information that is already there. Yes, I’m looking at living documentation.
I think this convention comes from the universal way spring-like framework tutorials are written. It has become second nature for everyone. These suffixes can be useful, but they are certainly not useful in every case I bump into.
In general, in my code, I only introduce Hungarian suffixes when painfully necessary, and I can’t find a decent alternative. Not before.
And I do sometimes. For example, in hexagonal architecture by my book, despite entities and domain models being in distinct packages, environments may struggle managing homonyms. We often have to deduplicate names for classes that are mapped one for one, independently of their packages. Unless the domain or the architecture guided names towards natural differentiation, which finally occurs more often than not when the domain makes its way into the code.
As I rarely code on my own, and teams generally cling to Hungarian suffixes, I clearly won’t die on that hill. We often have higher priorities in terms of changing points of views.
Finally, I try to get rid of words that bring nothing more to the table than “I didn’t take the time to pick an actual name”, like tools, utils, and all words from the sarcastic and genius arolla dice. When one finds these words in names, they almost always realize that just deleting them doesn’t change a bit of their meaning. And if you encounter a IUtilsManagerTools, which can thus be replaced with nothing, it’s really time to pause and think.
Of course, this section falls down when these empty words take a strong, clear, and explicit meaning in a formal jargon, a convention.
Naming is an important part of modeling business and architecture. It also allows our code to be habitable, for our future selves and colleagues. It is a delicate art, that makes us navigate on a steep and windy ridge. No rule is absolute. We try to optimize along axes like code scannability, mutual understanding among the team and with users, or model plasticity. We iterate, we discuss, it depends™. My heuristics help me make decisions and structure discussions with the team.
What are your naming heuristics?