Cyclomatic Complexity - is it a rule or a guide?Filed Under: Weekly Tuesday Dose of goodness
Hi all, continuing from my previous article, I’ll talk about the basic understanding of MCC, also known as McCabe’s Cyclomatic Complexity.
Many a times, this metric has been taken upon as the golden rule of thumb towards code complexity. While it applies to most cases, there’re still some areas where it may not serve its purpose.
Today, we’re going to talk about it in my own personal perspective, how this metric can be a rule as well as just a guide in some cases.
To understand what’s MCC, first we need to understand what’s the unit of measurement MCC uses.
Introduction
The unit of measurement is a function/method/procedure that is within the code project, not by class or lines of codes.
So what does it measure?
Well, it measures all the condition points. Unlike the formula that is given, I’ll use a very very simple calculation method. Take a look at the example below: (Each complexity point is highlighted in bold)
int calculationAlgorithm(int a, int b, int c)
{
int result = 0;
if(a > 2 || b < 0)
result = 5;
switch(c)
{
case 0:
result*=3;
break;
case 1:
result*=2;
break;
case 2:
result*=10;
break;
}
return result;
}
MCC therefore is 1 + 5 = 6 ; MCC always start with 1.
So, what’s the big deal? Well, the higher the MCC, the harder it is to maintain the code. The standard limit for applications is usually either 20 and 30. The limit for critical applications (such as autopilot or something) is usually set at 10.
Therefore, MCC index of 6 is really manageable, as you can see and manage at a glance. Even if the cases have many more lines (say 25 lines per case) it’s still relatively manageable.
Codes with High MCC
To put this in perspective, if you have a function with 20 cases and each case has 25 lines, then you’re probably looking at 500+ lines of codes at once. Maintenance of this function will be a nightmare!
In this case, you MCC will be 21 (since it’s function 1 + 20 cases).
Unless there a good reason to have so many conditions, it may be good time to refactor some of your codes into proper function calls where they belong. I know function calls are stack-expensive and has a performance penalty but they’ll help you in the long run if properly used.
Codes with Low MCC
MCC is not a 1 way ticket up. That means to say that a high MCC measurement means that the code is hard to maintain and that a low MCC measurement means that it’s easy to maintain. That’s not necessarily true.
While it’s good to have relatively low average MCC, it’s also a sign that the codes may be too fragmented.
What do I mean by that? Simply put, functions calling other functions for the sake of code segregation without consideration for code clarity and readability.
Let’s take a look at the same example earlier, modified for our discussion:
int calculationAlgorithm(int a, int b, int c)
{
int result = 0;
result = getRange(a, b);
result = modulateResult(result, c);
return result;
}
int getRange(int a, int b)
{
if(a > 2 || b < 0)
return 5;
return 0;
}
int modulateResult(int result, int c)
{
switch(c)
{
case 0:
result*=3;
break;
case 1:
result*=2;
break;
case 2:
result*=10;
break;
}
return result;
}
This would be a classical example of a better than average refactoring. It’s still fairly readable and doesn’t really break the developer’s train of thought.
Let’s take a look at another example:
int calculationAlgorithm(int a, int b, int c)
{
int result = 0;
result = getRange(a, b);
result = modulateResult(result, c);
return result;
}
int getRange(int a, int b)
{
if(checkConditionA(a) || checkConditionB(b) )
return 5;
return 0;
}
int modulateResult(int result, int c)
{
switch(c)
{
case 0:
result = resultCalMul(result, 3);
break;
case 1:
result = resultCalMul(result, 2);
break;
case 2:
result = resultCalMul(result, 10);
break;
}
return result;
}
Looks the same right? Well, no. If you notice, there’re many more inner function calls to resolve the ultimate results. This may look neater and somewhat cleaner but it doesn’t necessarily make the code less complex.
Look at it, it’s still has roughly the same MCC as well.
However, if you look closer and step through the codes, you’ll find that it’s irritating to go through a bunch of functions and get lost because they’re so fragmented!
Of course, those with greater capacity will be able to remember the flow and paths. However, just how many have that capacity?
Conclusion
In conclusion, there’s no silver bullet or free lunch. There’s no one single way to resolve all problems. MCC is but an indication in most cases but can be used as a rule in terms of coding standards.
MCC is not a single-way indicator. A program with very low MCC average tends to be very fragmented and hard to read. On the other hand, a program with functions having very high MCC index tend to be extremely hard to maintain, especially if it’s a logic core component.
In my next article, I’ll talk about silver bullets and free lunches.
Regards,
Jeremy
- Permalink
- Admin
- 3 Nov 2009 5:51 PM
- Comments (0)