Software Architecture Analysis

This guide provides a detailed list of quality code metrics that will be used to evaluate the quality of the code and the architecture of a solution. These metrics are grouped by categories along with a brief description for each metric as defined by SonarQube:

General

  • Lines of code
    Number of physical lines that contain at least one character which is neither a whitespace or a tabulation or part of a comment.

    In determined languages, as Cobol, generated lines of code and pre-processing instructions (SKIP1, SKIP2, SKIP3, COPY, EJECT, REPLACE) are not counted as lines of code.

  • Classes
    Number of classes (including nested classes, interfaces, enums and annotations).

  • Directories
    Number of directories.

  • Files
    Number of files.

  • Functions
    Number of functions. Depending on the language, a function is either a function or a method or a paragraph. In some languages, Cobol for example, it is the number of paragraphs. In Java, Accessors are considered as methods, but in VB.net, Accessors are not considered as methods.

  • Lines
    Number of physical lines (number of carriage returns).

  • Statements
    Number of statements.

    Statements counter gets incremented by one each time a following keyword is encountered: if, else, while, do, for, switch, break, continue, return, throw, synchronized, catch, finally.

    Statements counter is not incremented by a class, method, field, annotation definition, package declaration and import declaration.

Test Coverage

  • Condition coverage
    On each line of code containing some boolean expressions, the condition coverage simply answers the following question: 'Has each boolean expression been evaluated both to true and false?'. This is the density of possible conditions in flow control structures that have been followed during unit tests execution.

    Condition coverage = (CT + CF) / (2*B)
    
    where
    
    CT = conditions that have been evaluated to 'true' at least once
    CF = conditions that have been evaluated to 'false' at least once
    
    B = total number of conditions
    
  • Line coverage
    On a given line of code, Line coverage simply answers the following question: Has this line of code been executed during the execution of the unit tests?. It is the density of covered lines by unit tests:

    Line coverage = LC / EL
    
    where
    
    LC = covered lines (lines_to_cover - uncovered_lines)
    EL = total number of executable lines (lines_to_cover)
    
  • Coverage
    It is a mix of Line coverage and Condition coverage. Its goal is to provide an even more accurate answer to the following question:

    How much of the source code has been covered by the unit tests?

    Coverage = (CT + CF + LC)/(2*B + EL)
    
    where
    
    CT = conditions that have been evaluated to 'true' at least once
    CF = conditions that have been evaluated to 'false' at least once
    LC = covered lines = lines_to_cover - uncovered_lines
    
    B = total number of conditions
    EL = total number of executable lines (lines_to_cover)
    
  • Unit tests
    Number of unit tests.

  • Unit test success density (%)

    Test success density = (Unit tests - (Unit test errors + Unit test failures)) / Unit tests * 100
    

Duplications

  • Duplicated blocks
    Number of duplicated blocks of lines. For a block of code to be considered as duplicated:

    • Non-Java projects:
      • There should be at least 100 successive and duplicated tokens.
      • Those tokens should be spread at least on:
      • 30 lines of code for COBOL
      • 20 lines of code for ABAP
      • 10 lines of code for other languages
    • Java projects:
      • There should be at least 10 successive and duplicated statements whatever the number of tokens and lines.

    Differences in indentation as well as in string literals are ignored while detecting duplications.

  • Duplicated files
    Number of files involved in duplications.

  • Duplicated lines
    Number of lines involved in duplications.

  • Duplicated lines (%)

    Density of duplication = Duplicated lines / Lines * 100
    

Complexity

  • Complexity
    It is the complexity calculated based on the number of paths through the code. Whenever the control flow of a function splits, the complexity counter gets incremented by one. Each function has a minimum complexity of 1. This calculation varies slightly by language because keywords and functionalities do.

    In case of Java:

    • Keywords incrementing the complexity: if, for, while, case, catch, throw, return (that is not the last statement of a method), &&, ||, ?
    • Notes:
      • else, default, and finally keywords do not increment the complexity.
      • a simple method with a switch statement and a huge block of case statements can have a surprisingly high complexity value (still it has the same value when converting a switch block to an equivalent sequence of if statements).

    Example: the following method has a complexity of 5

    public void process(Car myCar){          // +1
    if(myCar.isNotMine()){               // +1
         return;                         // +1
    }
    car.paint("red");
    car.changeWheel();
    while(car.hasGazol() && car.getDriver().isNotStressed()){   // +2
         car.drive();
    }
    return;
    }
    

    In case of C/C++, the complexity gets incremented by one for: function definitions, while, do while, for, throw statements, return (except if it is the last statement of a function), switch, case, default, && operator, || operator, ? ternary operator, catch, break, continue, goto.

    In case of VB.net, the complexity gets incremented by one for: method or constructor declaration (Sub, Function), AndAlso, Case, Continue, End, Error, Exit, If, Loop, On Error, GoTo, OrElse, Resume, Return (except if it is the last statement of a function), Stop, Throw, Try.

  • Complexity /function
    Average complexity by function.

  • Complexity /file
    Average complexity by file.

  • Complexity /class
    Average complexity by class.