Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

10 Small Tips for Better Code Readability

4.67/5 (16 votes)
2 Nov 2018CPOL5 min read 19.7K  
Small tips to make your code quality better

Introduction

I can’t stop writing about the importance of code quality and code readability. I think this is one of the most important points in coding. On the other hand, I have already worked for several companies and I sadly realized that the code quality is really poor at most of the companies, even at the famous ones.

In this article, I collected 10 small points. It is not so difficult to follow them and if you are following them, your code will be much more readable. I added some examples in C++, but all these points can be easily adapted to any other object oriented programming language.

Some of you might not agree with my points. First of all, there are some mostly old-fashioned coding styles which contradict with my points. On the other hand, some of these points can be a bit against performance. Of course, there are special cases when not all of these points should be blindly followed. But I think these 10 small points can make your code quality much better. I hope you like the points.

10 Tips and Tricks for Better Code Readability

1. Use the Right Variable/Function Names

The rule in principle is pretty easy, in practise, it is not always so easy: you should call everything based on that what it is. So avoid variables like i, j, x, y, value, return_value, etc. Name your variables and functions well. I present it with an example:

C++
vector<vector<int>> getThem()
{
  vector<vector<int>> vector1;
  for (vector<int>& x : theList)
  {
    if (x[0] == 4)
    {
      vector1.add(x);
    }
  }
  return vector1;
}

Do you not understand what this function is doing now? I’m sure, you have no real idea. Now I changed the names in the function, is it more clear now?

C++
vector<vector<int>> getSelectedCells()
{
  vector<vector<int> selectedCells;
  for (vector<int>& cell : gameBoard)
  {
    if (cell[CELL_VALUES::STATUS_VALUE] == STATUS::SELECTED)
    {
      selectedCells.push_back(cell);
    }
  }
  return selectedCells;
}

What about now? Now it looks like it is collecting the selected cells of a game board. Nothing changed, just the naming. That’s why naming is pretty important.

2. Use Constants Instead of Magic Numbers

Try to avoid the usage of magic numbers in your code, it is making it unreadable. Let me give an example:

C++
int t = x * 24 * 3600 * 365;

Where are these numbers coming from, what does it mean? It’s a bit unclean.

C++
int t = x * hours_per_day * seconds_per_hour * days_per_year;

Is it easier to understand now?
And let’s use what we already learnt in the first point:

C++
int seconds = years * hours_per_day * seconds_per_hour * days_per_year;

Reorganize a bit:

C++
int seconds = years * days_per_year * hours_per_day * seconds_per_hour;

Now it’s much more clear what is it, isn’t?

3. Use Enums Instead of Magic Numbers

In the code of inexperienced programmers, quite often, I come across similar solutions:

C++
switch (status)
{
  case 1:
    //do something
    break;
  case 2:
    //do something
   break;
…
}

Ok, but what is the meaning of status = 2? Who knows. Use rather an enum for status like:

C++
Enum class Status { InProgress, NowAnswer, Error, Ok };

It will make your code much more readable.

4. Give a Name to Complicated Conditions

In most code, there are a lot of complicated conditions, like:

C++
If (line > 5 && line < 100 && feature_enabled && !error_found && value == 5)

In such cases, it is really complicated to figure out what the condition is about. It is a good idea to name the condition and/or the sub conditions. For that, you can either use lambda functions or booleans, like:

C++
bool isLineInRange = line > 5 && line < 100;
bool featureActived = feature_enabled && !error_found;
if (isLineInRange && featureActivated && value == 5)

Now it is much easier to figure out the meaning of the condition.

5. Split Up Your Function TO Smaller Ones

OK, this point is maybe trivial: if you have a function which is too long and/or is doing multiple things, just create smaller functions which are decoupling a part of your function and call them from your function.

6. Do One Only One Thing in Your FunctIons/methods

This is connecting from the previous point. So for example, if you call a function as SaveToFile, I’m expecting from the function that it is saving something to a file and not changing anything on the current data. If you still need to change something on the data, move that part of code to a new function. It’s really important that your function needs to do exactly one thing and nothing more: the thing that is in its name. So for example, in a getter function, don’t do complicated calculations. In that case, just create a separate calculate and save function.

7. Make a Clear Difference Between Functions and Methods

It is a personal opinion, but I’m following this point at coding and I think it makes the code readability really better.

In my understanding, a function is a subprogram with a return value. And in my view, a function should not change the state of the object. So it should not change any object level private/public variable. Just read them, calculate the return value based on them and return. So for example, in C++, they can be marked at const.

The methods are subprograms without return value. So in C++, technically, they are the void functions. The main goal of methods is to change the state of the object. Every change in the status of the object needs to be done by using methods.

8. Organize Your Parameters/variablEs Into Structs

There are several cases when a function has several (3+) parameters. Some functions have a really long parameter list, over 10-15 parameters. When you are calling such functions, it is pretty difficult to figure out which added value belongs to which parameter. That’s why I think it is better to organize your parameters in one or more structs, like:

Instead of:

C++
drawRectangle(double x, double y, double width, 
  double height, int color_r, int color_b, int color_g)

use:

C++
struct Position
{
  Position(double x, double y) :
    x(x),
    y(y)
  {};

  double x;
  double y;
}

struct Size
{
  Size(double width, double height) :
    width(width),
    height(height)
  {};

  double with;
  double height;
}

struct Color
{
  Color(int red, int green, int blue) :
    red(red),
    green(green),
    blue(blue)
    {};
  int red;
  int green;
  int blue;
}

drawRectangle(Position position, Size size, Color color);

So that when you are calling it, instead of:

C++
drawRectangle(100.5, 54.3, 34.5, 23.3, 222, 144, 68);

You can use:

C++
drawRectangle(Position(100.5, 54.3), Size(34.5, 23.3), Color(222, 144, 68));

I think in this way, the meaning of each parameter is much more clear.

9. Convert Functions To Classes if Needed

Especially in old code, there are quite often functions with multiple input and output parameters. In most cases, I would avoid the usage of output parameters. But especially, if there are multiple ones, it is really better if you are creating a class, having all the parameters as private variables. Create setter for inputs and getter for outputs.

10. Cover Your Code With Unit Tests

Unit tests are really useful to verify your program and to avoid regressions. But it is even really good to have unit tests if you would like to understand a code. Simple, you can see for each function and class what is their expected behaviour.

These were my points for this article. I hope you like them, feel free to extend them if you have any other good ideas.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)