The following metrics measure the complexity of executable code within procedures. This includes both the internal complexity of a single procedure and the complexity of the data flow in and out of a procedure.
High complexity may result in bad understandability and more errors. Complex procedures also need more time to develop and test. Therefore, excessive complexity should be avoided. Too complex procedures should be simplified by rewriting or splitting into several procedures.
Complexity is often positively correlated to code size. A big program or function is likely to be complex as well. These are not equal, however. A procedure with relatively few lines of code might be far more complex than a long one. We recommend the combined use of LOC and complexity metrics to detect complex code.
Cyclomatic complexity, also known as V(G) or the graph theoretic number, is probably the most widely used complexity metric in software engineering. Defined by Thomas McCabe, it's easy to understand and calculate, and it gives useful results. This metric considers the control logic in a procedure. It's a measure of structural complexity. Low complexity is desirable.
How to calculate cyclomatic complexity?
CC = Number of decisions + 1
The cyclomatic complexity of a procedure equals the number of decisions plus one. What are decisions? Decisions are caused by conditional statements. In Visual Basic they are If..ElseIf..Else, Case, For..Next, Until, While, Catch and When. In order to get CC, one simply counts the conditional statements. A multiway decision, the Select Case block, typical counts as several conditional statements. The decisions counted for each statement or construct are listed below.
|If..Then||+1||An If statement is a single decision.|
|ElseIf..Then||+1||ElseIf adds a new decision.|
|Else||0||Else does not cause a new decision. The decision is at the If.|
|#If..#ElseIf..#Else||0||Conditional compilation adds no run-time decisions.|
|Select Case||0||Select Case initiates the following Case branches, but does not add a decision alone.|
|Case||+1||Each Case branch adds a new decision.|
|Case Else||0||Case Else does not cause a new decision. The decisions were made at the other Cases.|
|For [Each] .. Next||+1||There is a decision at the For statement.|
|Do While|Until||+1||There is a decision at the start of the Do..Loop.|
|Loop While|Until||+1||There is a decision at the end of the Do..Loop.|
|Do..Loop alone||0||There is no decision in an unconditional Do..Loop without While or Until. *|
|While||+1||There is a decision at the start of the While..Wend or While..End While loop.|
|Catch||+1||Each Catch branch adds a new conditional path of execution. Even though a Catch can be either conditional (catches specific exceptions) or unconditional (catches all exceptions), we treat all of them the same way. *|
|Catch..When||+2||The When condition adds a second decision. *|
* Rules marked with an asterisk were added into Project Analyzer v8.0. Previous versions did not take these rules into account.
The minimum limit for cyclomatic complexity is 1. This happens with a procedure having no decisions at all. There is no maximum value since a procedure can have any number of decisions.
If True Then. This kind of an "unconditional condition" counts as a regular decision in cyclomatic complexity, even when there is only one path to take, really.
Cyclomatic complexity comes in a couple of variations as to what exactly counts as a decision. Project Analyzer supports three alternative cyclomatic complexity metrics. CC is the basic version. CC2 and CC3 use slightly different rules.
CC does not count Boolean operators such as And and Or. Boolean operators add internal complexity to the decisions, but they are not counted in CC. CC and CC3 are similar what comes to Booleans, but CC2 is different.
CC2 extends cyclomatic complexity by including Boolean operators in the decision count. Whenever a Boolean operator (And, Or, Xor, Eqv, AndAlso, OrElse) is found within a conditional statement, CC2 increases by one. The statements considered are: If, ElseIf, Select, Case, Until, While, When.
The reasoning behind CC2 is that a Boolean operator increases the internal complexity of a decision. CC2 counts the "real" number of decisions, regardless of whether they appear as a single conditional statement or split into several statements. Instead of using Boolean operators to combine decisions into one (x=1 And y=2), you could as well split the decisions into several sub-conditions (If x=1 Then If y=2 Then). CC2 is immune to this kind of restructuring, which might be well justified to make the code more readable. On the other hand, one can decrease CC simply by combining decisions with Boolean operators, which may not make sense.
Including Boolean operators in cyclomatic complexity was originally suggested by Thomas McCabe. In this sense, both CC and CC2 are "original" cyclomatic complexity measures.
Alternative names: CC2 is also known as ECC extended cyclomatic complexity or strict cyclomatic complexity.
Note: A Case branch can cover several alternative values or ranges, such as Case 1, 2, 5 To 10. These are not counted in CC2, even if they add internal complexity to the decision, quite the same way as the Or operator does in an If statement. A Case with several alternatives (Case 1, 2, 3) is usually simpler than the same decision as an If statement (If x=1 Or x=2 Or x=3 Then). A Case like this will also yield a lower CC2 than the respective If. Splitting the If statement into successive If..ElseIf branches will keep CC2 unmodified, but rewriting it as a single Case will decrease CC2.
CC3 equals the regular CC metric, but each Select Case block is counted as one branch, not as multiple branches. In this variation, a Select Case is treated as if it were a single big decision. This leads to considerably lower complexity values for procedures with large Select Case statements. In many cases, Select Case blocks are simple enough to consider as one decision, which justifies the use of CC3. Cyclomatic complexity and Select Case
Alternative name: CC3 is sometimes called modified cyclomatic complexity.
|Metric||Name||Boolean operators||Select Case||Alternative name|
|CC||Cyclomatic complexity||Not counted||+1 for each Case branch||Regular cyclomatic complexity|
|CC2||Cyclomatic complexity with Booleans||+1 for each Boolean||+1 for each Case branch||Extended or strict cyclomatic complexity|
|CC3||Cyclomatic complexity without Cases||Not counted||+1 for an entire Select Case||Modified cyclomatic complexity|
CC, CC2 or CC3 — which one to use? This is your decision. Pick up the one that suits your use best. CC and CC2 are "original" metrics and probably more widely used than CC3. The numeric values are, in increasing order: CC3 (lowest), CC (middle) and CC2 (highest). In a sense, CC2 is the most pessimistic metric. All of them are heavily correlated, so you can achieve good results with any of them.
A high cyclomatic complexity denotes a complex procedure that's hard to understand, test and maintain. There's a relationship between cyclomatic complexity and the "risk" in a procedure.
|CC||Type of procedure||Risk|
|1-4||A simple procedure||Low|
|5-10||A well structured and stable procedure||Low|
|11-20||A more complex procedure||Moderate|
|21-50||A complex procedure, alarming||High|
|>50||An error-prone, extremely troublesome, untestable procedure||Very high|
The original, usual limit for a maximum acceptable value for cyclomatic complexity is 10. Other values, such as 15 or 20, have also been suggested. Regardless of the exact limit, if cyclomatic complexity exceeds 20, you should consider it alarming. Procedures with a high cyclomatic complexity should be simplified or split into several smaller procedures.
Cyclomatic complexity equals the minimum number of test cases you must execute to cover every possible execution path through your procedure. This is important information for testing. Carefully test procedures with the highest cyclomatic complexity values.
There is a frequently quoted table of "bad fix probability" values by cyclomatic complexity. This is the probability of an error accidentally inserted into a program while trying to fix a previous error.
|CC||Bad fix probability|
As the complexity reaches high values, changes in the program are likely to produce new errors.
The use of multi-branch statements (Select Case) often leads to high cyclomatic complexity values. This is a potential source of confusion. Should a long multiway selection be split into several procedures?
McCabe originally recommended exempting modules consisting of single multiway decision statements from the complexity limit.
Although a procedure consisting of a single multiway decision may require many tests, each test should be easy to construct and execute. Each decision branch can be understood and maintained in isolation, so the procedure is likely to be reliable and maintainable. Therefore, it is reasonable to exempt procedures consisting of a single multiway decision statement from a complexity limit. Note that if the branches of the decision statement contain complexity themselves, the rationale and thus the exemption does not automatically apply. However, if all the branches have very low complexity code in them, it may well apply.
Resolution: For each procedure, either limit cyclomatic complexity to 10 (or another sensible limit) or provide a written explanation of why the limit was exceeded.
Cyclomatic complexity readings
The total cyclomatic complexity for a project or a class is calculated as follows.TCC = Sum(CC) - Count(CC) + 1
TCC equals the number of decisions + 1 in a project or a class. It's similar to CC but for several procedures.
Sum(CC) is simply the total sum of CC of all procedures. Count(CC) equals the number of procedures. It's deducted because we already added +1 in the formula of CC for each procedure.
TCC is immune to modularization, or the lack of modularization. TCC always equals the number of decisions + 1. It is not affected by how many procedures the decisions are distributed in.
TCC can be decreased by reducing the complexity of individual procedures. An alternative is to eliminate duplicated or unused procedures.
Cyclomatic complexity is usually higher in longer procedures. How much decision is there actually, compared to lines of code? This is where you need decision density, which also known as cyclomatic density.DECDENS = Sum(CC) / LLOC
This metric shows the average cyclomatic density in your project. The numerator is sum of CC over all your procedures. The denominator is the logical lines of code metric. DECDENS ignores single-line procedure declarations since cyclomatic complexity isn't defined for them.
DECDENS is relatively constant across projects. A high or low DECDENS does not necessarily means anything is wrong. A low DECDENS might indicate lack of logic, such as in generated code, or code that primarily loads some data instead of performing actions.
The following metrics measure nesting levels. It is assumed that the deeper the nesting, the more complex the code.
Depth of conditional nesting, or nested conditionals, is related to cyclomatic complexity. Whereas cyclomatic complexity deals with the absolute number of branches, nested conditionals counts how deeply nested these branches are.
The recommended maximum for DCOND is 5. More nesting levels make the code difficult to understand and can lead to errors in program logic. If you have too many levels, consider splitting the procedure. You may also find a way to rewrite the logic with a Select Case statement, or an easier-to-read If..Then..ElseIf..Else structure.
Although it might seem to give a lower DCOND, it's not recommended to join multiple conditions into a single, big condition involving lots of And, Or and Not logic.
Depth of looping equals the maximum level of loop nesting in a procedure. Target at a maximum of 2 loops in a procedure.
If you are really interested in structural complexity measures, there is a book that makes a thorough mathematical examination of 98 proposed measures for structural intra-modular complexity. This is for the very advanced reader.
©Aivosto Oy - Project Analyzer Help Contents