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:
- Anchor
- Description
- Prescription
Anchoring
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 Hat
named 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.
Describing
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 Hat
"
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 Hat
".
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 Hat
.
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.
Prescribing
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 size
, brim
, 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 (zw5
, 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 zw5
.
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.