You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When we say that someone's code quality is bad, in most of the time, what we really mean is that the code is hard to understand. Many developers prefer to start a new project from scratch rather than add features to a legacy project, because the mental cost of understanding the code is much higher than creating something new. As the features of the project increases, it inevitably becomes more difficult to understand. Therefore, it's important to have a way to measure and control the code's complexity and understandability.
In this article, I will introduce two mainstream methods for measuring code complexity - cyclomatic complexity and cognitive complexity. While explaining why cyclomatic complexity may not always be sufficient, I will also introduce you cognitive complexity. Using its rules, I will explain which programming behaviors can make code harder to comprehend.
Cyclomatic Complexity
Cyclomatic Complexity was initially formulated as a measurement of the “testability and maintainability” of the control flow of a module.
Cyclomatic Complexity measuring code complexity via those metrics:
Number of decision points: The number of decision points in the code, such as loops, conditionals, and case statements.
Number of independent paths: The number of independent paths through the code. This is calculated using the formula V(G) = E - N + 2, where E is the number of edges in the code's control flow graph, and N is the number of nodes.
Image we have a function with flow graph above, we can calculate the cyclomatic complexity is V(G) = 9(edges) - 8(nodes) + 2 = 3. So when the numbers of decision points increases, while the node numbers is not changing, We will say it is more complex in cyclomatic complexity metric.
Simply put, we can calculate cyclomatic complexity via counting the independent flow paths(decision points) that our abstraction can take while executing.
While Cyclomatic Complexity gives equal weight to both the sumOfPrimes and getWords methods, it is apparent that sumOfPrimes is much more complex and difficult to understand than getWords. This illustrates that measuring understandability based solely on the paths of a program may not be sufficient.
Cognitive Complexity
Cognitive Complexity is a more comprehensive metric than Cyclomatic Complexity, as it measures not only the number of control flow structures, but also how they interact with each other and the mental effort required to understand the code. It assigns a cognitive weight to each control flow construct based on its complexity and interactions with others, enabling a more accurate assessment of code readability and maintainability. This is important because certain code constructs, such as nested loops and conditional statements, can be more difficult for humans to understand and reason about than others.
Basic criteria and methodology
A Cognitive Complexity score is assessed according to three basic rules:
Ignore structures that allow multiple statements to be readably shorthanded into one
Increment (add one) for each break in the linear flow of the code
Increment when flow-breaking structures are nested
Additionally, a complexity score is made up of four different types of increments:
Nesting - assessed for nesting control flow structures inside each other
Structural - assessed on control flow structures that are subject to a nesting
increment, and that increase the nesting count
Fundamental - assessed on statements not subject to a nesting increment
Hybrid - assessed on control flow structures that are not subject to a nesting
increment, but which do increase the nesting count
These rules and the principles behind them are further detailed in the following sections.
Ignore shorthand
A guiding principle in the formulation of Cognitive Complexity has been that it should incent
good coding practices. That is, it should either ignore or discount features that make code
more readable.
Null-coalescing
// bad practicefunctionsomething(a){if(a!=null){// +1returna.map(item=>item+1);}}// good practicefunctionsomething(a){returna?.map(item=>item+1);}
Cognitive Complexity will ignore null-coalescing to incent good coding practices.##
Increment for breaks in the linear flow
Another guiding principle in the formulation of Cognitive Complexity is that structures that
break code’s normal linear flow from top to bottom, left to right require maintainers to work
harder to understand that code.
A try...catch will contribute complexity very similiar to if...else . So it also increment to cognitive complexity.
Switches
A switch and all its cases combined incurs a single structural increment. This is different than cyclomatic complexity, which incurs increment for each case.
But for maintainer’s point of view, a switch with cases is much easier to understand than if...else if chain.
Unlike Cyclomatic Complexity, Cognitive Complexity adds a fundamental increment for each
method in a recursion cycle, whether direct or indirect. Because Recursion contribute very similiar complexity like Loop.
Increment for nested flow-break structures
Nesting flow-break is something that heavily increase code complexity, five if...else nested is much harder to understand than same five linear series of if...else .
The Cognitive Complexity algorithm gives these two methods markedly different scores,
ones that are far more reflective of their relative understandability.
Metrics that are valuable above the method level
With Cyclomatic Complexity, it can be difficult to differentiate between a class with a large number of simple getters and setters and one that contains complex control flow, as both can have the same number of decision points. However, Cognitive Complexity addresses this limitation by not incrementing for method structure, making it easier to compare the metric values of different classes. As a result, it becomes possible to distinguish between classes with simple structures and those that contain complex control flow, enabling better identification of areas of a program that may be difficult to understand and maintain.
Industry Standard
Cyclomatic Complexity
Code Quality
Readability
Maintainability
1-10
Clear and well-structured
High
Low
11-20
Somewhat complex
Medium
Moderate
21-50
Complex
Low
Difficult
51+
Very complex
Poor
Very difficult
Cognitive Complexity
Code Quality
Readability
Maintainability
1-5
Simple and easy to follow
High
Easy
6-10
Somewhat complex
Medium
Moderate
11-20
Complex
Low
Difficult
21+
Very complex
Poor
Very difficult
Setup Complexity Metrics for You Code with ESLint
One effective way to manage complexity metrics for your code is by using ESLint, a popular linting tool that can help detect and report on various code issues, including Cyclomatic and Cognitive Complexity.
Cyclomatic Complexity
To set up ESLint to report on Cyclomatic Complexity, you can use the eslint-plugin-complexity plugin, which provides a configurable rule for enforcing a maximum Cyclomatic Complexity threshold. First, you'll need to install the plugin by running npm install eslint-plugin-complexity. Then, you can add the plugin to your ESLint configuration file and configure the maximum threshold value:
In this example, we've set the maximum threshold to 10. If the Cyclomatic Complexity of a function or method exceeds this threshold, ESLint will report an error.
Cognitive Complexity
To set up ESLint to report on Cognitive Complexity, you can use the eslint-plugin-cognitive-complexity plugin, which provides a configurable rule for enforcing a maximum Cognitive Complexity threshold. First, you'll need to install the plugin by running npm install eslint-plugin-cognitive-complexity. Then, you can add the plugin to your ESLint configuration file and configure the maximum threshold value:
In this example, we've set the maximum threshold to 15. If the Cognitive Complexity of a function or method exceeds this threshold, ESLint will report an error.
By setting up these metrics in ESLint, you can proactively monitor and manage the complexity of your code, making it easier to understand and maintain over time.
Summary
In conclusion, code complexity is a crucial factor that can significantly impact work efficiency and project maintainability. The use of Cyclomatic Complexity and Cognitive Complexity metrics can help measure and manage code complexity, allowing developers to identify potential problem areas and optimize code readability and maintainability.
There are several ways to reduce code complexity, like using design patterns and applying TDD({% post_link Testing-Best-Practice-Tdd %}). Overall, by understanding and managing code complexity, developers can build better, more maintainable software that delivers value and meets user needs.
The text was updated successfully, but these errors were encountered:
Background
When we say that someone's code quality is bad, in most of the time, what we really mean is that the code is hard to understand. Many developers prefer to start a new project from scratch rather than add features to a legacy project, because the mental cost of understanding the code is much higher than creating something new. As the features of the project increases, it inevitably becomes more difficult to understand. Therefore, it's important to have a way to measure and control the code's complexity and understandability.
In this article, I will introduce two mainstream methods for measuring code complexity - cyclomatic complexity and cognitive complexity. While explaining why cyclomatic complexity may not always be sufficient, I will also introduce you cognitive complexity. Using its rules, I will explain which programming behaviors can make code harder to comprehend.
Cyclomatic Complexity
Cyclomatic Complexity was initially formulated as a measurement of the “testability and maintainability” of the control flow of a module.
Cyclomatic Complexity measuring code complexity via those metrics:
Image we have a function with flow graph above, we can calculate the cyclomatic complexity is V(G) = 9(edges) - 8(nodes) + 2 = 3. So when the numbers of decision points increases, while the node numbers is not changing, We will say it is more complex in cyclomatic complexity metric.
Simply put, we can calculate cyclomatic complexity via counting the independent flow paths(decision points) that our abstraction can take while executing.
Code Example
The presence of the conditional statement
if(x)
in this program creates a decision path, resulting in a cyclomatic complexity of 2.An illustration of the problem
Cyclomatic complexity is a useful metric to measure code complexity, but it has its problem. Let’s looks into two examples:
While Cyclomatic Complexity gives equal weight to both the
sumOfPrimes
andgetWords
methods, it is apparent thatsumOfPrimes
is much more complex and difficult to understand thangetWords
. This illustrates that measuring understandability based solely on the paths of a program may not be sufficient.Cognitive Complexity
Cognitive Complexity is a more comprehensive metric than Cyclomatic Complexity, as it measures not only the number of control flow structures, but also how they interact with each other and the mental effort required to understand the code. It assigns a cognitive weight to each control flow construct based on its complexity and interactions with others, enabling a more accurate assessment of code readability and maintainability. This is important because certain code constructs, such as nested loops and conditional statements, can be more difficult for humans to understand and reason about than others.
Basic criteria and methodology
A Cognitive Complexity score is assessed according to three basic rules:
Additionally, a complexity score is made up of four different types of increments:
increment, and that increase the nesting count
increment, but which do increase the nesting count
These rules and the principles behind them are further detailed in the following sections.
Ignore shorthand
A guiding principle in the formulation of Cognitive Complexity has been that it should incent
good coding practices. That is, it should either ignore or discount features that make code
more readable.
Null-coalescing
Cognitive Complexity will ignore null-coalescing to incent good coding practices.##
Increment for breaks in the linear flow
Another guiding principle in the formulation of Cognitive Complexity is that structures that
break code’s normal linear flow from top to bottom, left to right require maintainers to work
harder to understand that code.
Some of them are:
Catches
A
try...catch
will contribute complexity very similiar toif...else
. So it also increment to cognitive complexity.Switches
A
switch
and all its cases combined incurs a single structural increment. This is different than cyclomatic complexity, which incurs increment for each case.But for maintainer’s point of view, a
switch
with cases is much easier to understand thanif...else if
chain.When we using
switch
we only need to compare a single variable to a named set of literal values, making it easier to understand and maintain.Sequences of logical operators
Understanding the first two lines isn’t very difficult. On the other hand, there is a marked difference in the effort to understand the third line.
When mixed operators, boolean expressions become more difficult to understand.
Recursion
Unlike Cyclomatic Complexity, Cognitive Complexity adds a fundamental increment for each
method in a recursion cycle, whether direct or indirect. Because Recursion contribute very similiar complexity like Loop.
Increment for nested flow-break structures
Nesting flow-break is something that heavily increase code complexity, five
if...else
nested is much harder to understand than same five linear series ofif...else
.Intuitively ‘right’ complexity scores
Let’s look back to our first example, where cyclomatic complexity give them the same score.
The Cognitive Complexity algorithm gives these two methods markedly different scores,
ones that are far more reflective of their relative understandability.
Metrics that are valuable above the method level
With Cyclomatic Complexity, it can be difficult to differentiate between a class with a large number of simple getters and setters and one that contains complex control flow, as both can have the same number of decision points. However, Cognitive Complexity addresses this limitation by not incrementing for method structure, making it easier to compare the metric values of different classes. As a result, it becomes possible to distinguish between classes with simple structures and those that contain complex control flow, enabling better identification of areas of a program that may be difficult to understand and maintain.
Industry Standard
Setup Complexity Metrics for You Code with ESLint
One effective way to manage complexity metrics for your code is by using ESLint, a popular linting tool that can help detect and report on various code issues, including Cyclomatic and Cognitive Complexity.
Cyclomatic Complexity
To set up ESLint to report on Cyclomatic Complexity, you can use the
eslint-plugin-complexity
plugin, which provides a configurable rule for enforcing a maximum Cyclomatic Complexity threshold. First, you'll need to install the plugin by runningnpm install eslint-plugin-complexity
. Then, you can add the plugin to your ESLint configuration file and configure the maximum threshold value:In this example, we've set the maximum threshold to 10. If the Cyclomatic Complexity of a function or method exceeds this threshold, ESLint will report an error.
Cognitive Complexity
To set up ESLint to report on Cognitive Complexity, you can use the
eslint-plugin-cognitive-complexity
plugin, which provides a configurable rule for enforcing a maximum Cognitive Complexity threshold. First, you'll need to install the plugin by runningnpm install eslint-plugin-cognitive-complexity
. Then, you can add the plugin to your ESLint configuration file and configure the maximum threshold value:In this example, we've set the maximum threshold to 15. If the Cognitive Complexity of a function or method exceeds this threshold, ESLint will report an error.
By setting up these metrics in ESLint, you can proactively monitor and manage the complexity of your code, making it easier to understand and maintain over time.
Summary
In conclusion, code complexity is a crucial factor that can significantly impact work efficiency and project maintainability. The use of Cyclomatic Complexity and Cognitive Complexity metrics can help measure and manage code complexity, allowing developers to identify potential problem areas and optimize code readability and maintainability.
There are several ways to reduce code complexity, like using design patterns and applying TDD({% post_link Testing-Best-Practice-Tdd %}). Overall, by understanding and managing code complexity, developers can build better, more maintainable software that delivers value and meets user needs.
The text was updated successfully, but these errors were encountered: