Can you use cyclomatic complexity to gauge a project's health? Learn what cyclomatic complexity is, how to use this metric as a useful guide, how to calcul
<big>What Is It & How Can We Use It? </big>
by Neven Dimac, Software Engineer
In my desire to strengthen my existing knowledge and learn something new, I came across the term cyclomatic complexity. In my relatively short IT career, I observed a few unknowns and enigmas, but the aforementioned term somehow lingered in my thoughts.
As I slowly focused on “writing efficient and maintainable code,” the phrase cyclomatic complexity came up in obscure formulas and incomprehensible explanations.
I wanted to dive into the phrase’s meaning and learn more about what’s hiding behind this term, how I can incorporate its method into my day-to-day job, and whether my team can benefit from it in their workflow.
What Is Cyclomatic Complexity?
First, what is cyclomatic complexity and how is it used in software engineering?
Thomas McCabe coined the term in 1976, and according to Thomas, it represents the measure of the decision logic in a single software module, to be more precise. It’s a software metric that gives the quantitative measure of the logical complexity of the program. It also can be explained as the measurement tool for the number of linearly-independent paths through a program module.
Teams can segregate the function into two primary uses:
- The number of the recommended tests for software
- Maintainability and reliability of the software
The Cyclomatic Complexity Formula
The McCabe methodology or The McCabe metric is based on graph theory and mathematically analyzes the software's structure or design.
Figure 1. Three methods of evaluating the cyclomatic complexity of the graph. Riabov, Vladimir. (2007). Graph Theory Applications in Developing Software Test Strategies for Networking Systems. Rivier Academic Journal. 3. 1-13.
As shown in Figure 1, several approaches for computing and evaluating code complexity exist. But the formula that’s most mentioned and propagated through the rest of the literature and technical articles is:
V (G) = E - N + 2
E represents the total number of edges in the flow graph.
N represents the total number of nodes in the flow graph.
Let’s compare the code example with the design flow graph to analyze further and understand the formula.
Figure 2. Comparison of the design flow graph with code example.
In Figure 2, we can see the comparison of the design flow graph with the code example. If we further break down the comparison values by assigning values to the variables that represent number of edges (E) and number of nodes (N), we get 7 edges and 6 nodes.
This results in a cyclomatic complexity of 3:
V(G) = E – N + 2 => V(G) = 7 – 6 + 2 = 3;
Increasing the Complexity of the Code
Learn more about checking and analyzing the complexity of code with C# and Visual Studio.
Each flow graph consists of nodes and edges. The nodes represent computational statements or expressions, and the edges represent the transfer of control between nodes besides the keywords from Figure 2.
The use of loop constructs can increase complexity (for, for each, while), switch block (number of case statements), jumps statements (continue, goto), exceptions (catch), and conditional enablers (and, or, ternary operator).
Implementing any of those constructs introduces a new path through the code.
Cyclomatic Complexity – C#
If we analyze the complexity metrics and calculation approaches through the programming languages we can detect a discrepancy.
Unfortunately, the calculation algorithm for cyclomatic complexity is not standardized in the programming world. The official Microsoft documentation has a slightly different formula:
cyclomatic complexity = the number of edges - the number of nodes + 1.
Figure 3. Exception catch complexity.
Code from Figure 3 will generate the complexity of one, which can be seen in the image above. But according to the formula from Figure 2 should be a complexity of 2.
Based on NDepend (a static analysis tool for .NET managed code) source, it says that calculation represents the number of decisions that can be taken in the procedure.
For C# specific, the complexity of a method is 1+ for the following expressions:
- Ternary operator
And the following expressions are not calculated in the result of the cyclomatic complexity algorithm:
- Object creation
- Method calls
Cyclomatic Complexity – Visual Studio
Checking and analyzing the complexity of the code with the help of Visual Studio (the path can vary depending on the version of Visual Studio) is a reasonably straightforward task.
Visual Studio comes with a built-in calculator for code metrics that returns simple code metric data such as maintainability index, cyclomatic complexity, etc.
In Visual Studio, for any given method, the value of 25 is considered dangerously high and causes Visual Studio to create an error.
To activate the code metrics results window in Visual Studio, we should navigate to the analysis and select Calculate Code Metrics and cyclomatic complexity.
See an example on the next page.
Figure 4. Complexity check via navigation toolbar.
After the computation is complete, the window for Code Metrics Results will appear with metrics (dependent on the version of Visual Studio) for:
- maintainability index
- cyclomatic complexity
- depth of Inheritance
- class coupling
- lines of source code
- lines of executable code
Visual Studio Examples
With the first example, we notice that cyclomatic complexity is at 1, the lowest possible value.
Figure 5. Cyclomatic complexity of 1.
Figure 6. Cyclomatic complexity of 2.
The example from Figure 6 has an additional decision code, which increased complexity by +1.
Figure 7. Cyclomatic complexity of 6.
In the code example from Figure 7, we’ve increased the complexity to the value of 6 by adding one more if statement followed by the switch case with three decisions. At this point, we could think about lowering the complexity.
To lower the complexity of the code, we could extract statements into the separated methods.
Figure 8. Extracted methods.
As shown in Figure 7, the extracted methods lowered the main program by five and now have an initial value of 1. But now we’ve got the two separated methods LowComplexitySwitch and LowComplexityIf, with the complexity values of 4 and 3.
With that approach, we cleaned the code a bit and improved the readability and maintainability.
However, we should notice one more metric tightly correlated to the cyclomatic complexity: the maintainability index. If we compare the maintainability index from Figure 7, which is 67, with the Figure 8 value of 83, we can see the increase in the value.
In this case, of the maintainability index, the higher number makes for higher code maintainability.
When writing code, we must focus on designing code that will be maintainable; on writing small, readable methods to meet defined standards.
We recommend setting metrics and thresholds to help you write quality and readable code, which will prevent code decay.
Through different literature and data sources, the metrics vary. As we have had the opportunity to see in the example of the C# algorithm for calculating the complexity of the code, approaches differ from one language to language. Unfortunately, it’s still language-dependent.
In his book “Code that Fits in Your Head Heuristics for Software Engineering,” Mark Seemann focuses on the number 7 as a threshold for method complexity. And if the method exceeds the upper limit, it needs to be broken down or extracted as a separate method. However, due to the diversity of software development approaches, different levels of knowledge of development teams, and the complexity of the software solution, the defined standards of code complexity also vary. So, we can find some approaches that advocate the number 10 as the upper limit, even higher, up to 15.
According to Thomas McCabe, the proposed upper value is 10. Limits over ten should be reserved for projects with several operational advantages over typical projects, such as an experienced team, a modern programming language, structured programming, code walkthroughs, and a comprehensive test plan.
Remember, the higher the complexity, the higher number of tests needed. In the table below, we can review the proposed metric for the project. Of course, metrics can vary, but presented values should be valid referent points for defining the complexity thresholds.
Figure 9. Metrics threshold for Cyclomatic Complexity.
Complexity Comparison: Common Pitfalls
As previously stated, cyclomatic complexity counts the number of logical paths through a function. The algorithm counts the decisions in the code, sums them up, and calculates the complexity value.
Unfortunately, there are some pitfalls if we only rely on cyclomatic complexity as the final insurance of the code quality and maintainability.
Figure 10. Complex cyclomatic complexity value.
If we compare the complexity value from Figure 7 with the complexity value from Figure 10, we can easily detect that value from Figure 7 is higher than Figure 10.
Does this mean that code from Figure 7 is more complex than code from Figure 10?
Unfortunately, no. If we compare the code from Figure 7 with the code from Figure 10, the code from Figure 7 can be easily read and understood without effort, while the code from Figure 10 requires some additional effort to understand.
More specifically, cyclomatic complexity doesn’t reveal the relative effort needed to understand or modify a piece of code.
With additional practices in mind, can we conclude that analyzing the cyclomatic complexity is a poor practice to follow when trying to reduce the code complexity?
Analyzing the cyclomatic complexity metric is not necessarily a poor approach to code quality because it closely correlates to cognitive complexity. The higher the cognitive complexity, the harder it is to read and maintain the existing code base.
Generally speaking, too high cyclomatic complexity is a symptom of problems in the existing codebase or a potential cause of future problems. We should keep in mind that the more complex a given block of code is, the harder it is to read and maintain.
Reducing the Cyclomatic Complexity Value
Code complexity is the usual source of bugs in the software, a valid point from an abstract and practical/concrete sense, as well. The higher the complexity, the harder it is to read and understand the written logic.
Complex code generated by constant and frequent team changes without defined coding guidelines and standards will eventually result in code decay and a lack of sustainability.
Guidelines to help reduce code complexity:
- Focus on smaller methods – easier to read and understand and maintain.
- Reduce the number of “branching decisions;” instead of if/else and switch, try to implement a decorator and strategy pattern.
- Clean duplicated code – follow the DRY principle.
- Remove obsolete code – the code that is not used should be removed, not commented out.
- Create reusable helpers and services that encapsulate functionality.
Tips for Practical Usage of Cyclomatic Complexity
Apply these practical tips to reduce the complexity of your code.
1. Generally speaking, software engineers search for various ways to achieve code quality and maintainability. But unfortunately fail, due to the various variables that affect codebase quality. With the constant pressure from senior management, tighter project deadlines, frequent changes of the team member, and non-existent or ignored coding standards, eventually, the complexity of the codebase will increase, and the errors will become a common practice and not the exception.
2. To avoid the common pitfalls, we could introduce several practices and rules to help us maintain a quality codebase:
- Detailed PR reviews,
- Unit Tests Coverage,
- Detailed Coding Cookbook,
- Visual Studio stricter compiler options, TreatWarningsAsErrors (all warning messages are reported as errors)
3. Introducing cyclomatic complexity analysis as a part of a Continues Integration build where if the threshold is exceeded the changes will be rejected.
4. Cyclomatic complexity defines the tests process – the number of tests required for a method equals the method's cyclomatic complexity. It measures the minimum effort and best areas of concentration for testing.
5. Cyclomatic complexity is easy to apply a metric rule that could be integrated as an additional validation rule when developing.
6.Cyclomatic complexity could be an additional validity metric during the PR reviews.
Be careful using cyclomatic complexity as a go-to metric for the project's health.
Cyclomatic complexity is not a perfect metric nor a fool-proof method for increasing code quality.
Unfortunately, we can have terrible code with low complexity or relatively quality code with higher complexity. However, higher complexity could be a sign of a potentially larger problem.
High complexity functions are harder to maintain and prone to more bugs.Higher complexity functions directly lead to more complex unit tests, which can make code difficult to maintain in the long run due to the difficulty of testing. We shouldn’t have illusions about the universality and application of these metrics.
Cyclomatic complexity can be a useful guide or additional rule, but ultimately, we must use our own rules. Of course, additional metrics can raise awareness of code decay.
When and if we combine metrics with aggressive thresholds and firm coding standards, we eventually establish a culture that actively pays attention to code quality.
& About the Author
About the Author
Neven Dimac, Software Engineer
From the backend point of view, I'm focused primarily on the C# .NET (Core) stack, which has recently become more and more oriented towards cloud development, where my main focus lies on the consumption of Azure services (Web app, Azure storage, Azure SQL, etc.) and also implementation of Microservices architecture.
A Hitachi Group Company
GlobalLogic is a leader in digital engineering. We help brands across the globe design and build innovative products, platforms, and digital experiences for the modern world.
By integrating experience design, complex engineering, and data expertise—we help our clients imagine what’s possible, and accelerate their transition into tomorrow’s digital businesses.
Headquartered in Silicon Valley, GlobalLogic operates design studios and engineering centers around the world, extending our deep expertise to customers in the automotive, communications, financial services, healthcare and life sciences, manufacturing, media and entertainment, semiconductor, and technology industries.
GlobalLogic is a Hitachi Group Company operating under Hitachi, Ltd. (TSE: 6501) which contributes to a sustainable society with a higher quality of life by driving innovation through data and technology as the Social Innovation Business.
We are on a two-decade journey and proud of our growth and the milestones we have achieved. A startup to a Hitachi group company, our product engineering story is unique. Thanks to our people who have been our companions and growth drivers in this amazing journey. 'We are GlobalLogic' is a spectacular showcase of what we do, what we represent, and our capabilities as a global leader in product engineering services.