Modern software development projects often involve dozens of moving parts intricately pieced together by programmers on distributed teams.
When the underlying code—in its architecture and implementation—is less complex, then it’s easier to understand.
And when you understand your codebase, this goes a long way in enhancing its quality and maintainability.
Cyclomatic complexity is a quantitative measure of a computer program's complexity used in computer science.
In essence, it reflects the number of linearly independent paths through a program's source code.
By monitoring this metric, you can identify code areas that are potentially problematic or overly complex, which makes them harder to maintain and more susceptible to errors.
Understanding cyclomatic complexity will help you write better and cleaner code.
In this post, we’ll explore what cyclomatic complexity is, how it is calculated, and why it matters. We'll also discuss how to test and analyze this complexity, and the tools available to help you manage it effectively.
What is cyclomatic complexity?
Cyclomatic complexity is a software engineering metric that was introduced by Thomas J. McCabe in 1976. This metric is a numerical score that represents the number of different paths a program can take during its execution.
A higher score means more execution paths (higher complexity), while a lower score means fewer paths (lower complexity).
Programs with high cyclomatic complexity tend to be more prone to errors and harder to test and maintain. On the other hand, a lower cyclomatic complexity value suggests that the program is highly readable and easier to understand, test, and modify.
By quantifying the complexity of a program, developers are better prepared to approach and prioritize code modifications, refactoring, and testing. When it comes to managing larger codebases—where the risk of bugs increases with complexity—the cyclomatic complexity metric becomes especially useful.
What is the formula for cyclomatic complexity?
The formula to calculate cyclomatic complexity is relatively straightforward. Start by observing the program’s control-flow graph—a graphical representation of all paths that might be traversed through a program during its execution.
- Control-flow graph examples of simple programming scenarios
(a) if-then-else statement
(b) while loop
(c) a natural loop with an if statement break in the middle
(d) a loop with two entry points
Looking at a control-flow graph, we would represent the number of edges in the graph as E, while N represents the number of nodes in the graph.
The formula for cyclomatic complexity C is:
C = E - N + 2P
where P represents the number of connected components. For a single program, P = 1.
This calculation gives us the number of linearly independent paths through the code. It indicates the minimum number of paths that you need to test to ensure each decision point is executed at least once. When C is high, then you have more complex code with more paths—meaning potentially higher maintenance and testing effort.
A simple conclusion we can draw from considering cyclomatic complexity is this: if, while, for, or switch statements add complexity because each condition introduces a new path in the flow of the program. As a developer, you can look at this formula and quickly gauge the complexity of any part of your program.
Understanding and applying this formula will help you keep your code clean, efficient, and maintainable.
What is an example of cyclomatic complexity?
To illustrate cyclomatic complexity with a practical example, let's look at a basic piece of code written in JavaScript. This example will help us understand how cyclomatic complexity is calculated and why it matters.
This straightforward JavaScript function checks if a user is eligible for a discount based on various conditions:
We can represent these lines of code with the following control flow graph:
Let’s walk through how we would calculate cyclomatic complexity.
First, let’s look for decision points in the function. In our example, there are two conditions:
- if (age > 60)
- else if (membershipDuration > 5).
Each decision point can lead to different outcomes:
- The condition age > 60 is true
- The first condition is false, but membershipDuration > 5 is true.
- Both conditions are false.
As we look at the control-flow graph, our total number of edges is 8. Our total number of nodes is 7. And because the entire function is a single connected component, P = 1. When we apply the formula, we get this:
C = E - N + 2P
= 8 - 7 + 2(1)
= 3
This cyclomatic complexity score of 3 indicates that there are three distinct paths through the code. Each of these paths must be tested to ensure all scenarios are covered. By breaking down our code methodically like this, we clarify what’s needed for software testing and highlight the complexity in our code—complexity that could potentially be simplified.
How to test cyclomatic complexity
Testing cyclomatic complexity, stated simply, means ensuring that you’ve adequately tested all of the possible paths in your program. Adequate test code coverage is important if you want to keep your code quality and reliability high.
After you have calculated the cyclomatic complexity of your code, break down your code to identify what those paths are. Here’s a tip: Each decision point in your code (such as conditional statements or loops) usually contributes to a new path.
Once you’ve identified your paths, create test cases for each one. The goal is to execute each path at least once. This ensures that all the functional aspects of the code are tested, and it will help you in catching potential errors in code logic.
Use automated testing tools to run your test cases. After analyzing the test results, check for any failures or unexpected behaviors. Each failed test case might indicate a bug or a flaw in that path of your source code.
If all your tests are passing but you feel like the cyclomatic complexity is too high, then you can consider refactoring the code to simplify it. This might involve breaking down complex functions into simpler ones or reducing the number of decision points. After refactoring, repeat the testing process to ensure the new code maintains or improves quality.
By regularly determining cyclomatic complexity and then testing your code accordingly, you can maintain a manageable level of complexity. This simplifies the testing process and your codebase, leading to more reliable and maintainable software.
How to do cyclomatic complexity analysis
Cyclomatic complexity code analysis involves reviewing your program’s source code to understand its structure and identify areas where the complexity can be reduced.
To do this, you would start by gathering all the source code that needs analysis. This might be a specific module or a set of functions—or maybe even an entire application—depending on your focus.
You can use tools like static code analyzers to automate the process of calculating cyclomatic complexity for each function or module. These tools will help you in providing a quick overview of your code complexity.
From there, identify any parts of code with high complexity scores. These are the areas that may be potentially difficult to understand, test, and maintain. Prioritize areas that should be refactored. Determining which areas those are will depend on their complexity and the criticality of their functionality within the application. Focus on areas where reducing complexity will yield significant benefits for maintainability and reliability.
As you refactor and the cyclomatic complexity decreases, you may find that your number of test cases also decreases. After refactoring, rerun your tests (which also have been rewritten) to ensure you haven’t broken anything. Then, recalculate the cyclomatic complexity to ensure that your changes have effectively reduced the complexity.
By regularly performing cyclomatic complexity analysis, development teams can proactively manage their codebase, ensuring that it remains clean, testable, and maintainable. This practice will help you improve code quality, and it’ll ultimately make your development processes more efficient.
What are tools and software for cyclomatic complexity?
Identifying and dealing with code areas with high cyclomatic complexity is crucial for maintaining high-quality, maintainable software. Many tools and software solutions are available to help developers assess and monitor this complexity.
- SonarQube is a comprehensive code quality tool that integrates directly into your development workflow. It provides detailed reports on cyclomatic complexity, among other software metrics. SonarQube helps identify complex code that might need refactoring and supports 30+ programming languages (including Java, Python, Go) and frameworks.
- SonarCloud is a cloud-based service that provides automated code review for complexity analysis. This is particularly useful for continuous integration/continuous deployment (CI/CD) and DevOps platform environment, offering real-time feedback on code changes and their impact on complexity.
- SonarLint is an IDE plugin that provides on-the-fly feedback to developers as they write code, helping them to maintain low complexity from the start. It can be used in popular IDEs like Visual Studio, Eclipse, and IntelliJ IDEA.
These tools provide valuable insights into code complexity, helping teams make informed decisions about where to focus their refactoring efforts to improve maintainability and simplify debugging.
Conclusion
Understanding and managing cyclomatic complexity is important if you want to develop clean and high-quality software. This metric serves as a guide for developers to identify areas that may be prone to errors or difficult to maintain. By regularly measuring and analyzing cyclomatic complexity, you can ensure your code is both efficient and maintainable.
To get started using tools that will help keep your cyclomatic complexity low and your software quality high, use SonarSource tools for free today.