A Guide to Cognitive Complexity in Software

The journey of the software industry is full of challenges and innovations. 

Cognitive complexity is one such aspect of software development. It takes into consideration how readable and understandable is the code for humans. 

Let’s dig in further to explore the concept of cognitive complexity in software. 

What is cognitive complexity?

Cognitive complexity was already a concept in psychology, however, it is now used in the tech industry too. It is a level of difficulty in understanding a given piece of code which could be a function, class, or issue. 

A non-understandable code is a dead code. 

Cognitive complexity is an important implication for code quality and maintainability. The more complexity of the code, the higher the chances of bugs and errors during modifications.  This can lower the developer productivity which further slows down the development process. 

Factors that influence cognitive complexity

Control flow

Nested loops, deeply nested conditionals, and intricate branching logic can result in difficulty in understanding the code. 

Function length

Long functions or methods with multiple responses increase the cognitive load of the developers which makes it harder to understand the code. On the other hand, smaller, focused functions are generally easy to understand. 

Code structure

How the code is organized and structured directly affects how easily a developer can understand and navigate it. A well-structured code can make software easier to debug and maintain. 

Usage of libraries

When external libraries are integrated with complex APIs, it can introduce cognitive complexity, when not used judiciously. 

Documentation

Documentation acts as a bridge between the code and the software development team’s understanding of it. Insufficient or poorly written documentation can result in high cognitive complexity.

Levels of cognitive complexity

Low complexity level

In this scenario, the code is relatively simple and easy to understand. The code adheres to the coding standards, follows best practices, and no unnecessary complexities are included. A few examples are Simple algorithms, straightforward functions, and well-structured classes. 

Moderate complexity level

The code is slightly more complex and may require further efforts to understand and modify it. While it includes some areas of complexity that can be addressed but still manageable. For example, Function with multiple levels of nested loops and moderately complex algorithm. 

High complexity level

At a high complexity level, the code is highly complex and difficult to understand. This makes the code more prone to errors and bugs and difficult to maintain and modify. This further increases the cognitive load of the developers. Complex algorithms with multiple layers of recursion and classes with a high number of interconnected methods are some examples. 

Causes of cognitive complexity

Poor architectural decisions

Too much coupling between modules or poor separations of concerns are some of the wrong architectural decisions that can take place. Inadequate or intricate architectural choices can lead to higher cognitive complexity in software. This can further contribute to technical debt which can result in spending more time fixing issues and directly impact the system’s performance. 

Lack of knowledge and experience

There may be many instances when developers are unfamiliar with technologies or have insufficient understanding of the industry for which software is developed. This can result in high cognitive complexity as there is a lack of knowledge regarding the development process. 

Another instance could be when the software engineering team struggles with making sound architecture decisions or doesn’t follow coding guidelines.

Large functions or classes

Although large pieces of code including classes, functions, or modules aren’t necessarily complex. However, their increase in length may be a cause of high cognitive complexity. 

In other words, more code = higher chances of cognitive complexity. It is because they are more prone to bugs and fixing issues. It can also increase the cognitive load of the developers since they have to comprehend large functions which will be more time-consuming. 

Legacy code

Aging or poorly maintained code can be challenging for the software engineering team to understand, update, or extend. This is because these codebases are usually outdated or aren’t documented properly. Moreover, they may also lack security features and protocols that make them more susceptible to security vulnerabilities and breaches. Outdated code can also pose integrating challenges. 

High essential complexity

Essential complexity is a type of complexity that is intrinsic to the domain the developers are working on. It is an inherent difficulty of a problem software is trying to solve, regardless of how the problem is implemented or represented. This makes the underlying problem harder to grasp as the developers have to resort to heavy abstractions and intricate patterns. Hence, resulting in high cognitive complexity. 

Unclear naming conventions and comments

When the names in the code are deduced from their purpose and role or don’t provide clarity, it hinders the smooth navigation of the code. But that’s not all! Comments that are riddled with abbreviations, jargon, or incomplete also don’t provide clarity and add an unnecessary layer of mental effort for the development team to understand it. 

Different ways to measure cognitive complexity

Pull request size

This metric calculates the average code changes (In lines of code) of a PR. The larger the size, the higher the chances of complex changes. 

Cyclomatic complexity

Cyclomatic complexity measures the number of linearly independent paths through a function or module. Higher cyclomatic complexity indicates the investigation of potentially challenging code sections. 

Review depth

It calculates the average number of comments per PR review. Review depth highlights the quality of the review and how thorough reviews are done and helps in identifying potentially complex sections before they get merged into the codebase. 

Code churn

Code churn doesn’t directly measure cognitive complexity. But, it tracks the number of times a code segment is modified. This suggests potential complexity due to differences in understanding or frequent adaption.

Nesting complexity

This metric measures the depth of nested structures within code including loops and conditionals. The higher the nesting complexity, the harder it is to understand the code. Nesting complexity helps in identifying areas that are needed for simplification and refactoring. 

Halstead complexity measures

It analyzes various aspects of code including operators and operands. This helps in estimating cognitive efforts and offers an overall complexity score. However, this metric doesn’t directly map to human understanding. 

Use static analysis tools

Static analysis tools such as Sonarqube take a unique approach to measuring cognitive complexity compared to many other static analysis tools. It incorporates various factors to provide a real assessment of the difficulty of the code such as control flow complexity, code smells, and human assessment. Based on all these factors, a cognitive complexity score is calculated for each function and class in the codebase. 

How to reduce cognitive complexity?

Refactoring

Apply refactoring techniques such as extracting methods or simplifying complex logic to improve code structure and clarity. 

Follow coding standards and best practices

Adhere to coding principles such as KISS (Keep it short and simple) and DRY (Don’t repeat yourself) to increase the overall quality of code and reduce cognitive complexity. 

Use static analysis tools

As mentioned above, Static analysis tools are a great way to identify potentially complex functions and code smells that contribute to cognitive load. Through cognitive complexity score, developers can get to know the readability and maintainability of their code. 

Encourage clear communication and collaboration

By fostering an open communication culture, teammates can discuss code designs and complexity with each other. Moreover, reviewing and refactoring code together helps in maintaining clarity and consistency. 

Typo’s automated code tool not only enables developers to catch issues related to maintainability, readability, and potential bugs but also can detect code smells. It identifies issues in the code and auto-fixes them before you merge to master. This means less time reviewing and more time for important tasks. It keeps the code error-free, making the whole process faster and smoother.

Key features

  • Supports top 8 languages including C++ and C#
  • Understands the context of the code and fixes issues accurately
  • Optimizes code efficiently
  • Standardizes code and reduces the risk of a security breach
  • Provides automated debugging with detailed explanations

Conclusion

Understanding and addressing cognitive complexity is key to ensuring code quality and developer efficiency. By recognizing its causes and adopting strategies to reduce them, development teams can mitigate cognitive complexity and streamline the development process.