Sure, well-named variables and functions are important in all programming languages. But when is a name good enough? And when is it especially critical? I just received an interesting comment about my new Clean Code course on Pluralsight. The commenter felt that “Get” is a fine method name and used the example of person.Get(personId)
. This example reads clearly enough, but there’s three reasons to question such a short function name, particularly when working in a dynamic language.
1. Statically Typed Languages Help Convey Intent
First, method names matter more in dynamic languages than typed languages. Why? In statically typed languages the parameter types help convey intent. Consider JavaScript vs C# for this example:
JavaScript
C#
Note the two additional pieces of fidelity that you pick up in C#:
- Parameter type – You can see a personId of type int is the argument
- Return type – It’s clear the method returns a person. There’s no need to read the body of the method to know this in C#.
These two pieces of information help clarify the intent of the function. Thus, you can certainly get away with more terse method names in statically typed languages.
2. What about the future?
There’s a second issue with a short method name like “Get”. What happens later when there are multiple ways to get a person by the same datatype? Imagine we create a new function that gets a user by phone number or Social Security Number. (insert gasps here). At this point the method name “Get” becomes risky business. People may pass it a phone number, not realizing it expects a personId. You’d thus be likely to rename “Get” to “GetByPersonId” at that time to resolve the ambiguity caused by the short function name. And this leads to the third issue.
3. The Risky Rename
One could certainly argue that’s GetByPersonId is what the method should’ve been named in the first place – especially in a dynamic language. And this leads to the final reason naming matters more in dynamic languages: When you decide to rename variables and functions later, statically typed languages allow you to “lean on the compiler” for safety. This term comes from Michael Feathers’ book “Working Effectively with Legacy Code“.
Leaning on the compiler involves two steps:
- Altering a declaration to cause compile errors
- Navigating to those errors and making changes
You can lean on the compiler to make structural changes to your program.
-Michael Feathers
You have no such crutch in a dynamic language and thus introduce greater risk anytime you rename. This is also why a robust testing suite is particularly critical in dynamic languages – it helps fill in for simple bad reference issues that a compiler would’ve caught in a statically typed language.
The bottom line is we live in a world of intellisense, high resolutions, and dirt cheap storage. So when torn on which name to choose, clean coders should err on the side of clarity rather than brevity. And in a dynamic language, this principle is all the more important.
There’s much more to be said on the topic of variable and function naming. To learn more, check out “Clean Code: Writing Code for Humans” at the link below and chime in on Hacker News.
I don’t think GetByPersonId() goes far enough. I suggest PersonFromPersonID() which puts the return type and the parameter in the function name.
Great point James. I can certainly see the logic of mentioning the return type in a dynamic language. Though if you include the return type, I wouldn’t drop the verb. So I’d go even one step farther: GetPersonByPersonId. Typically, functions should start with a verb. The importance of starting with a verb becomes especially clear when you add other functionality like DeletePersonByPersonId, etc.
/**
* @Namespace – Person
* @Function get – gets a person by their id
* @Param {int} – personId
*@Return {Person} – The functions returns a person object
*/
this.gets you a documented api. I don’t disagree but in early development while things change name/functionality and scope a bunch I think intelligent declarative variable names can take longer than just documenting it
Sure Adam, this would certainly help. But I’d still argue a well named function is critical. Agreed? The risk here is this comment style becoming a crutch for poor naming
For example, this:
var oneDozen = 12;
is preferable to:
var x = 12; //one dozen
While your solution would certainly clearly document the API, I feel comments should only be added when the code can’t be sufficiently expressive on its own. I prefer James suggested approach for this reason. Code is much more likely to be kept updated than comments and it doesn’t burn another 6 lines on the screen for every function. That said, I agree your approach can be useful as long as the author doesn’t stop worrying about naming things well due to over-reliance on comments.
`var oneDozen = 12;` is not preferable to `var x = 12;`. In fact, they are both bad. `x` is bad because it says nothing about the kind of data it contains (unless it’s the x point value, x axis, etc), and `oneDozen` is bad because it’s simply restating the exact data it contains.
Note that `ONE_DOZEN` wouldn’t necessarily be a bad constant name (because it never changes), but it’s not a good variable name because the nature of variables are that they can and will change. My rule is, if changing the value means you have to change the name for it to make sense, it’s a bad name.
A much better name, without knowing what those variables represent, would be something like `personCount` (using the content of the post).
My example was intended to be interpreted as JavaScript. I agree if in C# declaring a constant would be preferable.
Hi Cory, thank you for blogging in relation to my question! Interesting answers as well. I would like to come back to the second point you are making. The implication I am getting from your point is that method overloading is a bad idea. If the intention is to return a person, you could overload the parameter in the Get method so it can take different data types. However as I write this I am thinking that that can be a problem if you want to send a parameter as a string which might be a telephone number or an email address. So then method overloading probably has limited scope in clean coding?
Oh and a second point I get from your answers; it is good to have the same standards for dynamic and strongly typed languages. Seems like a good idea, but important to bear in mind in this example that what looks good in one may not look good in the other.
Method overloading in dynamic languages is troublesome because one must write ad-hoc logic to sniff the parameter datatype. This gets particularly tricky when the same datatype is being passed, as in the example of passing either a phone number or a personId to a function simply called “Get”. Since they’re both ints, how would one determine the intent? And even if one were a string and the other an int, it would require code within the function body to sniff the parameter. I consider this a strong smell. I’d much prefer two separate well-named functions that handle a specific input. To avoid repeating yourself you could always have these two separate delegate to another function.
Overloaded methods are less an issue in strongly typed languages since the intent is clearer with explicit parameter types and completely separate function bodies can be defined to handle each type.
I agree that strongly typed languages help communicate intent to the programmer by including the return type in the method definition, but I would argue that this is merely a byproduct of satisfying the compiler and should be completely unnecessary where the programmer is concerned.
As a Rubyist, I would have not give the method Person.get() (or find()) another thought. I’d assume it takes an integer ID as the argument and returns a person instance per the language’s conventions. If not, you’re doing it wrong™. This is one of the reasons I like Ruby. Expecting Person.get() to take a phone number or ss#, per your example, would be generally be a bad assumption.
It would be nice to have a compiler to catch gaps in my testing suite, but why are there gaps in the first place? Renaming a method and finding an all-green test suite would probably be another you’re doing it wrong™ situation with a dynamic language.
I also agree that programmers should aim for clarity over brevity. However, one of the nice things about using an opinionated language (or at least a language with a dominating opinionated framework) is that you can achieve clarity without sacrificing brevity.
Great article ! Never forget duck typing: you must care more about the semantics of the object than what their type will be at execution, so make it explicit in your function names.
Just a little remark though: You use “strongly typed” as the opposite of “dynamically typed”. Actually, Python is strongly typed ! (meaning you don’t have arbitrary type conversions like in JS, when you do “1” + 1 for instance).
The opposite of “dynamically typed” is “statically typed”.
Great point Ahmed. I’ve updated the post accordingly. Thanks!