Why Naming is the Hardest Programming Problem
Adam and I were pairing, working through a critical piece of our program's architecture. The cursor sat blinking. We had been staring at it in awkward silence. We were stumped.
"I'm leaning toward 'version'", I said, as much to break the silence as anything else.
Adam considered for a moment, then replied, "Well, that might cause confusion. Someone might expect it to be a semantic version or a sequential integer. What if we go with 'reference'?"
"Okay, I agree about 'version,' but 'reference' sounds too generic. What about 'revision'? I feel like that captures the fact that this is a change."
We went on like this for a good thirty minutes, even considering rearchitecting parts of the code as a result of the chosen name. In the end, we decided to table the discussion until the following day.
Later that evening I found myself wondering if we were over-engineering. Why spend so much time figuring out the name? After all, the compiler really doesn't care. Why were we expending so much energy on such a small detail? Glancing at my bookshelf, I saw books by the philosophers Quine, Aristotle, Pierce, and Russell, and was immediately struck by this: All of those philosophers (and indeed many more) struggled mightily with issues of naming.
And suddenly I understood not only why naming is the hardest problem in programming, but why it is right that this should be the hardest problem.
A Tale of a Different Adam
Adam, my pairing buddy, isn't the only Adam to wrestle with naming.
The traditions of Judaism, Christianity, and Islam all share a common creation story. In the Judeo-Christian telling of the story, the first human (Adam) is tasked by God (Elohim) with naming all of the animal species in the Garden of Eden. As Elohim parades each past Adam, Adam assigns a name. Naming is hard, right? But for the ancient Semitic reader, this responsibility is tremendous. Because in this culture, names are more than just labels. They are meaningful anchors that play both a descriptive and prescriptive role.
For example, the name Adam literally means dirt or earth, a name that signifies Elohim's act of fashioning Adam from mud, and also gives a nod to Adam's role. Elohim, in asking Adam to name the animals, gives the care and uptake of the earth over to humankind.
What does any of this have to do with programming? Simply this: The ancient Semitic cultures understood exactly how powerful naming is, and also why it is powerful. This is a lesson we as programmers can take to heart.
(For an academic treatment of naming in ancient Middle Eastern cultures, see Umberto Moshe David Cassuto's A Commentary on the Book of Exodus, esp. 36ff)
What's In a Name?
Names play three potential roles:
Often times, we focus on the name as the anchor or label. Saul Kripke, a contemporary analytic philosopher, is keenly interested in the relationship between the name (as anchor) and the thing being named. The act of naming something, he points out, is a profound moment. Something that had no reference is suddenly directly addressable. He calls this act of naming the initial baptism (or primal baptism) to signify how momentous naming is. Philosopher Slavoj Žižek summarizes this:
[A] word is connected to an object or a set of objects through an act of 'primal baptism', and this link maintains itself even if the cluster of descriptive features which initially determined the meaning of the word changes completely.
(The Sublime Object of Ideology, 1989, p. 90)
We, as programmers, intuitively understand this first role of the name. And we, more than most, understand the depth of what Kripke saw. We can even illustrate it in just a couple of lines of code:
var myHat = new Hat(); myHat.color = blue; (new Hat()).color = red;
What does our code do? Well, first, we create a new
myHat. We assign
myHat the color blue. Next, we create another
Hat and make it red.
Now observe the linguistic gymnastics we have to play to describe this code.
myHat is blue. The other
Hat is red. See what we are doing with our words? One thing we can talk about specifically (
myHat). The other, we can only identify by directing the reader to the context. The other hat; another hat; the second hat. All of these frame the second object as the one left when we mentally eliminate
myHat as the target of the reference.
Simply by giving
myHat a name, we have privileged its position in our code because we have made it directly addressable.
The second thing a name can do is describe something. You probably noticed that when we wrote this line of code, we actually worked with two names:
var myHat = new Hat();
(okay, it could be argued that there are 2, 4, 7, or 8 names in the line above.... but we need to stay focused.)
Borrowing the sort of "primal baptism" attitude, our code above rather majestically said, "the name
myHat shall hereby indicate a particular instance of the thing called
Just for fun, if we'd said
const myHat =... we could get even more fancy: "the name
myHat shall, 'til death or garbage collection, henceforth indicate a particular instance of the thing called
We might also take the name
Hat to be a descriptive term. It plays some role in helping us mentally correlate a thing (
Hat) with its properties (
color). And while my variable name
myHat is not particularly creative, it too is descriptive. If I inserted 200 lines of code between the
var myHat = new Hat(); line and the
myHat.color = blue, chances are good that upon reading through that code, you would still assume that
myHat.color is assigning a color to a
Just as the ancient Middle Eastern cultures understood Adam as descriptive, so we understand
myHat as descriptive. The name itself tells us something about the object it anchors.
What other properties or methods might belong on our
Hat object? Take a moment and think of a few.
Very likely, the sorts of attributes we each came up with were things like
material, or perhaps feeling patriotic, even methods like
Hat.stickAFeatherIn(feather). We did this because we assumed
Hat was describing a hat. (And that it was not, for example, an acronym for "Highly Accurate Telemetry")
This illustrates what I believe is the most profound part of naming: It is prescriptive. That is to say, the name of a thing holds power over our cognition. It shapes us toward a particular understanding of something. Let me illustrate by rewriting the example:
var zw5 = new m9p33(); zw5.color = blue;
We still have the anchors (
m9p33). We still can describe these by saying that our
m9p33 instance named
zw5 has the
color set to
blue. But if we each came up with a list of five properties and methods for this object, we'd likely overlap on very little (if anything). Why? Because the names don't set any cognitive predisposition for us. They don't prescribe a way of thinking about an
m9p33 or a
Or we can contrast this with an example moving the opposite direction:
var http = server.Listen(ip, port);
The intuitive understanding (assuming you work with network services) is that we've just created a new server that is going to be used for HTTP.
Notice that the instance name
http is prescribing for us an understanding that goes beyond what is prescribed by
server.Listen(). Just by naming it
http, I have influenced your understanding of what the code is to be used for.
Now, as we use that
http variable, we will begin to use it as an HTTP server. The name has told us what to do with it.
All names work as anchors, but it takes effort to have them also act as description and prescriptions.
Conclusion: Naming Should Be Hard
Code is a form of dual expression. That is, it has two "audiences". First, it is a set of instructions that can be translated into an executable process. Second, it is a form of human-to-human communication (even if the human I am writing for is only future me).
Failing to write code with the human audience in mind is failing to write good code.
Good names are therefore a qualitative feature of code. The names won't just shape how we reference objects, classes, variables and so on. They shape expectations of what these things do (description). And they will shape how we evolve our code over time (prescription). As a natural result, doing a good job architecting code entails thoughtful and deliberate naming practices. Naming, in short, should be hard.