top of page
  • Writer's pictureMichael Paltsev

Cyclomatic Complexity 101: A Developer's Guide to Code Simplicity


Introduction

Have you ever come across code that's very easy to understand, while some code makes you want to pull your hair out (for as long as you have some hair left on your head)? And let's not even get started on writing unit tests for the latter one - it sometimes takes twice the effort of writing the code itself.

Well, it turns out there's a connection between code clarity and testability. In this post, we're diving into Cyclomatic Complexity: What is it (and what isn't it)? How can we measure it? Why does it matter? How can we improve it? And what will we get by doing that?


What is Cyclomatic Complexity?

Cyclomatic Complexity, developed by Thomas J. McCabe, Sr. in 1976, is a software metric used to assess the complexity of a software program. This metric is determined by analyzing the program's control-flow graph, which can encompass the entire program or smaller sections like methods, functions, or classes. In this graph, nodes represent groups of program commands that can't be further divided, and edges connect nodes if one command can immediately follow another.

But why bother with Cyclomatic Complexity when you can assess code by your gut feeling, right? Well, the truth is, while personal judgments have their place, they're not always systematic, can be error-prone, and don't provide specific improvement insights.

Cyclomatic Complexity offers a standardized and objective method for evaluating code, ensuring that everyone assesses it the same way. By understanding the complexity of different code segments, we can strategically focus our efforts on reducing complexity where it matters most. This, we'll soon learn, will help us to achieve other things as well.


Calculating Cyclomatic Complexity

To calculate the complexity we use a very simple formula:

Where

  • M is the Cyclomatic Complexity

  • E is the number of edges in the graph

  • N is the number of nodes in the graph

  • P is the number of connected components

Using this definition, we can conclude that the values of Cyclomatic Complexity range between 1 and, well, infinity.

But you don't have to know this formula to do the calculations. For as long as you are using a decent IDE, you can use a plugin to calculate it for you.

Nevertheless, for the sake of a good demonstration, let's try to calculate the Cyclomatic Complexity of a relatively simple method:

So, we have 1 if/else statement, and each branch has 1 assignment expression. They all end with an output expression. That means that we have 5 nodes, and 5 edges, and the number of connected components that we have is 1. That means that our Cyclomatic Complexity, based on the formula that we saw earlier, is 2.


What do we do with the value?

Okay, so we know what the value of the cyclomatic complexity of our code is. But what is an acceptable value, and what does it tell us?

You should not worry because Thomas McCabe Jr thought of it as well and introduced us to the following table:

Cyclomatic Complexity Value

Code Complexity

Risk

1 - 10

simple

little risk

11 - 20

more complex

moderate risk

21 - 50

complex

high risk

> 50

un-testable code

very high risk

If we apply this to the value that we got in our previous paragraph (which was 2), we can conclude that our code complexity is simple and that this method does not pose a risk.

But there is another, more interesting, thing that we can understand from the cyclomatic complexity value: it tells us, in advance, the number of different unit tests that we need to write for us to get a complete instruction coverage of our code. This number, you've probably guessed it, is the value of the cyclomatic complexity.

The more you think about it, the more obvious it gets: higher cyclomatic complexity, means more unit tests which in turn means more code complexity which in turn increases the risk that this code possesses.


How to Keep Cyclomatic Complexity Low

First, if you haven't already, consider reading 'Clean Code: A Handbook of Agile Software Craftsmanship' by Robert C. Martin. This book offers valuable insights into writing clean and maintainable code, which ultimately leads to lower Cyclomatic Complexity. If you're looking for immediate tips, read on.


Reduce the number of your If statements

Excessive if statements, especially nested ones, can lead to higher code complexity.

Consider doing the following when you write or refactor code:

  • Check to see if you can invert an if statement and whether or not it will decrease the number of nested if's that you have.

  • Analyze if these if branches represent different behaviors for the object. If this is true, maybe some design patterns such as the decorator pattern can be of service.

  • If your if statements are constantly checking the validity of values, create a contract that will eliminate the necessity of such checks.

  • Keep your if conditions as simple and as easy to read as possible.

There are more, but these are the major ones.


Keep your code blocks small

By this, I mean that your methods and functions should be as small as possible. There are two major benefits: the less code you have in your method chances are that its complexity is smaller (and as a result, makes it easier to read), and the less code you have directly contributes to the cyclomatic complexity value.

To do that, you need to first make sure that your method does exactly what is supposed to do. E.g., if your method name is calculatePi it should only do that. It should not try to print the calculation out or do anything else.

Next, you need to remove any unused code. Yes, even if this code will maybe someday be needed by someone. When this day comes, someone will write that code.

Last but not least, if you have duplicated code in several methods, extract it to a new method (or even to a different class).


Eliminate Cross-Cutting Concerns

What I mean by that is that a class should only have the logic that has to do with that specific class. Don't be tempted to add logic that is vaguely relevant to the class.

Doing this, by usually extracting the unneeded logic, or even better, deleting it altogether if it is not needed, will have a very positive impact on the overall cyclomatic complexity score of your class. This is because less code means less complexity.


Composition is always Better

When you try to extract logic, always remember that you should favor composition over inheritance.

This will allow you to better reuse your code thus reducing the cyclomatic complexity even further. In addition, inheritance can increase the cyclomatic complexity of classes in the present or the future when new capabilities will be added to the base class.

You can even use aspect-oriented programming to try and mitigate these issues.


Conclusion

The Cyclomatic Complexity, as we've just learned, is a very important metric that can predict the complexity of our code and the effort that it will take us to maintain it. We've discussed about how to analyze the code and get its cyclomatic complexity score. We then made a connection between that score and the amount of maintenance that this code will require, thus allowing us to refactor it on time. And, in the end, I've also shared some tips about how to reduce the cyclomatic complexity if needed.

I hope that this post was as helpful as it was fun to write. Would love to read your comments.

Recent Posts

See All
bottom of page