JArchitect Metrics

Metrics Definition



Metrics on Application

NbLinesOfCode

Defined for application, projects, packages, types, and methods.

This metric (known as LOC) can be computed only if source files are present. The LOC for a method is equals to the number of statements found for this method.

Computing the number of lines of code from PDB’s sequence points allows to obtain a logical LOC of code instead of a physical LOC (i.e directly computed from source files). 2 significant advantages of logical LOC over physical LOC are:

  • Coding style doesn’t interfere with logical LOC. For example the LOC won’t change because a method call is spawn on several lines because of a high number of argument.
  • logical LOC is independent from the language. Values obtained from projects written with different languages are comparable and can be summed.
  • Notice that the LOC for a type is the sum of its methods’ LOC, the LOC for a package is the sum of its types’ LOC, the LOC for an project is the sum of its packages’ LOC and the LOC for an application is the sum of its projects LOC. Here are some observations:

    • Abstract methods and enumerations have a LOC equals to 0. Only concrete code that is effectively executed is considered when computing LOC.
    • Packages, types, fields and methods declarations are not considered as line of code because they don’t have corresponding sequence points.

    Recommendations:

    • Methods where NbLinesOfCode is higher than 20 are hard to understand and maintain.
    • Methods where NbBCInstructions is higher than 40 are extremely complex and should be split in smaller methods (except if they are automatically generated by a tool.)

NbLinesOfComments

Defined for application, projects, packages, types, and methods

This metric can be computed only if source files are present. The number of lines of comment is computed as follow:

  • For a method, it is the number of lines of comment that can be found in its body and also javadoc comments before the method declaration. In Java the body of a method begins with a '{' and ends with a '}'.
  • For a type, it is the sum of the number of lines of comment that can be found inside the class, and javadoc comments of this class.
  • For a package, it is the sum of the number of lines of comment that can be found in each of its definition.
  • For a project, it is the sum of the number of lines of comment that can be found in each of its source file.

Notice that this metric is not an additive metric (i.e for example, the number of lines of comment of a package can be greater than the number of lines of comment over all its types).

Recommendations:
This metric is not helpful to asses the quality of source code. We prefer to use the metric PercentageComment.

PercentageComment

Defined for application, projects, packages, types, methods

PercentageComment = 100*NbLinesOfComment / ( NbLinesOfComment + NbLinesOfCode)

Recommendations:
Code where the percentage of comment is lower than 20% should be more commented. However overly commented code (>40%) is not necessarily a blessing as it can be considered as an insult to the intelligence of the reader. Guidelines about code commenting can be found here.

NbBCInstructions

Defined for application, projects, packages, types, and methods

Notice that the number of BC instructions can vary depending if your projects are compiled in debug or in release mode. Indeed compiler's optimizations can modify the number of BC instructions. For example a compiler can add some nop BC instructions in debug mode to handle Edit and Continue and to allow attach an BC instruction to a curly brace. Notice that BC instructions of third-party projects are not taken account.

Recommendations:

  • Methods where NbBCInstructions is higher than 100 are hard to understand and maintain.
  • Methods where NbBCInstructions is higher than 200 are extremely complex and should be split in smaller methods (except if they are automatically generated by a tool.)

NbProjects

Defined for application - Only application projects are taken into account.

NbPackages

Defined for application and projects. The number of packages. The anonymous package counts as one. If a package is defined over N projects, it will count as N. Packages declared in third-party projects are not taken account.

NbTypes

Defined for application, projects, packages. The number of types. A type can be an abstract or a concrete class, a structure, an enumeration or an interface. Types declared in third-party projects are not taken account.

NbMethods

Defined for application, projects, packages, types. The number of methods. A method can be an abstract, virtual or non-virtual method, a method declared in an interface, a constructor, a class constructor, a finalizer, a property/indexer getter or setter, an event adder or remover. Methods declared in third-party projects are not taken account.

Recommendations:
Types where NbMethods > 20 might be hard to understand and maintain but there might be cases where it is relevant to have a high value for NbMethods.

NbFields

Defined for application, projects, packages, types. The number of fields. A field can be a regular field, an enumeration's value or a readonly or a const field. Fields declared in third-party projects are not taken account.

Recommendations:
Types where NbFields is higher 20 might be hard to understand and maintain but there might be cases where it is relevant to have a high value for NbFields.

PercentageCoverage

defined for application, projects, packages, types, and methods. The percentage of code coverage by tests. Code coverage data are imported from coverage files. If you are using the uncoverable attribute feature on a method for example, if all sibling methods are 100% covered, then the parent type will be considered as 100% covered.

Coverage metrics are not available if the metric NbLinesOfCode is not available.

Recommendations:
The closer to 100%, the better.

NbLinesOfCodeCovered

Defined for application, projects, packages, types and methods. The number of lines of code covered by tests.

NbLinesOfCodeNotCovered

Defined for application, projects, packages, types and methods. The number of lines of code not covered by tests.

Go to top


Metrics on Projects

By measuring coupling between types of your application, JArchitect assesses the stability of each project. An project is considered stable if its types are used by a lot of types of third-party projects (i.e stable = painful to modify). If an project contains many abstract types (i.e interfaces and abstract classes) and few concrete types, it is considered as abstract.Thus, JArchitect helps you detect which projects are potentially painful to maintain (i.e concrete and stable) and which projects are potentially useless (i.e abstract and instable.)

Note:
This theory and metrics have been first introduced by the excellent book Agile Software Development: Principles, Patterns, and Practices in C# Robert C. Martin (Prentice Hall PTR, 2006)

Related Link:
Coverage FAQ.

Afferent coupling (Ca)

The number of types outside this project that depend on types within this project. High afferent coupling indicates that the concerned projects have many responsibilities.

Efferent coupling (Ce)

The number of types outside this project used by child types of this project. High efferent coupling indicates that the concerned project is dependent. Notice that types declared in third-party projects are taken into account.

Relational Cohesion (H)

Average number of internal relationships per type. Let R be the number of type relationships that are internal to this project (i.e that do not connect to types outside the project). Let N be the number of types within the project. H = (R + 1)/ N. The extra 1 in the formula prevents H=0 when N=1. The relational cohesion represents the relationship that this project has to all its types.

Recommendations:
As classes inside an project should be strongly related, the cohesion should be high. On the other hand, too high values may indicate over-coupling. A good range for RelationalCohesion is 1.5 to 4.0. Projects where RelationalCohesion < 1.5 or RelationalCohesion > 4.0 might be problematic.

Instability (I)

The ratio of efferent coupling (Ce) to total coupling. I = Ce / (Ce + Ca). This metric is an indicator of the project's resilience to change. The range for this metric is 0 to 1, with I=0 indicating a completely stable project and I=1 indicating a completely instable project.

Abstractness (A)

The ratio of the number of internal abstract types (i.e abstract classes and interfaces) to the number of internal types. The range for this metric is 0 to 1, with A=0 indicating a completely concrete project and A=1 indicating a completely abstract project.

Distance from main sequence (D)

The perpendicular normalized distance of an project from the idealized line A + I = 1 (called main sequence). This metric is an indicator of the project's balance between abstractness and stability. An project squarely on the main sequence is optimally balanced with respect to its abstractness and stability. Ideal projects are either completely abstract and stable (I=0, A=1) or completely concrete and instable (I=1, A=0). The range for this metric is 0 to 1, with D=0 indicating an project that is coincident with the main sequence and D=1 indicating an project that is as far from the main sequence as possible. The picture in the report reveals if an project is in the zone of pain (I and A both close to 0) or in the zone of uselessness (I and A both close to 1).

Recommendations:
Projects where NormDistFromMainSeq is higher than 0.7 might be problematic. However, in the real world it is very hard to avoid such projects. Therefore, you should allow a small percentage of your projects to violate this CQLinq constraint: WARN IF Percentage > 15 IN SELECT PACKAGES WHERE NormDistFromMainSeq > 0.7

Go to top


Metrics on Packages

Afferent coupling at package level (PackageCa)

The Afferent Coupling for a particular package is the number of packages that depends directly on it.

Efferent coupling at package level (PackageCe)

The Efferent Coupling for a particular package is the number of packages it directly depends on. Notice that packages declared in third-party projects are taken into account.

Level

Defined for projects, packages, types, methods. The Level value for a package is defined as follow:

  • Level = 0 : if the package doesn’t use any other package.
  • Level = 1 : if the package only uses directly package defined in tierce projects.
  • Level = 1 + (Max Level over package it uses directly)
  • Level = N/A : if the package is involved in a dependency cycle or uses directly or indirectly a package involved in a dependency cycle.

Level metric definitions for projects, types and methods are inferred from the above definition.
This metric has been first defined by John Lakos in his book Large-Scale C++ Software Design.

Recommendations:
This metric helps objectively classify the projects, packages, types and methods as high level,mid level or low level. There is no particular recommendation for high or small values.
This metric is also useful to discover dependency cycles in your application. For instance if some packages are matched by the following CQLinq query, it means that there is some dependency cycles between the packages of your application: SELECT PACKAGES WHERE !HasLevel AND !IsThirdParty

Go to top


Metrics on Types

Type rank

TypeRank values are computed by applying the Google PageRank algorithm on the graph of types' dependencies. A homothety of center 0.15 is applied to make it so that the average of TypeRank is 1.

Recommendations:
Types with high TypeRank should be more carefully tested because bugs in such types will likely be more catastrophic.

Afferent Coupling at type level (Ca)

The Afferent Coupling for a particular type is the number of types that depends directly on it.

Efferent Coupling at type level (Ce)

The Efferent Coupling for a particular type is the number of types it directly depends on. Notice that types declared in third-party projects are taken into account. Recommendations:
Types where TypeCe > 50 are types that depends on too many other types. They are complex and have more than one responsibility. They are good candidate for refactoring.

Lack of Cohesion Of Methods (LCOM)

The single responsibility principle states that a class should not have more than one reason to change. Such a class is said to be cohesive. A high LCOM value generally pinpoints a poorly cohesive class. There are several LCOM metrics. The LCOM takes its values in the range [0-1]. The LCOM HS (HS stands for Henderson-Sellers) takes its values in the range [0-2]. A LCOM HS value highest than 1 should be considered alarming. Here are algorithms used by JArchitect to compute LCOM metrics:

  • LCOM = 1 – (sum(MF)/M*F)
  • LCOM HS = (M – sum(MF)/F)(M-1)
  • Where:
  • M is the number of methods in class (both static and instance methods are counted, it includes also constructors, properties getters/setters, events add/remove methods).
  • F is the number of instance fields in the class.
  • MF is the number of methods of the class accessing a particular instance field.
  • Sum(MF) is the sum of MF over all instance fields of the class.
The underlying idea behind these formulas can be stated as follow: a class is utterly cohesive if all its methods use all its instance fields, which means that sum(MF)=M*F and then LCOM = 0 and LCOMHS = 0.

Recommendations:
Types where LCOM > 0.8 and NbFields > 10 and NbMethods >10 might be problematic. However, it is very hard to avoid such non-cohesive types. Types where LCOMHS > 1.0 and NbFields > 10 and NbMethods >10 should be avoided. Note that this constraint is stronger (and thus easier to satisfy) than the constraint types where LCOM > 0.8 and NbFields > 10 and NbMethods >10.

Cyclomatic Complexity (CC)

Defined for types and methods. Cyclomatic complexity is a popular procedural software metric equal to the number of decisions that can be taken in a procedure. Concretely, in Java the CC of a method is 1 + {the number of following expressions found in the body of the method}:

if | while | for | foreach | case | default | continue | goto | && | || | catch | ternary operator ?: | ??

Following expressions are not counted for CC computation:

else | do | switch | try | using | throw | finally | return | object creation | method call | field access

The Cyclomatic Complexity metric is defined on methods. Adapted to the OO world, this metric is also defined for classes and structures as the sum of its methods CC. Notice that the CC of an anonymous method is not counted when computing the CC of its outer method.

Recommendations:
Methods where CC is higher than 15 are hard to understand and maintain. Methods where CC is higher than 30 are extremely complex and should be split in smaller methods (except if they are automatically generated by a tool.)

BC Cyclomatic Complexity (BCCC)

The CC metric is language dependent. Thus, JArchitect provides the BCCC which is language independent because it is computed from BC as 1 + {the number of different offsets targeted by a jump/branch BC instruction}. Experience shows that JArchitect CC is a bit more larger than the CC computed in Java. Indeed, a Java 'if' expression yields one BC jump. A Java 'for' loop yields two different offsets targeted by a branch BC instruction while a foreach Java loop yields three.

Recommendations:
Methods where BCCyclomaticComplexity is higher than 20 are hard to understand and maintain. Methods where BCCyclomaticComplexity is higher than 40 are extremely complex and should be split in smaller methods (except if they are automatically generated by a tool).

Size of instance

Defined for instance fields and types. The size of instances of an instance field is defined as the size, in bytes, of instances of its type. The size of instance of a static field is equal to 0.

The size of instances of a class or a structure is defined as the sum of size of instances of its fields plus the size of instances of its base class.

Fields of reference types (class, interface, delegate…) always count for 4 bytes while the footprint of fields of value types (structure, int, byte, double…) might vary.

Size of instances of an enumeration is equal to the size of instances of the underlying numeric primitive type. It is computed from the value__ instance field (all enumerations have such a field when compiled in BC).

Size of instances of generic types might be erroneous because we can’t statically know the footprint of parameter types (except when they have the class constraint).

Recommendations:
Types where SizeOfInst is higher than 64 might degrade performance (depending on the number of instances created at runtime) and might be hard to maintain. However it is not a rule since sometime there is no alternative. Non-static and non-generic types where SizeOfInst is equal to 0 indicate stateless types that might eventually be turned into static classes.

NbInterfacesImplemented

The number of interfaces implemented. This metric is available for interfaces, in this case the value is the number of interface extended, directly or indirectly. For derived class, this metric also count the sum of interfaces implemented by base class(es).

Association Between Class (ABC)

The Association Between Classes metric for a particular class or structure is the number of members of others types it directly uses in the body of its methods.

Number of Children (NOC)

The number of children for a class is the number of sub-classes (whatever their positions in the sub branch of the inheritance tree). The number of children for an interface is the number of types that implement it. In both cases the computation of this metric only count types declared in the application code and thus, doesn't take account of types declared in third-party projects.

Depth of Inheritance Tree (DIT)

The Depth of Inheritance Tree for a class or a structure is its number of base classes (including the System.Object class thus DIT >= 1)

Recommendations:
Types where DepthOfInheritance is higher or equal than 6 might be hard to maintain. However it is not a rule since sometime your classes might inherit from third-party classes which have a high value for depth of inheritance. For example, the average depth of inheritance for third-party classes which derive from System.Windows.Forms.Control is 5.3.

Go to top


Metrics on Methods

Method rank

MethodRank values are computed by applying the Google PageRank algorithm on the graph of methods' dependencies. A homothety of center 0.15 is applied to make it so that the average of MethodRank is 1.

Recommendations:
Methods with high MethodRank should be more carefully tested because bugs in such methods will likely be more catastrophic.

Afferent coupling at method level (MethodCa)

The Afferent Coupling for a particular method is the number of methods that depends directly on it.

Efferent coupling at method level (MethodCe)

The Efferent Coupling for a particular method is the number of methods it directly depends on. Notice that methods declared in third-party projects are taken into account.

BC Nesting Depth

The metric Nesting Depth for a method is the maximum number of encapsulated scopes inside the body of the method. The metric BC Nesting Depth is computed from the BC code. Values computed are very similar to what we would expect by computing them from the Java source code.

When you have a testing condition with N conditions, such as if( i > 9 && i < 12) then it is considered as N scopes because it is possible to decompose such conditions into N atomic conditions.

When a method has a large number of case statements corresponding to a switch, the Java compiler generally produce optimizations while generating the BC. In such case, the BC Nesting Depth corresponding value might be slightly higher to what you would expect.


Recommendations:
Methods where BCNestingDepth is higher than 4 are hard to understand and maintain. Methods where BCNestingDepth is higher than 8 are extremely complex and should be split in smaller methods (except if they are automatically generated by a tool.)

NbParameters

The number of parameters of a method. Ref and Out are also counted. The this reference passed to instance methods in BC is not counted as a parameter.

Recommendations:
Methods where NbParameters is higher than 5 might be painful to call and might degrade performance. You should prefer using additional properties/fields to the declaring type to handle numerous states. Another alternative is to provide a class or structure dedicated to handle arguments passing.

NbVariables

The number of variables declared in the body of a method.

Recommendations:
Methods where NbVariables is higher than 8 are hard to understand and maintain. Methods where NbVariables is higher than 15 are extremely complex and should be split in smaller methods (except if they are automatically generated by a tool.)

NbOverloads

The number of overloads of a method. If a method is not overloaded, its NbOverloads value is equals to 1. This metric is also applicable to constructors.

Recommendations:
Methods where NbOverloads is higher than 6 might be a problem to maintain and provoke higher coupling than necessary.

PercentageBranchCoverage

Defined for methods. Branch coverage is a more accurate measure of coverage than PercentageCoverage because it compensates for method complexity. Since branch coverage is generated from the underlying opcodes, it often does not map cleanly to source code. That means it’s difficult to take branch coverage values and determine how to write tests that will improve coverage.

Branch coverage is only available if your coverage data are imported from Cobertura coverage files and if the metric NbLinesOfCode is available.

Recommendations:

The bottom line is:

  • Use PercentageCoverage as your measure of quality, but
  • Use PercentageBranchCoverage to determine which code statements need more testing.
It is also interesting to observe branch coverage values in conjunction with Code Source Cyclomatic Complexity values.

Go to top


Metrics on fields

Afferent coupling at field level (FieldCa)

The Afferent Coupling for a particular field is the number of methods that directly use it.

Go to top