Introduction
In this tutorial, we will continue introducing coding guides. Previously, we saw guidelines on how to create high-quality classes. Now we will see how to create high-quality routines. I will not go into details on how to use loops, variables and conditions. This I will explain in the upcoming blog posts. Here, we will see guides on how to name our high-quality routines, how to pass parameters and so on. Here, I am using the word “routine” to describe what you might know as a function or a method. This should be a shorter post, compared to the previous one.
Just to be clear, these guidelines are not something I have figured out on my own. I have used different sources like this excellent C++ FAQ or this great book from Steve McConnell – Code Complete: A Practical Handbook of Software Construction, Second Edition, which I strongly recommend you to read. You can buy it from Amazon.
Reasons to create routines
There are many reasons why you should create and use routines in your code. Here, I will mention some of the most important ones.
Avoid duplicate code
Probably the most important reason to create routines. We all have written code pieces, which we need to use on multiple places in our program. We can go ahead and repeat the same code on each place, thus making our program code bigger and harder to maintain. The maintenance cost comes from the fact that if there is a bug in the code piece we have put in several places, we will have to find all these places and update the code in each one of them. When somebody is looking at our program code, he/she will be seeing the same code here and there. And we want to avoid that
Reduce complexity
Using routines, we can wrap some complex code and leave that code aside. Thus, we need to worry about it only when we do updates to it. In our code, we will see just one routine call. If one of our routines is very long and hard to understand, we can take some related code pieces, put them in separate routines, and then just call those routines. This way our routine size will shrink, which will make it easier to maintain. Such code pieces could be some nested loops. Instead of having several for loops nested into each other, we can replace them with routine calls. Thus, we will reduce the complexity of our nested parts.
Improve portability
Every one of us has had to write a code, which will be ported to different hardware and/or different operating systems. You know that the code, which uses the standards, is easily ported. But if you have a code piece, which uses OS specific function calls, you can’t easily port it. You need to put extra effort. If your program has many places, where you are using specific code, you will be in trouble porting and maintaining those places. Fortunately, you can easily wrap those code pieces into routines and then just have some simple logic, to call the respective routine, depending for what hardware or OS we are building.
Understandable abstraction
Code piece that does a particular job can be placed into a single routine. Then we can name the routine so it will be self-explaining what it does. Our routine itself will become self-documented if we use such routines instead of raw code. Let us look in the following example:
void useComplexCode() { constexpr int c_participantsCout = 5; std::vector<Person> l_people{}; for (int i = 0; i < c_participantsCout; ++i) { Person l_person; std::cout << "Enter person's name: "; std::cin >> l_person.name; std::cout << "Enter person's surname: "; std::cin >> l_person.surname; std::cout << "Enter person's address: "; std::cin >> l_person.address; std::cout << "Enter person's age: "; std::cin >> l_person.age; l_people.push_back(l_person); } // ... // Do more stuffz // ... }
If you look at the code piece inside the for loop you will understand that this code does some particular job. In our case, it reads data from the user and stores this data inside a vector. Then the routine does some other operations.
We can improve this routine by taking out the code for reading data into a separate routine and call that routine instead. See how I changed it
void getPeople(std::vector<Person>& a_people, int a_peopleCount) { for (int i = 0; i < a_peopleCount; ++i) { Person l_person; std::cout << "Enter person's name: "; std::cin >> l_person.name; std::cout << "Enter person's surname: "; std::cin >> l_person.surname; std::cout << "Enter person's address: "; std::cin >> l_person.address; std::cout << "Enter person's age: "; std::cin >> l_person.age; a_people.push_back(l_person); } } void useSimpleCode() { constexpr int c_participantsCout = 5; std::vector<Person> l_people{}; getPeople(l_people, c_participantsCout); // ... // Do more stuffz // ... }
Now I have one routine, which reads the data and stores it. I have named it getPeople(), which states that it retrieves information about people (later we will see how to name our routines). And in my main routine I just call that newly created routine. Do you see how it documents itself that my main routine will get some people data and then it will do some operations?
Of course, this is a simple example. In the real world, we will have more complex algorithms, which we will have to add into separate routines.
Hide execution sequence
Often, there is some execution sequence order, which our program depends on. It is better to wrap this sequence into a routine and then work with that routine. For example, we can have a routine, which works with data from a file and needs to execute the following sequence: Open a file; read the data from the file; close the file. We can wrap this sequence into one routine and call that routine. We may reuse that sequence or if we want to make some changes, we can easily find it and update it.
Wrap pointer operations
If you have any pointer operations, it is a good idea to wrap them inside some routine, thus isolating them from your main code. Pointer operations are error prone, so if you need to change them, you can easily find them and do the update. Later, you can change the pointers with another less error-prone type.
Improve performance
When a code is placed in one place inside routines, you can more easily profile those routines, find the bottlenecks and improve the performance of your code. There will be no need to look at several places for the performance bottleneck’s source.
Simplify Boolean tests
I am sure you have seen some conditions with big and complex checking. You can get lost in them. Let us look at one such case:
void useBooleanTest() { int a; int b; std::string text; // ... // Some code piece // ... if (((a >= 0 && a <= 100) && (b <= 120)) || ((a + b >= 190) && (a * b < 5000)) || ((a >= 50) && (text == "TXT")) /*|| Many more checks */) { // Do something } }
You can see that the test in the if clause is quite complex. In addition, it can get even more complex if you need to add extra checking. That is why we better move it into a separate routine and call that routine instead.
bool checkConditions() { int a; int b; std::string text; bool result; // Some simplification algorithms returning result return result; } void useBooleanFunc() { // ... // Some code piece // ... if (checkConditions()) { // Do something } }
Now you can see how we have taken out the complex boolean tests from our main routine and we just call the newly created routine. The code looks simple and easy to understand, thus, easy to maintain. In addition, if we need to add a new check in the Boolean test, we can go ahead and add it in the new routine.
One line functions
Some people will argue that making a routine only for one line of code is not a good idea. Well, I will argue with them. Let us look it this way – if that one line looks complex and it is not self-explanatory, we can move it to a routine with a better name and this will make it more easily to understand that one line. Check this for example
Int result = TOTAL_CAPACITY - (totalNumberOfCars + (totalNumberOfPeople / 4));
This one line does some calculations, but what calculations. Unless, there is a comment above, it would be hard to understand it. It would be much better if we move it to some routine with a meaningful name like getRemainingCapacity(). Then our code will look like this.
int result = getRemainingCapacity();
Much easy to understand, isn’t it? I think yes. Now you know that in your main routine you are getting the remaining capacity of something. So yes, sometimes it is worth putting one line of code into a routine.
Naming the routines
Here I will present you several good practices in naming the routines. Naming a routine is an important task, because we will access the routine by its name. It should have some meaningful and easy to understand name. Here is what I consider as best practices.
Describe everything the routine does
Most of the time, the users will use our routine directly, without looking at the routine code. They will read the routine name and will try to guess with it does. The users might not read the documentation even if it is part of the header file. The routine name is the first thing every developer sees. That is why it is important to describe all the actions, which the routine takes.
For example, if our routine verifies the CAN data validity and then sends a CAN message, we better name it verifyAndSendCanMessage() instead of just sendCanMessage(). This way, the user will know that our routine does a verification in addition to the CAN message sending, without even looking at the documentation or at the implementation (if he/she has an access to it).
Keep the routine names as long as needed
The routine name’s length is also important. Per some researches, 9 to 15 character is the optimal length for a routine name. Do not make the name too long and try not to add unnecessary information in it. This will harm the code readability. Also, do not make the name too short and most importantly – do not try to shorten the name if it is not necessary. Here is example of a good routine name – receiveCanMessage(). You can easily understand what the routine does. And here is an example for a bad routine names – rcv() & receiveCanMessageOnCanPort1ByCheckingThePortPeriodically(). The first name is very short and you cannot figure out what does it mean. Is it short for receive or short for something else? The second name it too long and contains unnecessary implementation details in it.
One more thing – if you are writing a routine that is part of class, do not add that class object to the name. Assume we have a class named CanMessage. Instead of naming a function that receives a message – receiveCanMessage(), just name it receive(). It is part of a CAN message object so it is obvious that it will receive a CAN Message.
Don’t use meaningless verbs
There are verbs, which are too general and can mean many things. And we, programmer tend to use them. Such verbs are handle and perform. When we use them to name routines, we can harm our code readability and the maintenance. For example, if we name a routine, which transfer and receive CAN message – handleCanMessages() – it will be too general and not clear how it handles them. Does it store them, does it send them or does it destroy them – no one knows. Better rename the routine to transcieveCanMessages(). This way, the user will know that our routine is sending and receiving CAN messages. Well, a routine, which sends and receives messages at the same time, might not be a perfect example for a good routine, but at least we have named it correctly.
Use a description of the returned value
If our routine returns some value or some object, we can use its name in the routine’s name. For example if our function returns the availability status of the object, we should name it isAvailable(). Or in case our routine calculates and returns some shape’s area, we can name it getArea().
Use a verb, followed by an object
When we are writing routines, which are not part of some object, it is a good practice to name them using a verb, followed by an object. For example, a routine, which prints documents should be named printDocument() instead of just print(). With the first version, we will know exactly what we are printing. The second version is more suited for routines, which are part of a class.
Do not use numbers or characters to differentiate routines
If several routines execute different steps of one algorithm, we might be tempted to name them something like applyStep1(), applyStep2(), applyStep3() and so one. This is very poor naming. These names do not state exactly what each routine does. They are not self-documenting. A better naming would be getUserInput(), calculateArea() & printResults(). Now these names state exactly what each routine does and the user will not be forced to read the documentation or worse – look at their code.
Follow the team/company conventions
If any naming conventions are available in the team or in the company, you are working for, better follow them. Such conventions could be that each function name shall start with a capital letter or each word in the name shall be separated by an underscore and so on. By following them your code will be consistent with the other developers’ code, which is important for every project. Assume that there are three routines, written by three different developers. It would be a mess if one routine starts with a capital letter, the next one starts with a lower letter and the third one starts with an underscore.
Routines length
Let us say few words about the routine length. Each one of us has written or read a routine, which is too long and you have to scroll for a while until you get to the end. On the other hand, you are at the end and you find some variable, which is set somewhere in the beginning. You have to scroll back and find how it is set. Therefore, you are wasting time scrolling back and forth over the whole routine.
A better approach would be to have multiple shorter routines. Per some studies, the best length for a routine is to fit into one screen. Some people say that a routine should be even shorter but I think that fitting to one screen is short enough. When the routine fits into one screen, there is no need to scroll forth and back. You have all the code at your disposal. This reduces slightly the chance to use something wrong.
Some studies says that the perfect routine length is 50 to 150 lines of code. That also can work. The important thing is to keep the routine as short as possible. The shorter the routine is the easier to maintain it would be. When you have the whole routine available at your display, it would be easier to find any issues and fix them. It would be highly unlikely to make any serious issue when you have all the code at a glance.
The routines parameters
The routines provide us with the ability to pass data to them in the form of parameters/arguments. There are some good practices for the routine parameters, which I would like to mention here.
Put the parameters in input-modify-output order instead of ordering them randomly
Most of the times we order the parameters in no particular order – randomly. Sometimes we might add them in alphabetical order. The good practices say that we should put them in input-modify-output order. Usually this is the order we will be using them. Firstly, we will get the input arguments and use them in our algorithms. Then, using the results from our algorithms, we will modify the second group of parameters. Finally, we will provide the outputs to the user. This is some kind of a natural order.
Put the status and error parameters last
This one is an exception to the above guideline. If we have status or error arguments, it is a good practice to put them at the end of the arguments’ list. They have some different meaning than the input, modify and output arguments. That is why it is a good idea to keep them separate and at the end of the list.
Do not add unused parameters
If there are any parameters, which the routine is currently not using, better not add them in the list of parameters. Sometimes it is confusing to see some parameter and when you try to look for it, you cannot find it anywhere. You will waste some time looking for it.
In addition, the compilers will give a warning if you have unused parameters. If the compiler is giving a warning, this would mean that having unused parameters is considered a problem.
If your consideration is to use them later, better add them later, rather than keeping them in your code.
Limit the number of used parameters
Having many routine parameters is confusing and error-prone. First of all, it makes reading the routine hard and you need to remember all of the parameters. The chance to forget a parameter or to add it in a wrong place when calling the routine is higher when having a lot of parameters.
Some authors suggest that the perfect maximum number of parameters is seven. Having more than that is not considered a good practice. If you still need to provide more parameters, the suggestion is to wrap them in a class or structure and provide them that way.
Do not use routine parameters as working variables
This guideline suggests that if you need to make some intermediate calculations and store them somewhere, do not use the routine parameters. Instead, create a local variable. Let us look at the following example:
int calculateYearsToRetirement(int employeeAge) { constexpr int cRetirementAge = 55; employeeAge = cRetirementAge - employeeAge; return employeeAge; }
As you can see, we have one integer parameter employeeAge. We pass it by value so even if we do changes to it, those changes will not affect the variable at the routine call. However, the experts do not consider this as a good practice. We have passed this argument by value and our idea is to use it only as an input. Usually, we should not modify inputs. Yet, we modify it and then return it.
One situation, where our code might fail will be the following: If for some reason we change the argument to be passed as a reference or pointer, but not constant ones, and we do not make any change in the routine code, we can get a nasty surprise. A variable we provide to this routine as a parameter will be changed without expecting this to happen.
The worst thing is that the compiler will not give us any error or warning. However, we can force it to do so simply by making the argument to be passed as a const value. The compiler will caught any attempt to modify a const value as an error. This way, we will get warning and we will not make this mistake.
Document the parameters
It is very important to document the parameters well. Make sure to write whether the parameters are input or output; what is their range of allowed values; if the parameter is a status or error code, make sure to describe the meanings of the possible values.
In addition, they consider it as a good practice to use assertions to verify that the provided values are within the range of available values. This will ensure their correct usage.
Routine parameters’ passing
We described good practices on providing the routines with parameters. Here, in short, I would like to describe the three ways on passing the parameters and how/when to use each one of them.
Firstly, let us start with the passing by pointer or reference (non-constant ones). We use this method when we want to be able to change the passed object. The reference and the pointer give us access to the object itself, not some copy. When we do changes in the routine, these changes will affect the object in the parent routine. We should be careful with this method. The experts suggest using pass-by-pointer only if we know that we might pass a “no object” value (null pointer). In any other case, we should prefer the references to the pointers.
Passing by const reference/pointer is the other approach. We use it when we want to avoid copying and pass the object itself. In this case, we do not want to change the object. Again, prefer to pass reference instead of pointer. This will save you some null pointer checks.
Finally, we can pass by value. Use this method if you do not want to change the object and the object itself is small enough – usually a couple of bytes.
Macros
I want to mention few words about the macros. The reason is that we can use macros to define routines. What the experts suggest is to try to avoid using macros at any cost. In the famous C++ FAQ, they say: “macros are evil in 4 different ways: evil#1, evil#2, evil#3, and evil#4.” Check those links to get more insights on why we should avoid macros.
Here is a list of what you can use in C++ instead of macros:
- const or constexpr variables.
- Inline functions.
- Templates for defining standard operations like min and max.
- enum class for enumerated types.
- typedef for defining type substitution.
In case you cannot avoid using macros, make sure to follow the below guidelines.
Add the macro expressions inside parenthesis
Let us look at the following macro
#define FUNC(a, b) a*b
If we call it like this, everything will be working as expected:
int a{ 1 }; int b{ 2 }; // ... int result1 = FUNC(a, b);
It will expand to a * b as we expect. But what if we call it like this:
// ... int result1 = FUNC(a + 1, b + 2);
This will expand to a + 1*b + 2. This will not give us the result we expect. It will multiply 1 and b instead of summing a with 1 and b with 2 first and then multiplying them.
What we want to do is to put them in parenthesis. Like this:
#define FUNC1(a, b) (a)*(b)
This is better. It should work as expected. However, there might be situations where the macro will not expand as we expect it. That is why, the best approach is to put it in parenthesis like this:
#define FUNC2(a, b) ((a)*(b))
Now under any circumstances the macro will expand and work correctly.
Surround macros with multiple statements in curly brackets
Let us look at the following example.
#define MACRO(a, b) \ b += a; \ ++a;
At a first glance, it looks OK. But what if we call it like this:
while (a < 10) MACRO(a, b);
What will happen is that only the first line of code will be executed inside the while loop and we will get into an infinite loop. And this is a nightmare. If instead we surround the macro in curly brackets like this:
#define MACRO1(a, b){ \ b += a; \ ++a; \ }
Then even if we use it in while loop without curly brackets, the macro itself will add some and we will not get into the situation with the infinite loop.
Name the macros the same way you name your routines
This would be very helpful if you decide one day to change the macro to a routine. You will have to change only in one place instead of many. In addition, you can change a routine into a macro without the need to make changes in multiple places.
Conclusion
Again, our tutorial was long. This time it might have had fewer code pieces, so hopefully it will not be very boring. If you have read up to this point– congratulations, you have learned some useful coding guidelines.
Here, we saw how to make better, high-quality routines. We will later see how to fill them with high-quality code. For now, you know how to define them so they will look professional.
The code will be available in GitHub and as an archive below.
Next time we will see more coding guidelines.


Passionate developer, loving husband and caring father. As an introvert programming is my life. I work as a senior software engineer in a big international company. My hobbies include playing computer games (mostly World of Warcraft), watching TV series and hiking.