Writing code is fraught with problems. One of those problems is letting the code get more complex than it needs to be to achieve its purpose.
I first came across Cyclomatic Complexity (CYC) when I was looking for a way to review some code. I wanted to be sure that the code not only worked, but was easy to maintain.
I’ve used it many times since, when checking code written by outside agencies.
Not because I want to be awkward about it.
But maintaining the codebase in-house becomes an issue when the code is complex.
Note what I said there: when the code is complex.
It’s possible (and normal) for the code to be solving some difficult problems. Tackling those problems is what can cause complex code.
Cyclomatic Complexity - sorry, what?
CYC in its most simple form counts the number of decisions in the source code.
Thomas J. McCabe Sr. in 1976 was the chap that developed it.
The more decisions there are, the more complex the code is. Remember, we are talking about how the code gets its work done. The source code doesn’t get called complex because it’s dealing with difficult issues.
There is a formula to work it out:
CYC = E – N + 2P
- P is the number of disconnected parts (subroutines)
- E is the number of transfers of control
- N is the number of nodes or sequential group of statements with only one transfer of control
If you are thinking lots of if/else conditionals are bad news, you’d be right. A score of 1 gets added to the complexity score each time it happens.
Transfer of control
For me, transfer of control is where things can get complicated.
Take a mid-sized Laravel web application for example. The Laravel framework provides loads of ways to build modular code.
The trouble starts with sub-classes.
Let’s say you have a controller to look after customer orders. You’d also have a model.
Because there is some processing needed, you might choose to use service classes. The controller uses the service classes to run a process.
When you think about it through a CYC lens, you’ve handed over execution control to the service class.
One transfer on its own is not a problem, of course. But if you have many service classes for many of the tasks called upon from the given controller…
Now we are layering up the complexity.
Some developers like to use repository classes too. While there is nothing wrong with that in principle, it does make another transfer.
More transfers = more complexity.
The design decision here then, is:
Do we reduce the number of linear paths to get our work done, or do we focus on architecture alone?
Or: which one do we give a crap about the most?
- Keeping complexity minimal
- Design patterns
Complex code is difficult to test. And it is difficult to maintain.
There are good reasons for using service classes, modules and repositories. That’s true. But not if they cause complexity.
The trick to it is: don’t overuse them. Try to avoid transferring control around all over the place. Then, your codebase won’t be difficult to maintain.