Shakespeare said, “a rose by any other name…” To him, a name was just a label for something deeply intrinsic within the thing itself.
But in software, it’s the opposite. There is no intrinsic-ness to any software objects except what they do and so our names become more than labels, they become expressions of the objects we create and therefore it’s important for us to use names that reveal the intention of what we’re trying to do.
We call this “intention revealing names” and I often find that the more straightforward a name I can use for something the better. I like verbose names, names that clearly say what they do. They can even be little phrases or sentences. I use camelCase.
When naming things I start with what it is I’m trying to accomplish. I’m always implementing some kind of feature in the system and so I focus on what is my goal for that feature is. Then I think about the steps involved in creating that behavior or feature. Again, each step in that behavior probably has sub-steps, and again I start breaking them down based upon what they want to accomplish.
How far do I take this? Generally, as far as possible. I want to name every little bit of behavior in my system because it gives me an opportunity to document what I’m doing and I far prefer using the names of methods to document what they do rather than creating a separate document to describe this or even adding comments near the code to describe what the code does.
We want our code to be expressive. In order to do this, we want to make our objects singular and purposeful around the task or feature that they are implementing. There are many guidelines for naming as well as standards and conventions that I won’t go into here.
In my last blog post, I talked about the importance of hiding implementation details and that’s especially important in how we name things. We want to name behaviors based upon what they do not say or imply how they do it. This can be a bit tricky because we normally don’t make this distinction when we are speaking to each other.
But, at some point the pedal has to hit the metal, we have to do the thing that we really want to do so how do we make that happen?
For example, if I want to sort a document in multiple places in my program I may simply call sort generically but my sort() method may implement a particular way of sorting like a quickSort. If I find out later that based on my usage, a different sort would be better then I can simply change sort() to use that different implementation and all of my clients or callers will benefit from using the improved implementation without needing to be changed at all.
If instead, when I wanted to implement sort in my code I called it what it really was, quickSort() then, if I realized I wanted to extend it later, I have to change those calls from “quickSort()” to “sort()” in multiple places.
Situations like these drive us to creating generic behaviors often implemented through interfaces that then resolve to specific implementations. This also opens opportunities for polymorphism in code.
I often find that when I scope methods locally I prefer to use more generic terms for their behaviors. When I scope methods more globally, in other words calling external methods, I prefer to name those methods to be more specific and their names tend to be longer. Again, these are just generalizations and I can think of exceptions to these rules.
Good names for data and behavior are our first line of defense for understanding a program. Perhaps this is the most important skill that a developer has, to be able to come up with good, understandable names for the components that they are building. It is certainly a quality that is highly appreciated by anyone who is charged with maintaining that code.
Note: This blog post is based on a section in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software.
Previous Post: « Hide How with What
Next Post: Keep Code Testable »