isaak.dev

Thoughts about naming variables and methods

06 Jul 2020 8 mins read start a discussion edit this page

The proper naming of variables, functions, methods, and classes is one of the most important attributes of elegant and clean code, that clearly reflects the intentions of the programmer, without assumptions about what was meant.

In this article, we will talk about code that is the exact opposite of the one described above - about code that was written carelessly or thoughtlessly. This article is a small confession, because I, like any other programmer, have also written such code in the past (in fact, I still write the bad code sometimes, though refactoring makes it much better). This is nothing terrible as long as we understand that we need to work on it.

This article is a translated version of my article from harb.com so, if you know Russian then you can read the origin version.

Let’s start.

Variables

One of the most annoying kinds of variables is variables that give a false impression of the nature of the data they store.

The requests library is extremely popular among Python developers, and if you have ever been looking for something related to requests, you must have come across with something like this:

import requests

req = requests.get('https://api.example.org/endpoint')
req.json()

Whenever I see this, I feel annoyed and even not because of the shortened name, but because the name of the variable does not match what is stored in this variable.

When you make a request (requests.Request), you get a response (requests.Response), so reflect this in your code:

response = requests.get('https://api.example.org/endpoint')
response.json()

Not r, not res, not resp and certainly not req, exactly response. res, r, resp – these are all variables whose contents can be understood only by looking at their definitions, and why jump to the definitions when you can initially give a suitable name?

Let’s look at another example, but now from Django:

users_list = User.objects.filter(age__gte=22)

When you see users_list somewhere in the code, you quite rightly expect that you can do this:

users_list.append(User.objects.get(pk=3))

but no, you can’t do this, since .filter() returns a QuerySet:

Traceback (most recent call last):
# ...
# ...
AttributeError: 'QuerySet' object has no attribute 'append'

If it is very important for you to specify a suffix, then specify at least one that reflects the real situation:

users_queryset = User.objects.all()
users_queryset.order_by('-age')

also okay, because such abbreviations (_qs) are usual for Django:

users_qs = User.objects.all()

If you really want to write exactly _list, then take care that the list really gets into the variable:

users_list = list(User.objects.all())

Indicating a type of data that a variable contains is often a bad idea, especially when you deal with dynamic languages, such as Python. In cases when it is very necessary to note that the object is a container data type, it is enough to simply indicate the name of the variable in the plural:

users = User.objects.all()

Consider another example:

info_dict = {'name': 'Isaak', 'age': 25}
# ...
# ... 
info_dict = list(info_dict)
# ...
# ...

You see a dict and you might want to do this:

for key, value in info_dict.items():
    print(key, value)

Instead, you’ll get an exception, because you were misled, and you will understand this only if you go to the definition of the variable and read the entire code from top to bottom, right down to the section from which you started the jump — this is the price of such variables.

Thus, when you indicate in the variable name the type of data stored in it, you are essentially a guarantee that this variable must contain the specified data type at any time during the execution of the program. Why should you take this responsibility if it is the direct responsibility of the interpreter or compiler? You shouldn’t! Better to spend time thinking of a good variable name than trying to figure out why the variables do not behave as you expect.

In the example above, the choice of the name of a variable is rather bad, and you could give a name that more accurately expresses the context (no need to be afraid to use names related to the subject area), but even in this case, you could make this code better:

info_dict = {'name': 'Isaak', 'age': 25}
# ...
# ... 
info_keys = list(info_dict)
# ...
# ...

or even like this, which is more idiomatic:

info_dict = {'name': 'Isaak', 'age': 25}
# ...
# ... 
info_keys = info_dict.keys()
# ...
# ...

Another type of annoying variable is ones with a shortened name.

Let’s go back to requests and consider this code:

s = requests.Session()
# ...
# ... 
s.close()

This is an example of an unnecessary shortening for a variable name. This is a terrible practice, and its horror becomes even more apparent when such code takes up more than 10-15 lines of code.

It is much better to write as is, namely:

session = requests.Session()
# ...
# ...
session.get('https://api.example.org/endpoint')
# ...
# ...
session.close()

or

with requests.Session() as session:
    session.get('https://api.example.org/endpoint')

You may argue that this is a more verbose option, but I will answer you that it pays off when you read the code and immediately understand that session is a Session.

Will you understand it by variable s without looking at its definition?

Methods

Smart naming of functions and methods is something that comes only with experience in designing an API, and therefore you can often find cases where methods do not behave as you expect.

Consider an example:

>>> person = Person()
>>> person.has_publications()
['Post 1', 'Post 2', 'Post 3']

We expressed a very clear-cut question in our code: “Does this person have publications?”, but what kind of answer did we get? Did we ask for a list of publications of a person?

The name of this method implies that the return value must be of Boolean type, namely True or False:

>>> person = Person()
>>> person.has_publications()
True

We can use a more appropriate method name for getting publications:

>>> person.get_publications()
['Post 1', 'Post 2', 'Post 3']

or

>>> person.publications()
['Post 1', 'Post 2', 'Post 3']

We often like to call programming a creative activity, and it really is. However, if you write not readable code, and then justify it with “creativity”, then I have bad news for you.

Further reading

I’m leaving this list of outstanding relevant literature written by well-known professionals in the field for further study of the issue:

  1. Robert Martin — Clean Code
  2. Robert Martin — Clean Architecture
  3. Robert Martin — The Clean Coder: A Code of Conduct for Professional Programmers
  4. Martin Fowler — Refactoring: Improving the Design of Existing Code

Subscribe to my blog if you want more