Some functions are more complex than others. Sometimes this happens due to objective reasons (necessary complexity). Sometimes — due to the characteristics of the programmer who wrote it (random complexity). And although it is impossible to unambiguously describe this complexity, there are ways to at least partially evaluate it.
Cyclomatic complexity is a structural, or topological, measure of the complexity of a computer program developed by Thomas J. McCabe in 1976.
The cyclomatic complexity of a part of a program code is the number of linearly independent routes through a program code. If the source code does not contain any branch points or loops, then the complexity is one, since there is only one route through the code.
If the code has a single statement
ifcontaining a simple condition, then there are two ways through the code: one if the condition of the statement
true, and one if
Such an assessment can be applied both to the program as a whole and to individual functions.
In the example above, the sum function has a
cyclomatic complexity equal to one, and the
abs function has two, since it contains branching, which means two independent execution paths.
The more possible execution paths, the more difficult the function is to understand, debug, and modify. It is obvious that, on the one hand, the functions need to be split, and on the other, the program logic should be described so that unnecessary paths do not appear. Even experienced developers often face this problem.
Linters in many languages measure the complexity index and signal. Let’s say, more than 5 for one function.
The approach I describe, also called the “pattern”, helps to better structure the function and sometimes reduce the cyclomatic complexity. Consider an example:
Everything in this function should be familiar to you.
f accepts age and gender. For people over 18, depending on the gender, returns the string “yes” or “no”. For all others — “null”. In general, with this function, everything is fine, but something can be improved.
The condition “return null if under 18” is much simpler and more obvious. It does not imply further branching and is formulated simply. This can be used and refactored (improving the working code without changing the functionality) so that this condition is fulfilled first.
Note that the nesting level has gone down. The main logic is outside the conditional constructs. In such an implementation of the function, it is more difficult to make a mistake: everything that is written in the above code, guard expression (the first test in this case) falls under the requirement of “18 and older”, and in the first example, the code for this condition must not be forgotten inserted inside the corresponding condition.
In future practices and in real life, it is found everywhere. Use it consciously and lower entropy.