March 19, 2026 - 5 min
How to Improve Your Code by Reducing Cyclomatic Complexity
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. As cyclomatic complexity grows, the code often becomes more cognitively demanding to understand.
Developing software solutions carries many challenges and complexity is one of them. Handling it can be approached from different angles — keeping cyclomatic complexity in check is one of those angles. In this article, we discuss how cyclomatic complexity emerges, how it is measured, and ways to reduce it with various good practices and a bit of psychology.
What is complexity?
Let’s start with the tautology: complexity is a complex topic. Neil Johnson states that “even among scientists, there is no unique definition of complexity – and the scientific notion has traditionally been conveyed using particular examples…”
Humans have ambigual relationship with complexity since it has the double connotations. At the same time, we can perceive it either positively or negatively, depending on the circumstances. There is no unique complexity, but rather many contextual and situational ones.
Where there is interaction, complexity inevitably follows. Various interacting components – humans, ants, neurons, stars, lines of code – gather into the networks that form complex systems. Most complex systems follow simple rules without a centralised focal point, but still collectively produce sophisticated behavior. They have the ability to process information and adapt through evolution or learning to improve their chances of success. They are characterised by “emergence,” a phenomenon of complex and unpredictable macroscopic patterns arising naturally from the interaction of many simple parts.
Emergence is important in the programming context, since individual parts interact to create behaviors that no single line of code “knows” about. For example, a single if/else statement is a simple rule, but tens or hundreds of them interacting in a complex environment create emergence. When emergence runs unchecked, the result is often the opposite: the emergencies in the form of spaghetti code which is tangled, opaque, and costly to read, maintain, and debug.
Meet the cyclomatic complexity
But the question remains: how do we measure such intricacy? Unfortunately, since there is no common and agreed definition of complexity, there is also no universally accepted quantitative measure to determine exactly how complex a system is. While complexity itself can be elusive, we use proxies, the practical metrics that quantify its various dimensions and offer actionable insights.
Cyclomatic complexity is one of them. It counts decision paths. It can be paired with cognitive complexity that tries to reflect how hard code is to understand. Such measures don’t give one “true” complexity number, but they do give us concrete signals to simplify, refactor, and reduce risk. It is also important to keep in mind that all those metrics are relative and not absolute values.
Developed by Thomas McCabe in 1976, 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 isn’t inherently bad. A function can have a high cyclomatic complexity and still be easy to read.
The problem is when complexity becomes excessive: that’s either a sign of existing design issues or a source of future ones. Cyclomatic complexity is one factor that feeds into cognitive complexity, or how hard a piece of code is to understand. The higher the cognitive complexity, the harder the code is to navigate, reason about, and maintain.
When it comes to cognitive complexity, few concepts are as foundational as “The Magical Number Seven, Plus or Minus Two.” First introduced in 1956 by George A. Miller, this principle suggests that the average human mind can only process or hold about seven functional units of information, usually called chunks, in its short-term memory at any given time.
Whether it was remembering a string of digits, a list of words, or the parameters in the method’s signature, this number was almost a recurring constant. When it comes to coding, by acknowledging that the typical cognitive limit is 7±2, developers can “chunk” complex information into smaller, digestible methods to prevent mental fatigue.
The numbers and what they mean
The minimum cyclomatic complexity is 1. To calculate beyond that value, following rules apply:
- The Floor is 1: Every function starts with a base score of 1 (the straight path from start to finish).
- The +1 Rule: Add 1 for every decision point or loop.
- The Keywords:
- Branching: if, else if, switch/case, catch.
- Looping: for, while, foreach.
- Logic: &&, ||, and ternary operators (? :).

This is true for most languages, but not all (e.g. pure functional programming languages). Also, you don’t have to do calculations manually. Almost every modern IDE has the possibility to calculate the cyclomatic complexity, either by built in static code analyzers or third party ones.
Once the calculation is complete, the resulting value can be interpreted using the following benchmarks:
- 1–10: Clean, testable, and stable.
- 11–20: Moderate risk
- 21–50: High risk; “Spaghetti” territory.
- 50+: Untestable; burn it down and start over.
A cyclomatic complexity between 1 and 10is widely considered good for clean, testable, and maintainable code. That range aligns with Miller’s magical number 7±2. The usual limit on how many chunks of information people can hold in working memory at once is closely related to code’s readability and maintainability.
Here is how that looks in code. The first example has cyclomatic complexity score 11.
public string ValidateUser(User user)
{
if (user != null) // CC +1
{
if (!string.IsNullOrEmpty(user.Username)) // CC +2
{
if (user.Age >= 18) // CC +3
{
if (user.HasAgreedToTerms || user.IsAdmin) // CC +5 (|| adds 1)
{
if (user.Email.Contains("@") && user.Email.Contains(".")) // CC +7 (&& adds 1)
{
return "Valid";
}
else
{
return "Invalid Email";
}
}
else
{
return "Terms not accepted";
}
}
else
{
return "Too young";
}
}
else
{
return "Missing Username";
}
}
else
{
throw new ArgumentNullException(nameof(user)); // CC +1 (implicit)
}
// Total CC: 11 (including base path and all branches)
}The optimised version has a score of 5.
public string ValidateUser(User user)
{
// 1. Guard Clause for null (CC +1)
if (user == null) throw new ArgumentNullException(nameof(user));
// 2. Simple checks with early returns (CC +3)
if (string.IsNullOrEmpty(user.Username)) return "Missing Username";
if (user.Age < 18) return "Too young";
// 3. Combined logical check (CC +4)
if (!user.HasAgreedToTerms && !user.IsAdmin) return "Terms not accepted";
// 4. Final validation logic (CC +5)
bool isValidEmail = user.Email.Contains("@") && user.Email.Contains(".");
return isValidEmail ? "Valid" : "Invalid Email";
}This metric is not without limitations. Counting the number of decision points in the code is a simplification of McCabe’s proposed method of calculating its complexity. Also, cyclomatic complexity does not consider nesting of control flow structures.
A function with three sequential for loops produces the same complexity metric as one with three nested loops. Yet, as already mentioned, nesting significantly increases cognitive load, making the code harder to maintain and more prone to quality issues.
Eating that spaghetti
There are actionable items that the developer can implement to reduce both the cyclomatic complexity and the chances to deliver the spaghetti code:
- Use small methods
- Reduce if/else statements
- Implement early returns
- Leverage polymorphism
- Remove obsolete code
- Eliminate duplicated code
Also, it’s wise to keep “The Magical Number Seven, Plus or Minus Two” in mind and try to follow these good practices to keep cyclomatic complexity in check:
- Limit parameters per function – Keep argument lists short (e.g. ≤ 5–7) and use objects or builder patterns when you need more.
- Name for meaning, not brevity – Use names that carry intent so readers don’t have to hold many mental “slots” to decode what something does.
- One level of abstraction per function – Don’t mix low-level details with high-level flow in the same routine, instead each function should read at a single conceptual level.
- Limit local variables in scope – Fewer live variables in a function reduces how much the reader must track at once.
- Keep related code close – Put related logic and data near each other so “chunks” are easier to form.
- Use small, focused modules – Follow single-responsibility principle, a class should have only one reason to change, meaning it should have only one job or responsibility.
Key Takeaways
Cyclomatic complexity is one code metric that can help to improve the code quality. Paired with Miller’s principle, it can greatly help to reduce the cognitive strain and eliminate the spaghetti code. The result? Code that is not only easier to read and understand, but also far more sustainable to maintain.
Give Kudos by sharing the post!