Writing readable code

There are usually several ways to solve a particular programming problem, and the code you write may be very different from the code that someone else might write when faced with the same problem.

While any code that solves the problem at hand might be said to be “correct”, style is also important. Here we focus on one of the most important aspects of coding style; that of writing code that is readable.

Pieces of code are rarely used once and then thrown away. Even code that is written to solve a small problem right now can find itself being used months or years later, either by the original author or by other users. By making your code readable you make it easier for someone reading the code to understand and follow what it does, whether this is someone else, or you in six months time when you need to reanalyse that one last piece of data.

Making your code readable also makes it easier to spot mistakes and bugs. Writing more readable code helps you to focus more on how pieces of your code work together, and focus less on exactly what line 36 is doing. Thinking about making your code readable also helps you to identify problems in your logic or code structure before you commit these to code, possibly creating bugs that do not reveal themselves until much later (and then are hard to track down and fix).

Readability is a question of style and is therefore something of a personal preference. As you write more code, you will develop your own taste for whether code is “readable” or not. A good first test for whether code is readable is to review it after a short break from writing: can you follow what it does, or do you need to start making notes to keep track of the details? An even better test is to ask someone else to review your code (called “code review”) and to comment on whether they follow what it is doing.

What makes one piece of code more or less readable than another is somewhat a matter of personal taste. But there are some basic principles to consider that can help your code to be more readable:

Principles of readable code

Thoughtful naming

Your choices of function and variable names should help explain what they are doing. Naming a function my_func() is not particularly helpful when later reading the code. Calling a function potential_energy() gives the reader a better idea what this function is used for. Similarly, calling a variable i is less helpful than calling it index.

Comments explain the “why”

Comments in Python are denoted using the # character. The Python interpreter will ignore all the text the appears after this.

# This is a comment, with nothing else on this line.
a = 1
b = 2
print(a+b) # This comment is on the same line as executable code.

In an ideal world your code would be written so that someone reading it can understand what the code is doing without comments.

Comments fill in the gaps, and should be used to explain why you are doing something, rather than how.

Here is a helpful comment:

# calculate the distance between atoms i and j
dr_ij = sqrt((ri[0] - rj[0])**2 + (ri[1] - rj[1])**2 + (ri[2] - rj[2])**2)

and here is a less helpful comment:

# From Pythagoras's theorem, the square root of the distance between points i and j is equal to the 
# sum of squares of the differences in position along x, y, and z.
dr_ij = sqrt((ri[0] - rj[0])**2 + (ri[1] - rj[1])**2 + (ri[2] - rj[2])**2)

Function docstrings are a particularly useful type of comment. They describe what functions do, what required and optional arguments inputs they require (or which arguments are optional) and what they return. Docstrings should always be included.

Write modular code

Books are divided into chapters, sections, paragraphs, and sentences to help the reader to mentally organise similar ideas together and to mark transitions. A book without paragraph breaks or chapters would be much more difficult to read.

Similarly, well written code is divided into modular components: modules, classes, functions. Breaking your code up into smaller building blocks, instead of always keeping everything in one unstructured file makes it much easier to read and to edit. Writing modular code also makes it easier to build more complex programs comprising 100s or 1000s of lines of code.

Each component should have one clearly defined role in the overall program. This allows you to write small clear blocks of code, each with a well-defined purpose, and then link these together to build your complete program. The structure of how you write your code should follow the structure of the problem you are trying to solve.

Making code modular also makes it easier to test your code to be more confident that it is doing what you want it to do.

The following code is only a few lines long, but is not particularly readable. (Can you figure out what this code is supposed to do?)

# Some ugly, hard to read code

s = ['H', 'Ca', 'Fe', 'Hg', 'Br', 'Xe']
m = [13.99, 1115, 1811, 234.321, 265.8, 161.4]
b = [20.271, 1757, 3134, 629.88, 332, 165.051]
for d in range(0, len(s)):
    if m[d] < 273.15 and b[d] >= 273.15:
        print(s[d])
        print("This element is a liquid at standard temperature and pressure.")
Hg
This element is a liquid at standard temperature and pressure.
Br
This element is a liquid at standard temperature and pressure.

We can check this code against our three principles above and see where it could be improved:

  • The three lists are simply given the names s, m, and b, which represent chemical symbols, melting points, and boiling points.

  • There are no comments or other documentation to explain the purpose of the code. What is the overall aim? What is the if statement for?

  • The code is not modular. In this case splitting up code that is only 7 lines long into functions is probably not necessary, although it might be helpful if there are a few key lines that would benefit from being separated into their own well-defined function.

Exercise

Rewrite the code above to be more readable and clear, remembering to have descriptive variable names and include relevant and informative comments.