Why I like Cyclomatic Complexity and Why I don’t

Cyclomatic complexity is a software metric used to indicate the complexity of a program. It is a quantitative measure of the number of linearly independent paths through a program’s source code. It was developed by Thomas J. McCabe, Sr. in 1976.”  This quote is from Wikipedia, although I found the same text (almost word-for-word) in several other sources.  You can check on Wikipedia for history and some supporting material.[1]

In practical terms, Cyclomatic Complexity is a number produced by a software tool that provides a measure of code complexity for a function/procedure.  The number increases as the programmer adds more “if”, “switch”, “for”, and “while” statements to the C code.  Development teams usually set a self-imposed limit of the Cyclomatic Complexity for a function; if the number is above the limit, the function must be partitioned into multiple functions.  The IEC61508 project I coded had a Cyclomatic Complexity limit of 12; additional  audit documentation was required for functions above 12.  One company that hired me used a complexity limit of 20.

Exceptions to the Cyclomatic Complexity limit are almost always needed.  In one project, I created a parse function for incoming HART commands; I used a “switch” statement on the command byte with more than 50 different case statements – one for each HART command.  There is no simple way to partition this into multiple functions.

When a tool produces the Cyclomatic Complexity number, it must be considered a relative number.  There is no absolute standard for measuring Cyclomatic Complexity.  Different tools may produce different numbers for the same function.  I have sometimes stared at a function trying to figure how the tool produced its result.  Tools differ on their handling of “&&” and “||” boolean operations inside an if statement.  Dr. Benjamin Hummel in his blog “McCabe’s Cyclomatic Complexity and Why We Don’t Use It” provides a single Java code example where different tools produced results of 2, 4 and 5. [2]

Even as a relative measure of code complexity, the Cyclomatic Complexity number lacks a certain “humanity” in its measurement.  I thought my use of the “switch” statement to distinguish between HART command was very easy to understand.  In the same C module, I had a function with 3 “ifs’”, each having one or two “else if’s” (as well as the “else” for each “if”).  I believe this function had a Cyclometric number in the mid-teens, yet the reviewers felt it was much more complex.  Cyclometric Complexity does not measure the depth of the conditional statements.  It also fails to account for more difficult concepts like code recursion, “continue” statements in a “for loop, or using a “throw” for error handling. [3]

Unlike Dr. Hummel, I like and use Cyclometric Complexity.  It provides a simple numerical metric, which a development group can use to flag functions that require additional coding or code review resources.  Yes, other code metrics also can be used (as suggested by Dr, Hummel) including function length (number of lines), depth of conditional statements, number of passed parameters, etc.  Personally, I am experimenting with counting the number of decision points in a function. [4]

Therefore, I recommend the use of Cyclometric Complexity, but I wouldn’t be caught up in the exact number.  There is not necessarily a complexity difference between a function with a metric of 10 vs. one for a metric of 12.  Rather, set a limit that indicates additional attention should be tied with the code.  Then either the function can be partitioned into two or more functions or comments added to indicate why the programmers believed the code is a good implementation.

[1] Wikipedia, Cyclomatic complexity, edited on 17 February 2019.   https://en.wikipedia.org/wiki/Cyclomatic_complexity

[2] “McCabe’s Cyclomatic Complexity and Why We Don’t Use It”, Dr. Benjamin Hummel, 2014-05-21.

[3] This is not a recommendation for any of these practices.

[4] Each “if”, “else if”, “while”, “for” and “switch” counts as 1.  The “do”, “else” and “case” statements do not add to the number.   I have yet to set a limit, but I think a tally above 7 or 8 warrants attention.  I should note that I never put more than one “switch” statement in a function.  Obviously, this is still “in development”.