- Why Metrics Matter
- Cyclomatic Complexity
- Coupling Metrics
- Combination Measures
- Conclusion
Cyclomatic Complexity
The first thing I would like to do in any essay where I describe a concept like cyclomatic complexity is to "de five-dollarize" the word. Cyclomatic complexity is nothing but a complex term that means “how hard is a given function to understand”. Strictly speaking, it measures the number of independent paths through a given piece of source code. It can range from 1, a simple routine with only one path, to hundreds in some programs I have seen over the years that do tasks such as, for example, contain single functions that span thousands of lines with many nested loops and decision points.
So where do we find really high cyclomatic complexity? It tends to occur in cases where any sense of design has broken down, and teams devolve into "script-writing" in utility functions to get things done. One particularly egregious example I saw was a 10,000 line of code single function that converted a large set of data into various types of PDF files depending on various parts in the data set. Such routines tend to be a source of job security for someone in the organization, because it is usually only one or two people who know how to navigate it. Changing such monster routines tends to be a significant source of defects, usually adding more defects than it resolves.
However, the most costly examples tend not to be the single massive cyclomatic complexity monster that stands over a code base, but distributed high cyclomatic complexity that comes from inattention to good object-oriented modeling. A frequent sign of such problems occur when you have frequent use of switch statementsor similar constructs in languages outside the C family. Massive switch count usually reflects a deficiency in modeling, where responsibilities of various classes are being bundled in a single class. These violations of the Single Responsibility Principle tend to contribute towards making the code base far more complex than it needs to be.
Other contributors towards cyclomatic complexity occur when developers do not leverage the functional features of the language they are using. In C#, we might see code like this:
List<int> someLargeListOfNumbers = GetLargeList(); List<int> numberList = new List<int>(); foreach(int number in someLargeListOfNumbers) { if (number < 100) { numberList.Add(number); } }
While this was common code prior to 2008, most languages have gained functional programming features that make writing this kind of code much more concise and less error-prone:
var numberList = GetLargeList().Where( number => number < 100);
This example does in one line what the previous one did in nine, reads more clearly, and as you might expect, has far less cyclomatic complexity.