Maintainability

How easy is it to fix a bug? How much time do you think it will take to refactor your code base?

Maintainability is an age-old metric to answer these questions. It's been reformulated and perfected over time and is made up of four component metrics:

  1. cyclomatic complexity (cc)

  2. halstead volume (hv)

  3. lines of code (LoC)

  4. lines of comment (LoCom)

These four metrics are combined in the following formula:

maintainability=1715.2log(hv)0.23c16.2log(LoC)+502.46LoComLoCmaintainability = 171 - 5.2 * log(hv) - 0.23 * c - 16.2 log( LoC) + 50 * \sqrt{2.46 * \frac{LoCom}{ LoC}}

A slightly alternative formula is used so a normalised value range can be achieved:

norm=max(0,maintainability100/171)norm = max(0, maintainability * 100 / 171)

This number tells us something about the maintainability of a code base:

Range
Conclusion

< 10

Grade C - This code is difficult to maintain

< 20

Grade B - Maintaining this code will take effort

<= 100

Grade A - Maintaining this code will be optimal

Component Metrics

To understand how to improve the maintainability of your code base you need to understand what factors in to your grade. Using the magnify button in the quality tools interface you can find out exactly what metrics you can improve!

Lines of code, and lines of comment are self explanatory. The more code you write, the more code you have to maintain, which will take more effort. Similarly, as you write more comments it becomes easier to reacquaint yourself with code you've written a while ago. This makes it easier to maintain larger code bases.

We'll dive into cyclomatic complexity and halstead volume in greater detail:

Cyclomatic Complexity

This is one of the original metrics to assess how difficult it is to understand a function.

This metric is simple, but effective, and measures the amount of branches in a function. Each if, require, try adds some conditional code to your code. A condition that you need to keep in mind while testing, refactoring, etc. The more conditions you have, the more complex your code becomes, and the harder it will be to maintain.

You can often improve cyclomatic complexity by refactoring large functions into smaller ones. You'll have to deal with fewer edge cases when writing tests for these smaller functions. Your unit test get smaller as a result.

Making further changes to this code will become much easier!

The Complexity Index

Don't confuse cyclomatic complexity and the complexity index. The complexity index is a more complex aggregate metric, which gives you a more accurate and complete assessment of the complexity of your code base!

Halstead Volume

Another tried and true metric that gives an indication of "information".

Halstead volume gives an indication of how many interesting things are "happening" in a function. How is best explained with some examples:

// This function has 6 lines of code
// It does the exact same thing three times. Not very interesting!
function simple(uint a) returns (uint) {
    uint b;
    b = a;
    b += 1;
    b += 1;
    b += 1;
    return b;
}

// This function has 6 lines of code
// It's now slightly more interesting, it does the same operation (+=) three times.
// However, it adds a different number now! This has a higher halstead volume!
function less_simple(uint a) returns (uint) {
    uint b;
    b = a;
    b += 1;
    b += 2;
    b += 3;
    return b;
}

// this function has 5 lines of code
// every line is different, we use lots of different variables, 
// and we apply lots of operations.
// this code has a high halstead volume
function complex(uint a, uint b) returns (uint result) {
    require(a > 10, "only accept high numbers");
    assert(b >= 0, "uints are positive");
    result = a;
    result = simple(result);
    result = less_simple(result)   
}

The Halstead Volume is determined based on amount of unique variables and operations, and how often they're used.

You can often improve this metric by splitting up complex functions that "do a lot" into smaller functions.

Last updated