Code complexity is a beast to deal with. Since a project’s goals and functionality change over time, the codebase generally grows and evolves. In the end, you wonder why huge sections of the codebase are even in there.
In addition, there can be significant problems with code complexity as a project comes to a close. If you’re a team lead, you and your dev team end up spending a horrendous amount of time reviewing extremely complex code and trying to iron out the last set of bugs.
In this article, I’ll explain what code complexity is and how to measure it. Why does it matter? Because once you’re able to measure the complexity of the code, you can manage it. Complexity might be a part of every codebase, but you can strive to put a design and structure in place that handles it and manages the side effects.
Defining Code Complexity
The complexity of a given piece of code has to do with just how complicated and unwieldy it is for a developer to understand. This idea makes intuitive sense. For example, the picture at the top of the article is way more complex than, say, a picture of a single rose on a white background.
In particular, the codebase’s size can make it difficult for anyone to wrap their heads around exactly what’s going on. This becomes even more of a problem when the code begins to have multiple paths through conditionals, has several dependencies, or is coupled with other parts of the codebase.
Just think about the following two example functions:
x2 = x * x
result = 
for x in lst:
if x > 3:
x2 = x * x
x2 = x * x * x
Just by looking at the two functions in a) and b), you can easily see that b) is more complex because there’s a lot more going on. In the second example, there are far more lines, there’s a for loop, and there’s an if-else statement that makes it more complex.
Why Code Complexity Matters
Okay, great, so it’s clear that some code is more complex than other pieces of code. But why does it matter? Some code just has to be more complicated because it has greater functionality. So it needs to be more complicated, right?
Some code needs to be more complicated, sure. But we’re looking for code that’s unnecessarily complex.
There are dangers with having code that’s extremely complex. You run the risk of increased code defects, increased time fixing bugs, and unreliable testing.
Robert C. Martin pointed out that “the ratio of time spent reading versus writing is well over 10 to 1.” You don’t want to increase the reading side of the ratio any further by producing unnecessarily complex code. Decrease unnecessary complexity, and you will decrease the time to production.
How Does This Keep Happening?
As Brandon Pearman points out, a codebase can become more complex through no fault of a single developer on their own. Each dev comes in each day and just adds their simple, efficient line of code to the codebase. But over the lifespan of the project, every developer coming in every day adds up. It’s the compound effect!
Now, you may think that this sounds impossible to control. Obviously, you want your team coming in and coding each day. (That’s why they get paid!) However, don’t fret. It’s possible to reduce unnecessary code complexity.
Below, I provide three avoidable causes of unnecessary complexity. You can avoid or lessen each of these problems. Doing so is better for everyone, as developers want to spend less time fixing bugs and more time producing new products.
Cause #1: Making Irreversible Decisions
Making irreversible decisions in the codebase will cause developers to have to hack around those decisions whenever they cause issues later down the line in the life cycle of the product. Each one of these workarounds will add unnecessary complexity to your codebase. Thus, you must have clarity and visibility with where you want your project to go so that in the future you aren’t trying to backtrack. Design a plan for any irreversible decision while acting quickly on any easily reversible decision.
Cause #2: Coupling
Coupling is the connection between one piece of code and another. Keep it to a minimum. If a problem arises with one piece, you’ll need to adjust each piece that’s coupled to it. A developer can fall down the rabbit hole trying to find out which coupled function didn’t get updated.
Avoid this problem by maintaining solid principles and guidelines for your team. That way your code will remain decoupled and flexible to future change.
Cause #3: Poor Readability
Refer back to Robert Martin’s quotation. Your team is going to be reading the codebase significantly more than writing code into it. So if the code is too complex and lacks readability, it’ll continue to slow down the production process.
Developers have great control over readability. With an increase in readability comes an increase in the quality of the code.
What Can You Do About Reducing Code Complexity?
The simple first step is to measure it. If you can measure it, you can manage it.
This is where you can upgrade to making data-driven decisions that no one can argue with. Luckily for us, several metrics can help measure code complexity and help you identify potential areas for improvement within the codebase.
- Cyclomatic Complexity
- Lines of Source Code
- Lines of Executable Code
- Coupling/Depth of Inheritance
- Maintainability Index
- Cognitive Complexity
- Halstead Volume
- Rework Ratio
Let’s look at each of these.
This metric is by no means comprehensive, but it can be a valuable asset to determining the complexity of a given piece of code. Originally developed in 1976 by Thomas McCabe, it measures the number of linearly independent paths through a program’s source code. You compute it by using the control flow graph of the program.
Here’s an example of a control flow graph:
Cyclomatic complexity measures the number of nested conditions within the code, such as those created by for, if/else, switch, while, and until. The greater the number of conditions (as in example b from above), the greater the complexity.
The cyclomatic complexity is the number of different pathways through the control flow graph. You can calculate it this way:
Cyclomatic Complexity = E – N + 2 * P
where E = number of edges, N = number of nodes, and P = number of nodes with exit points.
Lines of Source Code or Lines of Executable Code
These metrics are extremely easy to calculate and purely look at the number of lines of code. Source code measures the white space in the code as well, while executable doesn’t.
Coupling or Depth of Inheritance
This measures how intertwined and dependent a class or function is in relation to all others in the codebase.
This is a single value that measures how easy it is to maintain the code. It’s a combination of the four metrics above (cyclomatic complexity, lines of source code, lines of executable code, and depth of inheritance/coupling).
This measures the amount of cognitive effort required to understand the code’s flow. You compute cognitive complexity similarly to cyclomatic complexity. However, it doesn’t increment with if statements that have logical operators in them. Cognitive complexity only increments once with switch cases, but it does increment complexity with nested flow breaks.
This measures the amount of information in the source code. It counts the number of variables and how often they appear.
Your Rework ratio is the percentage of recently delivered code your team is already rewriting. While this isn’t a direct measure of code complexity, it is a potential indicator of overly complex code you may want to keep an eye on.
There you go! Now you’re aware of what code complexity is, why it’s important, what it does, and how to measure it. Now, the trick becomes how to continually maintain the code.
The metrics in this article are fantastic indicators to use throughout the project. They’ll allow you to catch any potential fallacies before they become horrendous monsters rearing their head out of the codebase.
My suggestion to you is to get a demo of LinearB and check out all the metrics they track. Tracking these metrics lets you help your team maintain high-quality code without interrupting your developers.
Make your life easier! Begin tracking code complexity before it becomes a problem. Your future self will thank you.