There's an old programming joke:
Computer science has two hard things: cache invalidation, naming, and off-by-one errors. đ
There is truth in this. Namingâor, more precisely, choosing great names for variables, functions and modulesâis one of the more challenging aspects of programming.
Choosing names that don't fit well with the code is oh-so easy. Such misnamings will prove problematic, making future development efforts harder.
And bad naming is rife in many codebases.
Still, naming things is not as tricky as the joke has us believe.
Today, I'll show you how easy naming can be.
Let's get into it.
Note: This guide focuses on the language-independent aspects of naming things.
General Guidelines
Take Your Time
Rome wasn't built in a day. Coming up with great names takes time. I recommend taking a minute to formulate a name. By all means, take 5-10 minutes, or take a break. It's better to spend a bit longer on crafting a great name than choosing a misleading or ill-fitting one that will confuse developers for years.
Be Prepared to Change It
If you think of a better name, change it. Renaming is easy to do with refactoring tools such as Resharper.
Two caveats: Be careful with dynamically typed languages like JavaScript. Introduced bugs will manifest at runtime unless you have comprehensive unit tests.Where the rename risks breaking clients, you'll want to avoid it. E.g. API response models.
Choose Meaningful Names
This makes sense, right? We name things for what they represent in the program. The more exacting and precise the name, the more likely readers will understand our code. If there is one overarching, crucial rule to naming, this is it.
Avoid Hungarian Notation
Hungarian notation refers to prefixing type information into variable names.
intCustomerNumber
might be fine while you're modelling the customer number as an integer type.
However, changing it to string type causes the variable prefix to become misinformation. Avoid.
Note: Sometimes, convention requires Hungarian notation. C# interfaces are defined with a prefixed I, as in IOrdersRepository
.
Let's start with the most straightforward programming constructsâconstants.
Constants
Opinions vary widely on the formatting of constants, from all uppercase
const int SECONDS = 60;
to prefixed k
const int kSeconds = 60;
which has a maths origin (from the German 'Konstant'),
to no special notation
const int Seconds = 60;
I don't have strong guidance for you on this matter.
The important part is to make the constant meaningful and unambiguous. In that way
const int NumberOfSecondsInMinute = 60;
explains its purpose better than any of the previous options.
Variables
This section refers to
- parameters,
- local variables,
- class members or fields
Booleans
Which is a clearer name for a boolean: active
or isActive
?
Correct! The latter isActive
expresses the true/false nature much clearer than merely active
.
Other great names for booleans are:
HasOutstandingOrders
doesRequireCleaning
orrequiresCleaning
My guiding rule is to try the boolean variable name (or expression) out in an if
statement and read it out loud:
if (student.exams)
is OK as its meaning can be inferred,
but if (student.hasExams)
is crystal clear.
Prefer clarity.
Variable Name Length
Short variable names are acceptable for short and simple functions.
Check out this example:
The local variable tz
clearly refers to the timezone. The method is called ToTimezone()
âwhat else would tz
stand for?
So, for tiny functions, variable names can be terse, possibly even a single character, as long as the meaning is clear.
However, when functions are longer, we also need longer variable names.
Why? In a long function, the number of moving parts can only be discerned with meaningful, context-rich names. Here's an example:
Luckily, the function has long, explanatory variable names, or keeping track of variables and behaviour would be even more challenging.
Functions & Methods
Functions are expressions of behaviourâthey do something.
What a function does should be clearly expressed by its name.
What does that mean?
What about a function called BankAccount
?
Does a name like that make sense?
No, it does not. That name fails to clearly define the purpose of this function. What does it do with the bank account?
Therefore,
Functions should start with a verb.
Examples:
Sum()
Open()
AddClassTopic()
ConvertToCustomer()
TryImportTransactions()
The second to last example could even be reduced to ToCustomer()
without loss of understanding.
Public Methods
Public methods should have short names. Readers browsing through a listing of public methods ought to be able to pinpoint the sought-after behaviour from a concise method name.
Furthermore, for public methods, the best practice is to prefer overloads rather than different method names for various combinations of parameters.
For example, the .NET System.IO.File
has these 3 overloads of Open()
:
The alternative might have been to specify three distinct method names, each outlining how to open a file:
OpenWithMode()
OpenWithModeAndAccess()
OpenWithModeAndAccessAndShare()
Yeah, it's pretty nasty. That's why we use overloads instead.
Pick the method and then the overload that suits you.
Private Methods
On the other hand, private methods can, and often should, have longer names.
Encapsulating code that carries out a given behaviour in a descriptively named private helper function is more maintainable than having comments. Comments rot and decay. They often get out of sync with the code they describe, and so become disinformation.
Here is an example of a commented behaviour which we can extract into a meaningfully named helper:
After extraction:
Isn't that much nicer?
This example also illustrates that we can frequently shorten words in the names of variables, functions and classes without loss of context. Calculate
can be reduced to Calc
, and the reader will still understand the meaning.
Properties > Methods
Consider whether a public field or property would be more concise when you have a public method that requires no parameters.
For example, on an Invoice
class, we could have a method to calculate the total amount as
CalcTotalAmount()
invoked asvar total = cart.CalcTotalAmount();
Alternatively, we could have a read-only property/field called
TotalAmount
, or even justTotal
called asvar total = cart.Total;
Beautiful, right?
Whether the total amount is calculated or retrieved is an internal matter that clients of the Invoice
class should be ignorant of.
Classes & Modules
For arriving at outstanding class (and module) names, please see my recent article: Ask 4 Questions for Outstanding Class Names
Other Thoughts
Be Consistent
One way to make our systems easy to maintain is by always using the correct terms. If the business has customers, the code should refer to 'customers' and not 'clients' or 'purchasers' unless those terms have a separate and different meaning.
Another way I like to provide consistency is by supplying my classes with a consistent interface.
Are these methods on a customer repository consistent?
GetCustomer(emailAddress);
RetrieveAllCustomers();
ReadCustomerBy(customerNumber);
No, they are a hodge podge of names.
Let's do a rename refactoring:
GetCustomer(emailAddress);
GetCustomer(customerNumber);
GetAllCustomers();
So much more consistent and predictable!
Conclusion
- Take your time - Good names take time. Coming up with a great name after a few minutes is better than instantly deciding on a confusing or dubious one.
- Change it - If you've thought of a better name, change it yet proceed with caution and a unit testing safety net.
- Meaningful Names - Names mean something. A name should closely match the thing or action it is modelling.