Handling cyclomatic complexity

Cyclomatic complexity is a measure of how many linearly independent paths there are through a program's code.

Consider a simple program that contains several conditional checks and function invocations:

if (a) {
alpha();
if (b) bravo();
if (c) charlie();
}
if (d) delta();

Even in this misleadingly simple piece of code, nine distinct paths can be taken. So, depending on the values of a, b, c, and d, there are nine possible sequences of alpha, bravo, charlie, and delta that will run:

  • alpha()
  • alpha() and bravo()
  • alpha(), bravo(), and charlie()
  • alpha(), bravo(), charlie(), and delta()
  • alpha(), bravo(), and delta()
  • alpha() and charlie()
  • alpha(), charlie(), and delta()
  • alpha() and delta()
  • delta()

A high level of cyclomatic complexity is undesirable. It can lead to the following:

  • Cognitive burden: Cyclomatically complex code can be difficult for programmers to understand. Code with many branches is difficult to internalize and hold in our minds and therefore harder to maintain or change.
  • Unpredictability: Cyclomatically complex code can be unpredictable, especially if rare situations occur where there is, for example, an unforeseen state transition or underlying change of data. 
  • Fragility: Cyclomatically complex code can be fragile in the face of change. Changing one line can have a disproportionate effect on the functionality of many other lines.
  • Bugginess: Cyclomatically complex code can cause obscure bugs. If there are a dozen or more code paths within a singular function, then it's possible for a maintainer to not see all of them, leading to regressions.
There are tools that can quantify a code base's cyclomatic complexity. We will cover these in Chapter 15Tools for Cleaner Code. Knowing areas of high cyclomatic complexity can help us to focus on those areas for maintenance and testing.

It's frustratingly easy to end up in a situation where there are so many different conditions and branches within a singular module that nobody can understand what's happening. In addition to using tools to help us to identify areas of high complexity, we can use our own judgment and intuitions. The following are some examples of complexity that we can easily identify and avoid:

  • A function that has more than one if/else/if combination 
  • An if statement that has many sub-conditions (many if statements within if statements)
  • A switch statement that has many sub-conditions following each case clause
  • Many case clauses within a switch block (for example, over 20 would be alarming!)

These are not precise cautions but they should give you an idea of what you should watch out for. When we find such complexity, the first thing we should do is to sit back and re-consider our problem domain. Can we describe our logic differently? Can we form new or different abstractions?

Let's explore an example of a piece of code with high cyclomatic complexity and consider how we might simplify it with these questions in mind.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.137.75