They say one of the most difficult tasks as a software developer is naming things but it is also one of the most important practices to have meaningful names when defining variables, functions, classes and everything in between.
Following good practices and naming conventions can make your code more readable and maintainable, and it most certainly will make it easier for other people to understand and work with it.
But how can we do that? What are the good and bad practices when naming things in software development?
I like to think of coding almost as writing a book, where I would try to guide the reader to follow a clear structure with meaningful names that helps them to stay on the right path of what they are looking for.
One suggestion that I like to follow when coding is to pause and think "What is my motivation to create this?". That makes you focus on the bigger picture other than the immediate problem-solving mindset according to the book The Pragmatic Programmer by David Thomas and Andrew Hunt (excellent read by the way).
Following up on the same reference mentioned above, there is a great example of why naming things right matters.
Let’s do a quick exercise to see this in practice:
Take a look at the image below and then follow the quick instructions, it won’t take more than a minute, I promise!
Say the words out loud as they are written.
Now say the words out loud according to their colour.
Even if you succeeded in the second step, you probably had to put more effort into it, didn’t you?
This is just a quick example to show that what is written takes precedence in our brains, even if the context around it means a different thing.
Now that we have an idea of how important it is to name things right, let’s take a look at some strategies:
👀 Naming variables
Variables are the smallest piece but usually the most significant since they are the base composition of the entire application. Naming them wrong may feel like a domino effect, where all the subsequent variables rely on the wrong meaning of previous ones.
Let’s look at some practices to avoid that:
- Avoid acronyms and abbreviations
- Be simple and descriptive, avoid generic names
- Watch out for misleading names
- Keep it short but meaningful
🔍 Avoid abbreviations:
Abbreviations may help with making your code smaller, but they make it extra complicated to understand the meaning of the variable in place. Unless you are working in a very constrained environment like embedded systems you should consider avoiding the abbreviations.
Abbreviations may seem intuitive to the person writing the code and even to people that have a clear understanding of the product but it may be a big blocker for people reading it for the first time (sometimes even second, third, …)
📜 Avoid generic names
Many programs use generic names on variables, sometimes because of patterns we learn early in our careers and keep using it like for i of items
.
Other times it is just because we are trying to keep the code somehow generic to facilitate extending it. No matter what the reasoning is, consider using clear and specific names other than super generic ones, unless you don’t have a choice.
The problem with generic names is that it doesn’t represent what the current piece of code is supposed to do because generic names usually bring also a sense of ambiguity.
Let’s take a look at a quick example:
# User-type employee
user = User("Jon", "Snow", type="employee")
...
do_something_with_user(user)
In the example above, if your codebase manages multiple types of users it may be tricky to know all possible paths every function needs to follow/check. That will force you to read the whole function to see what can happen for each type of user. It would be way better to be able to assume what a function does just by reading its signature, wouldn't it?
To avoid this problem, first, make sure that the variable name reflects the intention of what it is being used for. Next, double-check that it has a clear and unique meaning of what it represents.
Replace by:
employee = User("Jon", "Snow", type="employee")
🚽 Misleading names
This is a very scary one where it usually happens during maintenance, refactoring or change in the codebase where someone accidentally forgets to rename the variable right after changing what it represents.
This is extremely harmful because it guides the next readers directly to a misinterpretation of the code. Therefore it requires a lot of effort to understand, and maintain and it is usually the reason why weird and complicated comments and documentations exist.
Documentations are a required and really important step though, don't get me wrong! But having very verbose documentation only to explain poorly named code or misleading names is a waste of time both for the person writing it and for those who are going to read it later.
To avoid this issue, make sure that whenever you change a code you also rename the variables according to their context. Follow the first steps described here when applying code changes too, chances are you might not only avoid misleading names but also fix existing poorly named variables in place.
💊 Keep it short but meaningful
it is always good to have a very short variable name and we should aim for that as long as it represents what it means. But keep in mind that meaningful names take precedence over short names, so try to focus on the meaning first.
🔫 Naming functions
All of the suggestions about variables also apply here when naming functions. By following that, it will help you to describe the right context that the function represents.
But on top of that, other than providing a clear context we also need to explicitly show what action this function is supposed to perform.
Here are some quick suggestions:
- Functions should use
verbs
and be clear about what action it performs; - Handle only one single action;
- Use clear parameter names.
💉 Prefix suggestions
Here is a quick reference about what prefix you can use to explicitly describe the intention of your function:
Action | Prefixes | Description | Examples |
---|---|---|---|
Retrieve data |
get , find , show , list
|
Used when you need to retrieve data |
get_users , find_employees , show_user_details , list_comments
|
Insert data |
insert , add
|
Used when you intend to add new information |
add_user , insert_user
|
Update data |
update , change
|
Used when you want to update information |
update_user , change_username
|
Update or insert | set |
Used when you wish to update or create a new record if it doesn't exist | set_days_off |
Retrieve from 3rd party |
fetch , retrieve
|
Used when you wish to fetch data from a third-party resource, like over HTTP request for example |
fetch_users , retrieve_comments
|
Keep in mind that the table above is just a set of suggestions, not hard rules to be followed. I found out over the years that these are the most common prefix conventions for naming functions but, each company have their best practices and chances are you already follow some specific guideline.
📌 Functions should handle a single action
Be sure that your function does not do multiple actions.
Whenever your function contains an and
in the name or anything that reminds you that you most certainly are doing many things and you should consider splitting it up into multiple functions.
E.g.
def update_user_and_send_notification():
...
The format above just makes it too verbose and therefore less readable in most cases.
Consider splitting it up:
def update_user():
...
def notify_user_updated():
...
But what if they need to be executed in sequence?
No problem, just create a function that acts as an aggregator
def update_user_handler():
# first
update_user()
# then
notify_user_updated()
Usually the definition proposed above with
handler
is used on a layer that is exposed to outside callers on your application, like aservice
or anAPI
layer. See more below inNaming modules/packages
.
📌 Function parameters:
When dealing with functions it is very common to see very generic parameters and that can also be a big problem. whenever you have a poorly named function parameter it would result in the same problems we talked about in the naming variables section.
Let's look at the following example:
update_user(id, data: dict):
The parameter data
doesn't clearly state what is being updated in the user instance.
Is it the username? Age? Is it every single attribute?
You won't be able to know unless you read the whole function and see what the code does.
To avoid that, be explicit about what attributes the function is supposed to be handling changes.
One way to do that is to list every attribute as a separate parameter of the function:
def update_user(id: int, name: str, age: int):
If the list of attributes is too long, consider the use of a DTO
(Data Transfer Object) or a similar strategy to make the function readable but still specific.
def update_user(id: int, user_data: UpdateUserData):
...
class UpdateUserData:
name: str
age: int
Quick tip: Don't forget to explicitly define typing hints if you are using a dynamically typed language such as
Python
orTypescript
for example. That makes it much easier to understand what the function does just by reading its declaration.
✏️ Naming methods
Naming methods is very similar to naming functions, the main difference here is that the context is somewhat explicit already, since you are evoking the method from a specific class.
For example, an isolated function to ADD
a new user can be defined as the one below:
def add_user(user: User):
When defining the same functionality through a method of a UserRepository
class, for example, you can ignore the context part of the name, since it is already implicit by the class name:
class UserRepository:
def add(user: User):
...
📁 Naming classes
When naming classes you should keep in mind what is the main purpose of the class that you are creating. If you find that the class is too broad and can be used to manage multiple contexts, consider rethinking it and splitting it up. Defining too broad and long classes is one of the reasons why generic names are applied here and we should try to avoid that.
The most straightforward class definitions are the ones that represent a domain model, like a User
, Client
, Article
, etc. But we know that programs never stop there and we always stumble on broad classes every now and then.
Here are some quick suggestions:
- Keep it simple and descriptive;
- Use a noun
- Use CamelCase (or your preferred language default)
- Use whole words and avoid acronyms and abbreviations
📗 Naming abstractions/interfaces
Abstractions are a great way to make your code decoupled and testable, but naming it wrong may only complicate things.
Here are some quick suggestions to follow:
- Use CamelCase names like classes
- Clearly state that this is an abstraction/interface according to your preferred language:
- E.g.
AbstractDBSession
- E.g.
- It should represent a clear context or group of contexts:
- E.g.
AbstractUser
,AbstractClient
,AbstractRepository
- E.g.
📙 Naming enums
First of all, when defining enums
make sure that you need it. If your enum contains only a single value it should be a constant or even a simple boolean attribute.
E.g.
class UserStatus(enum.Enum):
ACTIVE = 'active'
class User:
...
status: UserStatus
# The code above could be a simple boolean attribute of the user object
# E.g.
class User:
...
is_active: bool = True
Now that you confirmed that you need an enum, you can apply the same suggestions of classes and variables definitions here.
- Use a noun
- Be clear about in what context it should be used:
- E.g.
UserStatus
,UserType
,AccountType
- E.g.
- Make it short and meaningful
📂 Naming modules
Module names should represent a group of functionalities that belong to the same general context. Your modules may vary a lot based on your own company's guidelines but some general rules can be applied to make sure that your package name makes sense.
One great suggestion I like to follow is the one found on Google's python styleguide, where you should be able to call a public code from a package using its full path, for example:
from my_app import domain
user = domain.User("Jon" , "Snow")
from my_app.services import user_handlers
users = user_handlers.get_all()
If you can follow the example above and it is easy to follow what the code does and where it comes from, then you can confirm that your package name is clear enough.
Every language, framework and even companies have their set of guidelines to name things, and the goal is to make sure that we are writing code that is going to be easy to understand by other people and our future selves.
Next time you code, try to see yourself as an author and apply these practices to guide you through the process. Your future self and every reader of your code will be thankful for that!
Thanks for making to the end, leave a like and comment if the article was helpful to you!
Top comments (6)
this is really helpful!
I relate to this so much, I have a bunch of new engineers join the team.
Getting to unlearn the crap they've been taught in their programming classes is hard.
Some useful examples here
Thank you for the feedback!
Yes, onboarding new engineers is always a challenge so trying to define a styleguide or using some existing one like Google's can be very helpful. I hope the tips here can provide some initial thoughts about that! :)
This should be mandatory.
Awesome content.
Thanks, Thiago.
Thanks Roger!
This advice is contrary to the Linux kernel coding style, which notes “C is a Spartan language, and so should your naming be. … C programmers do not use cute names like ThisVariableIsATemporaryCounter. A C programmer would call that variable tmp, which is much easier to write, and not the least more difficult to understand. … Local variable names should be short, and to the point. If you have some random integer loop counter, it should probably be called i. Calling it loop_counter is non-productive, if there is no chance of it being mis-understood.”
I understand that a lot of this post basically devolves to “this is my opinion, and these are the reasons for those opinions”. That’s fine. For what it’s worth, I agree with a lot of them! But perhaps the first piece of advice should be “find your project’s style guide, and frequently use a beautifying tool that can be configured to your project’s style guide.”
The length of the variable name has little to no impact on resource consumption in embedded systems since the variable names don’t cross the semantic gap in the compilation phase.