Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Quick Cyclomatic Complexity Question

Should’ve known better to ask this question on Twitter. Let it not be said that there are no downsides to always poking fun our tendency to take things too seriously.

Here’s some code:

function void Process( Job job )
{
    for ( var i = 0; i < 5; i++ )
    {
        switch ( job.status )
        {
            case ( JOB_STATUS.NEW ):
                ProcessNewJob( job );
                break;
            case ( Job_STATUS.CANCELLED ):
                CancelJob( job );
                break;
            default:
                UpdateJob( job );
                break;
        }
    }
}

The question is: What is the cyclomatic complexity of this code?

Cyclomatic complexity is a measure of the number of paths through your code. So my first intuition was that the answer is 15. One for each case statement, plus the default, and multiplied by 5 for each time through the loop.

Then it was pointed out to me by someone who has actually used it in practice that the for loop counts as 1 regardless of what’s inside it. This is confirmed here. Which would make the answer 4. This actually does make some sense to some degree because even though we are looping through five times, we aren’t actually adding any new paths through the code. I think. Maybe.

A quick Twitter poll seemed to lean toward 4 as well (after discounting answers that amounted to “it depends”). But I’d like to get some more thoughts on it before calling the matter closed.

Having said that, the most accurate answer was provided by my dear and loving cousin, who I can always count on to put things in perspective: I think the answer is you need to up your medication. That question didn’t even make sense on the metaphysical level.

Kyle the Ontological

This entry was posted in Metrics. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Jim Cooper

    @Philip

    > I asked Neal Ford (here:

    Yes, I know Neal a little. We’ve spoken at the same conferences several times.

    > I then looked at the documentation and found that
    > JavaNCSS uses the following definition:

    Yep, that’s generally the way it’s done. Using the graph nodes and edges method is too hard (because knowing you have the right graph is too hard).

    > So the difference in results between Ndepend and
    > tools in the Java world are down to the fact that
    > the former adds one for the ‘default’ branch,
    > whereas the latter don’t.

    Looks like it. Well found :-)

    SourceMonitor works a little differently and counts the exits from a switch statement rather than the cases, and explicitly adds 1 for a default “even if one is not present”.

    Note though, that the bit you quote doesn’t mention exit points, which can also change the metric if you have multiple return statements, for example.

  • Philip Schwarz

    I asked Neal Ford (here: http://memeagora.blogspot.com/2008/07/finally-treeware.html?showComment=1240521480000#c859366854008983466), the author of The Productive Programmer whether my technique is consistent with his, and he said it is.

    He also said: “If you are ever curious about your accuracy, run JavaNCSS on it. It does an excellent job of calculating CC.”

    So I run JavaNCSS (http://www.kclee.de/clemens/java/javancss/) on you Process method, and it also computed a complexity of 4.

    I then looked at the documentation and found that JavaNCSS uses the following definition:

    ########################################################################################################
    CCN is also know as McCabe Metric. There exists a much hyped theory behind it based on graph theory, but it all comes down to simply counting ‘if’, ‘for’, ‘while’ statements etc. in a method. Whenever the control flow of a method splits, the “CCN counter” gets incremented by one.

    Each method has a minimum value of 1 per default. For each of the following Java keywords/statements this value gets incremented by one:

    * if
    * for
    * while
    * case
    * catch
    * &&
    * ||
    * ?

    Note that else, default, and finally don’t increment the CCN value any further. On the other hand, a simple method with a switch statement and a huge block of case statements can have a surprisingly high CCN value (still it has the same value when converting a switch block to an equivalent sequence of if statements).
    ########################################################################################################

    So that explains why I get 4:

    default minimum: 1
    for statement: +1
    first case branch: +1
    second case branch: +1
    CCN = 4

    So the difference in results between Ndepend and tools in the Java world are down to the fact that the former adds one for the ‘default’ branch, whereas the latter don’t.

    Cheers,

    Philip

  • Jim Cooper

    @Philip

    > I am using the Eclipse Metrics 1.3.6 plugin

    Then I think it’s wrong. Both NDepend and SourceMonitor give 5.

    > I am using the definition of nodes and edges given
    > in chapter 6 of The Productive Programmer

    But that doesn’t give a definition of nodes and edges, just an example.

    I’m not particularly happy with the example either, to be honest. I’m not convinced that is actually the right graph to calculate the CC from (why count the end node but not the start node, for example). Using the nodes and edges method is fraught with error.

    You are better off using the alternative definition (which can be derived from the first one, see the wikipedia entry):

    p – s + 2

    where p is the number of decision points in the program, and s is the number of exit points.

    In this example there are 4 decision points (for, case, case, default) and one exit point

    Hence CC is 4 – 1 + 2 = 5

    It really, really, really, really truly is 5 :-)

    In Neal’s example it would be 2 decision points – 1 exit point + 2 = 3

  • Philip Schwarz

    @Steve

    >> Try measuring it with an analysis tool. The answer I get is always 5 :-)

    I am using the Eclipse Metrics 1.3.6 plugin, and it computes a cyclomatic complexity of 4.

    >>I think for starters that you have too many nodes.

    I am using the definition of nodes and edges given in chapter 6 of The Productive Programmer

  • Jim Cooper

    @Philip

    I think the graph should look like this:

    Start
    |___for
    | |___ switch
    | | |___ case __
    | | |___ case __|
    | | |___ def __|
    | | |
    End _|________________|

    Which gives 10 – 7 + 2 = 5

    But this is a really hard way to work out CC, and normally you would do it like NDepend:

    http://www.ndepend.com/Metrics.aspx#CC

  • Jim Cooper

    @Philip

    Try measuring it with an analysis tool. The answer I get is always 5 :-)

    I think for starters that you have too many nodes.

  • Philip Schwarz

    The cyclomatic complexity of the method is 4.

    The method has 9 nodes and 11 edges. In the following diagram, I have drawn nodes as red boxes, and edges as blue lines: http://tinyurl.com/cctu3o

    The formula for cyclomatic complexity (CC) is

    CC = E – N + 2.

    So the cyclomatic complexity of the method is

    Cc = 11 – 9 + 2 = 4

  • Jim Cooper

    @Patrick
    “what really does matters: consistency in measuring. ”

    CC is such an old measure now (at 33 it’s older than many of my coworkers!) that whenever I’ve compared different tools they do actually give the same answer.

    “NDepend has 2 ways of mleasuring CC, one from the source code and one from the IL code”

    Very clever, but not particularly useful, IMO. I don’t read the IL, I read the source code. We are not generally very interested in how complex the compiler finds the code. It’s only human readers that care about complexity

  • http://www.NDepend.com Patrick Smacchia

    I am surprised that nobody points to what really does matters: consistency in measuring.

    No matter the tool you use they will give you an answer, let’s say between 4 and 8. What really matters is that you want to avoid high CC with a threshold of let’s say around 10. As long as you know how your static analyzer tool behaves you can use properly its result and infer your own threshold.

    Btw NDepend has 2 ways of mleasuring CC, one from the source code and one from the IL code, definitions here:

    http://www.ndepend.com/Metrics.aspx#CC
    http://www.ndepend.com/Metrics.aspx#ILCC

  • sridhar

    For a real definition and graph-theory origins of the Cyclomatic Complexity metric, go here – http://hissa.nist.gov/HHRFdata/Artifacts/ITLdoc/235/chapter2.htm

  • Jim Cooper

    @Jon
    “If you are going to count each iteration of the loop”

    That isn’t possible in the general case. What’s the count in this loop?

    for ( var i = 0; i < 5; i++ )
    {
    i = 0;
    }

    and then there are break and continue and return statements, and that’s just for loops. While loops are even worse.

    I also don’t think how many times you go through a loop is a particularly useful measure :-)

  • Jon Cahill

    If you are going to count each iteration of the loop then wouldn’t you need to count each permutation to to correctly get each possible path?

    So you would have 3^5 permutations through the loop and 1 for no loop? ie 244?

  • Jim Cooper

    @Chuck

    “Before the function is called a lookup has to occur correct?”

    It’s not relevant to CC calculations though. You’re only counting paths through this one method. You don’t actually care which other methods are called (or even whether there are method calls).

    If you think it is a relevant complexity measure you could define your own :-) but it isn’t part of Cyclomatic Complexity

  • Chuck

    @Jim Cooper

    “CC doesn’t count the code paths through functions called from the function you’re doing the counting in.”

    Before the function is called a lookup has to occur correct? I don’t see why dynamic dispatch would be excluded.

  • Jim Cooper

    @James

    Sorry James, but you’re wrong. CC does not count the number of times through a loop (how would you count the number of times through a while loop?).

    It counts decision points.

    The answer is 5 – this is the actual answer (measured it with a tool like SourceMonitor if you don’t believe me)

    All functions have a minimum CC of 1. In this case the execution path for that minimal path is (kind of weirdly in this case), not going through the for loop. The reason this is counted is that no analysis is done as to whether the for loop really gets executed or not. You can imagine for loops that wouldn’t, and the cases aren’t differentiated.

    Then you add 1 for the decision point that results in going through the for loop.

    Then add 1 for each of the switch cases.

    That’s 5.

    Seems like it should be 4 (and I wouldn’t argue about it too hard, myself), but it is most definitely NOT 15

  • http://www.honestillusion.com James Curran

    I’m not an expert on Cyclomatic complexity, but I’d go with your original guess of 15. I think that the “rule” for()s count as 1 is based on an assumption that’s not true here. consider a simple for() loop:
    for(int x = 0; x < 3; +=x)
    a[x] = x;

    if we unroll that, we get :
    a[0] = 0;
    a[1] = 1;
    a[2] = 2;

    Which in fact, does have a CC of 1.

    However, if we tried unrolling our loop here, we get:
    ProcessNewJob( job );
    UpdateJob( job );
    UpdateJob( job );
    UpdateJob( job );
    CancelJob( job );

    or
    ProcessNewJob( job );
    UpdateJob( job );
    UpdateJob( job );
    UpdateJob( job );
    UpdateJob( job );

    or
    UpdateJob( job );
    UpdateJob( job );
    UpdateJob( job );
    UpdateJob( job );
    CancelJob( job );

    or
    CancelJob( job );
    CancelJob( job );
    CancelJob( job );
    UpdateJob( job );
    ProcessNewJob( job );

    (That last one — or any of the others — might be nonsensical, but we can’t tell that from the information given)

  • Jim Cooper

    The answer is 5 (try it in SourceMonitor, for example).

    You forgot the path that doesn’t do the for loop at all.

    I have no idea why Chuck is talking about dynamic dispatch. CC doesn’t count the code paths through functions called from the function you’re doing the counting in. They’re separate counts.

  • Kyle Baley

    Let’s assume for the sake of argument that I have no clue what dynamic dispatch is, let alone how to implement it. These methods are all part of the same class that this code is in. They exist at compile-time.

  • Chuck

    What about dynamic dispatch? How are ProcessNewJob, CancelJob, and UpdateJob resolved? You can’t tell by looking at this example.