Archiv des Autors: sokai
“Naming Things” is a Poor Name for Naming Things • Buttondown
Feb. 16, 2021, 4:02 p.m.
It’s been a while! Let’s just say the past couple of weeks have not been kind to me and leave it at that.
So, newsletter. I got 40% through writing about book recommendations, then read Why Is Naming Things Hard? and threw it all out to talk about naming things instead.1 So let’s talk about naming things!
Why is naming things hard? Neil sums it up like this:
And naming things is hard because of this compression. English isn’t precise like C++, so compressing from a precise to an ambiguous language increases losses. Another big reason is that choosing the most important things to include is hard. You’ll notice that most disagreements about naming fall on these two axes: either the name isn’t just right for what you want to do … or the name doesn’t capture important parts of the functionality.
This doesn’t tell the whole story. Neil is only talking about why naming as identifiers is hard. The problem with “naming things” is that it covers a bunch of related concepts, each that are hard in their own way. We’re not only faced with the individual challenges of naming things but also the challenge of realizing there are different things in the first place! Most people who talk about “naming things” don’t do this. They talk about a particular kind of “naming things”, often implicitly, as if that one kind covers the whole domain.
Naming things is hard because it itself is ambiguous. Here are some of the things it could mean:
Naming as Tokenizing
The translation between computer and human identifiers, such that the human can construct a mental model of the program with the help of the names. This is the kind of naming things that Neil talks about in his piece. It’s going from f(x) to http_get(request). The name “chunks” the program semantics into an easily identifiable form.
The problems here are as Neil said, though not 100% of the time. In some cases, English can be more precise than code, when multiple possible names map to the same code. This happens when the same code can serve different purposes. Is x = x + 1 incrementing a counter, or iterating through a list, or giving you unique identifiers? It could be any of them. The only difference is what name you choose.
But the rest of the critiques still hold. You’re compressing a lot of program semantics into a small name with bad grammar.
Naming as Domain Modeling
This is naming as going the other way, translating the human concepts into programming semantics. There are going to be more fundamental concepts in the machine-view than the human-view, because the machine-view needs to work as both a model of the human world and also as a correct program. Or worse, a correct maintainable program.
Consider, I don’t know, an event calendar system. You want to have an event repeat on the second Thursday of every month except November. Think of all the domain concepts you’d need to introduce to accurately capture that one sentence: Event, EventInstance, EventSchedule, SchedulingRule, LastDayOfMonthRule, I don’t know what else, you probably thought of it in a totally different way. None of this matters to the human. To the human, “second Thursday of every month except November” is barely more complex than “every day”.2 To the machine, this is a huge expansion of complexity.
And since we’re programming the machine, all of those names become human concepts again. We need to give them names so we can refer to them, tokenize our code, and discuss them with outs. We need to give them clear names that make it clear why this extra machine-complexity is necessary to capture the human domain. That’s hard.
Naming as Bounding
This is more a purpose of naming than a kind of naming, but interesting enough to discuss here. Beyond just describing what a thing is, a name should describe what a thing isn’t. It needs to exclude. We do this all the time with naming, as a User entity trivially excludes “chairs”. Things get harder when there are concepts that may or may not fit, depending on where you draw the lines. If we’re tracking books, does that include ebooks? Audiobooks? Books on cassette? Torah scrolls?
(If you’re a library, maybe this doesn’t matter, they’re all Reservables. But some reference books cannot leave the library. Should they be excluded? If not, then Reservables the wrong name. So what’s the right name?)
Naming as Codifying
If x must be a whole number, we say it has an integer type. If x can be a whole number or nothing at all, what type does it have? Is it a nullable type? An optional? A maybe? A voidable?
Several different communities saw the same thing, “types that may not have a value”, and codified it all with a name. Once there’s a name, it becomes possible to talk about it. When should you use optional types? What are the alternatives? Does type system X support them? Codifying creates a topic of discussion.
This makes naming-as-codifying a very sensitive process. Once you’ve given it a name, that name becomes how people introduce the concept. Unlike codebases, you can’t change a codification. A misleading name will be confusing people for the rest of your life. The stakes for finding good names is much higher.
One reason codifying annoys me: we codify because we see patterns. If a community needs “types that may not have a value” then they will codify it. You get a jargon-fracture where some concept has ten different names, each with their own literature and community. This makes researching some topics hell.
Naming as Creation
This is one of the weirdest and most important kinds of naming. This is taking a collection of experiences, ideas, and touchpoints and condensing it into a thing. Carving out a section of the noosphere and saying “this is now X.” This is subtly different from naming-as-codifying. That’s when there’s this… whatever everybody already recognizes as a discrete whatever, and they’re trying to find a name for it. Here, people don’t think about the thing because it doesn’t exist as a concept yet. The act of naming is what turns it into something we can think about. But the two can overlap a lot.
I don’t know if a term already exists for this, but I’ve run into it often enough that I internally codify it as “articulating”. Kent Beck and Ward Cunningham first articulated patterns.3 Individual patterns existed before them, and people used them, but those two first made “patterns” a general software concept. Michael Jackson articulated the machine-world dichotomy and problem frames. Bertrand Meyer articulated the open-closed principle. Some of these are more debatable than others, whether they were entirely new concepts or codifications of existing ones, but the fundamental idea here is the naming itself generating a new space of knowledge.
I don’t think I’m describing this very well. But it’s something I think is really important, and I spend a lot of time trying to articulate new things. On Emulation is one example, as is Constructive vs Predicative Data. The hard part is connecting the dots and realizing there’s something there that needs a name. I find that once I’ve done that, explaining the ideas is pretty easy. Some of them are in “obvious once you hear it” territory.
Also naming them is hard, because names are boundaries and it’s really hard to tell what needs to be included in the articulation.
There’s probably a lot more ways of “naming things” I couldn’t think of. They’re probably all hard, too. Naming things is hard because it could mean a bunch of different hard things. But you don’t realize that from just “naming things”. I can’t think of a better name for “naming things”, though. Naming things is hard.
-
Kidding! I still have the draft and am still working on it. ↩
-
More complex as a concept, I mean. It’d take more mental overhead to actually follow. But it’s barely more effort to understand. ↩
-
Ish. They translated the idea from Christopher Alexanders work on patterns in architecture. But I don’t think this makes what they did any less of an articulation. If anything, it makes a great argument for why interdisciplinary study is so valuable: it inspires articulation. ↩
Why Is Naming Things Hard?
An interesting shift happens once you realise you’re writing code for humans to read, and not just for machines to execute.
One big change is that writing clearly takes priority over correct code.1
Reading code involves building up a mental model of what each thing should do, and how these things interact with each other. The best code, however, short-circuits reading everything. Instead of reading the implementation, you use variable and function names, comments, and other clues to figure things out.2
This is chunking. You combine things together, and remember them together. For example, a Chess Opening, like the Queen’s Gambit, is a chunk. So is a forEach loop: just by the name, you can figure out that it’s going to do something to every element of an array.
Once you’ve built a chunk for a forEach loop, you don’t have to read the implementation of forEach to understand what it will do. This is very powerful, since it allows you to come back to the code, without having to read the implementation of everything again.
Thus, a good name is a chunk that encodes important information about what the thing does.
We’re compressing information into the name. This is almost always a lossy compression. Further, the more lines to compress, the more losses you incur, since you want your names to be no longer than 80 characters.
The goal of good naming is to minimize this loss.
And naming things is hard because of this compression. English isn’t precise like C++, so compressing from a precise to an ambiguous language increases losses. Another big reason is that choosing the most important things to include is hard. You’ll notice that most disagreements about naming fall on these two axes: either the name isn’t just right for what you want to do (does it assign or setup or link or mangle?). Or the name doesn’t capture important parts of the functionality.
I think this is the generating function for most best practices I’ve heard of, and thus lots more valuable than one specific guidance.
Generating best practices
-
Make functions do one thing
When a function does multiple things, the name needs to encode lots more information, like
link_user_to_trial_and_enable_downloads_and_set_charge_date(). That’s a long name, and a sign of losing too much information: what semantics should the name expose about the charge date, and enabling downloads?These side effects are hard to encode for in the name.
-
Keep functions small
Long functions, like functions doing lots of things, need to encode lots of information into the name. And since the name length is bounded, you lose a lot more information in compressing this long name, which leads to poorer names.
-
Avoid meaningless names
Since we’re writing code to be read by humans, names that don’t compress any information don’t help us build chunks. This makes it harder to read, since you have to go down one layer of abstraction. You need to parse the implementation to understand what that piece of code does.
-
Avoid too abstract names
do_things()is a suitable name for any function, but the compression is too lossy to be useful - you lose almost all information, and need to parse the body to figure out what it does. Not great. It’s almost a meaningless name. -
Be consistent
When you build similar chunks throughout your code base, it becomes easier to go up another layer of abstraction, by chunking the chunks. For example, continuing the trial management system example, you’ll have 3 things in place:
link_user_to_trial_agreement_given_email()enable_research_downloads_for_user()set_charge_date_for_user()
Since they’re operating in the same domain, you can go up one level tosetup_user_for_trial()that does all three.
Disrupting best practices
The generator of best practices allows you to reason about cases where best practices aren’t the best way forward.
For example, consider being consistent, and naming variables using camelCase. Your entire codebase uses camelCase. You’re writing a new function that’s very non-standard. It does arcane stuff that doesn’t fit into your existing model. You need it, because legacy integration/business requirements/etc. In effect, you don’t want anyone to gloss over it when they read it.
So, do you stay consistent, and keep it in camelCase? Or, do you switch to snake_case?
The goal is to keep things clear. I would switch to snake_case to show how it’s different from the rest, and have some comments explaining the arcane things it does.3
Precision with words
Spoken languages are very imprecise. There’s lots of ambiguity, double meanings, and sentences that don’t mean anything.
Code, however, needs to be precise. Like we saw above, this is a source of trouble, since we’re trying to compress a precise language into an imprecise one, which makes searching for the right English words harder.
These ideas apply to naming things outside of code, too! When you have a name for something, it suddenly becomes legible.
Consider: You see some people around you who test if they can do something once, and drop it if they fail. Some others keep trying until they learn how to do it. The first group prizes its intelligence, while the second group prizes its hard work. Then, when you hear about the Fixed vs Growth mindset, things immediately click.
Choosing the right name here is hard too. Every best practice that lost its nuance over time can be attributed to a name that didn’t encode the necessary information. People passed these ideas on without explaining the nuances.
Or, consider equating two names. “Abortion is murder” - equating two names that aren’t the same creates an interesting dynamic between emotions and the truth. Each name has different connotations, and on combining them together, you’re passing on the connotations of one onto the other. This isn’t necessarily a good thing. You have to be very careful of the equality operator in an imprecise language.
Conclusion
Optimise for clear, easy to understand code. The cost of reading it is higher than the cost of executing it.
To do this, leverage chunking: write names that are meaningful, and encode as much information as possible into the name.
However, writing meaningful names is a lossy compression. Every naming maxim follows from minimising this loss.
And finally, the same idea applies to naming things in the real world. Except, it’s even harder, since it’s encoding from an imprecise to another imprecise language. Code, atleast, was unambiguous in its execution.

Thanks to Soumya for reading drafts of this.
Have feedback? You can reply to this post on Twitter.
-
You might contest this, that correctness comes first, but if it’s clear to read, PR reviews will catch correctness bugs quickly. Or, if no-one catches mistakes, and then things break, it’s easier for your colleagues to fix things if they can read your code clearly.
Footnote to footnote: This is an instance of taking ideas seriously. ↩ -
Sometimes, poor naming gets in the way, generating an incorrect model of how things work. Here, you have no choice but to read the implementation. ↩
-
Ideally, you’d fix things properly so there isn’t a need for something like this. But, the default is to have things like these littered over a huge codebase. ↩
sokai starred dangrossman/daterangepicker
JavaScript Date Range, Date and Time Picker Component
JavaScript 10.6k Updated Jan 26
sokai starred kevinpapst/TablerBundle-Demo
Demo application for the TablerBundle theme bundle for Symfony
Twig 6 Updated Jan 23