There is this one quote that I hear often repeated.
There are only two hard things in Computer Science: cache invalidation and naming things. -- Phil Karlton
But is it really true? Yes, finding the perfect name for something is difficult. But let us take a step back and think about why we are naming things and why we are looking for good names.
Names are for humans
The computer usually does not care about how things are named. Therefore the name is just for the human reading and modifying the code.
Therefore, when choosing a name for something we have the goal to choose descriptive and unambiguous names.
How do we do this?
Make names human
- Use pronounceable names.
- Use searchable names.
- Avoid encodings. Don't append prefixes or type information.
Having human-readable names make them easier to remember, easier to think about and also easier to discuss with a college.
One word per concept
- Make meaningful distinction.
- Having a clear terminology is key to not get confused.
Its more important to be consistent then to be perfect. The only thing that matters is that someone joining the project can learn what the concepts behind things are and that they can infer from one thing to another what it might be about.
I do not care if the things executing your business logic are called Services, Interactors, Actions, Commands or Use Cases. Just stay with one name. The difference do not matter in the end. It just matters that everyone understands where to put and search for business logic.
Another example. How to make clear that getting some data involves more than just returning it from memory? Choose a name like for example fetch
. From now on, every method that gets data from a another API or the database is called fetchX
and everything that returns data from memory is called getX
. You have something that is being calculated based on input data? call it calculateX
Are these names 100% clear for everyone that is not working on this project? No, they are not. Are these perfect names for the concepts? No, they are not but as long as you keep the naming and meaning consistent, everybody working on the projet will understand and it wil help out.
Introducing redundant variables
Often, introducing variables for returned data is technically not needed because you call or return the result directly.
func doSomething() {
defer StartTimer()()
// [...]
}
Here, we call the function StartTimer. It returns a function that does something and we call it on the exit of the function with defer
. What does the returned function do? We could guess. Maybe we know the StartTimer
function already so we know. We could go into StartTimer
and have a look or read the documentation. The Point is, you need to know or look it up.
If you introduce a name for the returned function:
func doSomething() {
stopTimer := StartTimer()
defer stopTimer()
// [...]
}
This problem goes away. Everybody understands the resulting function instantly. No chance for confusion. Yes, we added 1 extra line, but that does not hurt anyone and we introduced a lot more clarity to our code. One less thing you have to know or look up.
Using type aliases
Some languages allow introducing type aliases (for example Kotlin, Typescript, Go), giving a existing type an additional name.
We can leverage this to make our functions more clear. Often even leverage the type system to catch errors:
func getUserData(companyID string, userID string) {}
// vs
type CompanyID string
type UserID string
func getUserData(companyID CompanyID, userID UserID) {}
The benefits are:
- Impossible to "accidentally" assign something else
- Easy to find where User IDs are used (searchability through static analysis)
- Prevents positional problems in functions, it is not possible to accidentally mix up
companyID
anduserID
. - Depending on the language, they allow adding functions to the type, for example for validation or normalization.
This article is extracted from a series of talks I gave.
Top comments (0)