Cyclomatic complexity can be helpful in keeping us away from writing complex code that can be more error prone, more complex to be tested and harder to reason about. I would give a special attention for the latter one as it implies in low readability and high maintenance costs. For example, an if/else statement is commonly used as:
if THIS thendo some logic in order to return THAT
if THAT thendo some logic in order to return THIS
if SOMETHINGELSE thendo some logic in order to return THAT and THISelsedo some logic in order to return THIS and THAT
Usually each block contains some logic in order to do something. Naturally each piece of logic requires attention as one must understand what a block is doing, what is its purpose and if the expected result is what is being produced. Now imagine a single function with twenty conditional statements, where each one produces a single different result, and you have to explain (and remember) what are all the values that this function returns and under which conditions. Probably you already got the point.
However, note that cyclomatic complexity doesn’t try to determine how complex each path contained in a control flow graph is. It doesn’t try to understand each block of your program’s architecture, instead, it focus on how most of the programs are written and assumes that all cases are equal. In order to be helpful it assumes that all execution path contains a specific and unique piece of logic, as most of the time, it is the case. I have been thinking about that point since I started programming in Go. Following the error handling pattern we use in Golang, check this other example:
THIS, ERR := fnGetThis()if ERR thenreturn error
THAT, ERR := fnGetThat()if ERR thenreturn error
SOMETHINGELSE, ERR := fnGetSomethingElse()if ERR thenreturn error
WHATEVER, ERR := fnGetWhatever()if ERR thenreturn error
do some logic in order to produce the expected result using THIS, THAT, SOMETHINGELSE and WHATEVER
It seems to me that none of the if statements that only return an error, and has no extra logic, can be error prone, nor is difficult to reason about, nor makes it harder to explain the purpose of that whole block. It does one thing, once everything it needs works.
Maybe a better cyclomatic complexity analysis would be presented if we could choose to discard the cases where returning an error is the only thing that is done, and nothing else. As the real complexity to generate those errors is not written there, but instead, in the functions that are being called.
I would like to hear more thoughts on this subject as my goal here is to discuss about it. So, feel free to leave a comment 😉.