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 is probably the most widely used complexity metric in software engineering. Defined by Thomas McCabe, it's easy to understand, easy to calculate and it gives useful results. It's a measure of the structural complexity of a procedure.
How to calculate cyclomatic complexity?
CC = Number of decisions + 1
Thus, cyclomatic complexity equals the number of decisions plus one. What are decisions? Decisions are caused by conditional statements. In Visual Basic they are If..Then..Else, Select Case, For..Next, Do..Loop, While..Wend/End While, Catch and When. The cyclomatic complexity of a procedure with no decisions equals 1. There is no maximum value since a procedure can have any number of decisions.
Cyclomatic complexity, also known as V(G) or the graph theoretic number, is calculated by simply counting the number of decision statements. A multiway decision, the Select Case statement, is counted as several decisions. This version of the metric does not count Boolean operators such as And and Or, even if they add internal complexity to the decision statements.
| Construct | Effect on CC | Reasoning |
|---|---|---|
| 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. |
| Select Case | Each Case branch adds one decision in CC. | |
| Case Else | 0 | Case Else does not cause a new decision. The decisions were made at the other Cases. |
| +1 | There is a decision at the start of the loop. | |
| Do..Loop | +1 | There is a decision at Do While|Until or alternatively at Loop While|Until. |
| Unconditional Do..Loop | 0 | There is no decision in an unconditional Do..Loop without While or Until. * |
| While..Wend | +1 | There is a decision at the While statement. |
| 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. * |
* These rules were added into Project Analyzer v8.0. Previous versions did not take these rules into account.
Cyclomatic complexity comes in a few variations as to what exactly counts as a decision.
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 conditionals considered are: If, ElseIf, Select, Case, Do, Loop, While, When. The reasoning behind CC2 is that a Boolean operator increases the internal complexity of the branch. You could as well split the conditional statement in several sub-conditions while maintaining the complexity level.
Alternative names: CC2 is sometimes called ECC extended cyclomatic complexity or strict cyclomatic complexity.
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. More about 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 is the original version and is probably the most widely used. CC3 provides the lowest values, CC comes next. CC2 is the highest variant, the most pessimistic one, one might say. 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 |
|---|---|
| 1-10 | 5% |
| 20-30 | 20% |
| >50 | 40% |
| approaching 100 | 60% |
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
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 (also called cyclomatic density).
DECDENS = CC / LLOCThis metric shows the average cyclomatic density of the code lines within the procedures of your project. Single-line procedure declarations aren't counted since cyclomatic complexity isn't defined for them. The denominator is the logical lines of code metric.
The total cyclomatic complexity for a project or a class is calculated as follows.
TCC = Sum(CC) - Count(CC) + 1In other words, CC is summed over all procedures. Count(CC) equals the number of procedures. It's deducted because the complexity of each procedure is 1 or more. This way, TCC equals the number of decision statements + 1 regardless of the number of procedures these decisions are distributed in.
The following few 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.
Readings
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