Posted by Michael Picovschi in Blog on May 1, 2012 with 1 Comment
If ever one of the following symptoms occurs then you probably have a Cyclomatic Complexity problem :
In 1976 Thomas J.McCabe defined Cyclomatic Complexity as a measure of a program complexity. As he wrote in his published paper the main goal of this measure is to supply :
A mathematical technique that will provide a quantitative basis for modularization and allow us to identify software modules that will be difficult to test or maintain.
In fact it measures the number of different paths that a function can walk through from its start to its end. The more conditions, exceptions, and flow control operation there are, the more complex the code will be. A function with a single “if” statement will have two different execution paths depending on the conditional result, thus a Cyclomatic Complexity of 2. With two “if” statements sequentially written this number goes up to 4.
As a developer a high cyclomatic complexity implies two things: this code is hard to test and this code is hard to understand.
The programmer has probably written this code under an intellectual fury, or iteratively by adding control structures and exceptions where he figured they were missing. Whatever the reasons are this code will have to be tested and functional. It means that if the complexity of a function is about 10, then at least 10 tests will have to be written only to fulfill the branch coverage, not counting other tests such as testing behavior for different values. It also means that someone else reading this code, or even the developer himself going back to his work will have to figure out when, why and what for those 10 execution paths exist.
Imagine this complexity going even higher and you can be at least 80% sure that a bug or unreachable code is going to appear somewhere in the middle of this spaghetti dish.
In opposition a function with a cyclomatic complexity of 2 or 3 will be a child’s play to test or understand.
It is commonly accepted that a C.C. value of 10 is high, everything under is better. Thus 5 is already quite good for anything else than getters/setters.
As we said for the developer and his team a code filled with if and try structures will be atomically hard to test, to maintain and to read. However these are only the technical and local problems that are directly implied.
Now lets say the team members managed to write a very complex code and to finally test it efficiently by tearing their hair out. They suddenly need to refactor it to adapt for next release. Unfortunately it appears that the full test written before, that took them so much time, is now obsolete. While their colleagues in the other team with the low-complexity code have much less troubles making it up-to-date. Too bad.
Meanwhile the program delivered tested to the client is having hurdles being functional. The high-complexity function was unfortunately not thoroughly tested and the app crashes with a null pointer exception because the test case was not even seen by those who wrote the function. And so the team has to go back to work to fix that. While their colleagues in the other team with the low-complexity code are drinking a coffee and chatting in the rest room. Too bad.
And because the company engaged itself giving a functional program it can’t charge the client for the fix and does it for free. And here we get a glimpse of which obscure paths can a high Cyclomatic Complexity guide us to.
Here is a code abstract coming from the free java project Apache CXF.
The class it comes from is org.apache.cxf.configuration.spring.SpringBeanQNameMap from the CXF API. It’s analyzes can be found here and their follows a sample of the measured metrics.
Lines: 163 | Lines of code: 116 | Methods: 1 | Instructions: 67 | Complexity: 27
What is bad here ? For a hundred of effective lines and a single method we got a cyclomatic complexity of 27 ! We aren’t going to refactor the full code of the method but we will, with an example, try to illustrate what are the good practices to diminish complexity.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Collection<?> ids = null;
PropertyValue pv = def.getPropertyValues().getPropertyValue(idsProperty);
if (pv != null) {
Object value = pv.getValue();
if (!(value instanceof Collection)) {
throw new RuntimeException("The property " + idsProperty + " must be a collection!");
}
if (value instanceof Mergeable) {
if (!((Mergeable)value).isMergeEnabled()) {
ids = (Collection<?>)value;
}
} else {
ids = (Collection<?>)value;
}
} |
The branch complexity of this extract is 5. It may be high but remember the complexity is not dependent of the length of code but on how many paths are possible, plus the throw statement stop is an exit point.
The first idea you might have is : “I should extract a part of this method to make it more simple !”. It does actually work to reduce a method or class complexity but not at all to diminish the overall code complexity. The code complexity will be still the same. It’s just that it will may be have a clearer decomposition.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//Main method
Collection<?> ids = null;
PropertyValue pv = def.getPropertyValues().getPropertyValue(idsProperty);
if (pv != null) {
Object value = pv.getValue();
ids = testValue(value);
}
//Other method
Collection<?> testValue( Object value ){
if (!(value instanceof Collection)) {
throw new RuntimeException("The property " + idsProperty + " must be a collection!");
}
if (value instanceof Mergeable) {
if (!((Mergeable)value).isMergeEnabled()) {
return (Collection<?>)value;
}
} else {
return (Collection<?>)value;
}
return null;
} |
|
1 2 3 4 5 6 7 |
if (A) {
if (!B) {
mySimpleStatement
}
} else {
mySimpleStatement
} |
Wait ? Two times the same statement ? There must be something we can do ? Of course there is ! With a simple use of bool algebra we can manage to have a much more clearer result and reducing branch complexity by 1.
If we write this test formally we get
|
1 |
(A &&!B) || !A = mySimpleStatement |
that is reduced in
|
1 2 3 |
!(A && B) = mySimpleStatement
or
!A || !B = mySimpleStatement |
and so the final source will be
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Collection<?> ids = null;
PropertyValue pv = def.getPropertyValues().getPropertyValue(idsProperty);
if (pv != null) {
Object value = pv.getValue();
if (!(value instanceof Collection)) {
throw new RuntimeException("The property " + idsProperty + " must be a collection!");
}
if ( !( value instanceof Mergeable && ((Mergeable)value).isMergeEnabled() ) ) {
ids = (Collection<?>)value;
}
} |
Finally in the given table you have a glimpse of the different way to deal with C.C., their strengths and weaknesses.
| Solution | Decomposition | Bool Algebra | Redesign | Thorough Testing |
|---|---|---|---|---|
| Pros | Fast & Easy | Fast & Reduces the C.C. | The only mean to reduce C.C. efficiently | No need to redesign the source code |
| Cons | Does not reduce C.C. | Not always applicable | Long & Rewrite tests | Hard to maintain |
Cyclomatic Complexity is the one measure to figure out if your code is easy to test, to maintain and to understand. Many reasons can lead to a high cyclomatic complexity, from the simplest, the algorithm is not optimized, to more vicious ones such as a bad data structure or a wrong decomposition data/business. Anyway reducing cyclomatic complexity not only increases your code quality locally but also avoids future troubles for your team, your company and people you deliver to.
If you want to have better measures’ reports for your application try our free plugin for sonar available on Sourceforge : Scertify™ Refactoring Assessment
http://nemo.sonarsource.org
A Complexity Mesure, Thomas J. McCabe, IEEE TRANSACTIONS ON SOFTWARE ENGINEERING, VOL. SE-2, NO.4, DECEMBER 1976
Comment rate in applications: the higher the better?
Posted by Armel GOURIOU in Metric Meanings on June 29, 2012 with 2 Comments
In various audit and quality tools you can find the metric “Comment rate”. What does it mean? How is it related to the application’s quality? How does it impact your technical debt? I am going to try to give you insights on this metric.
Read more…