Software development is currently one of the fastest-growing fields around. New software startups are entering the industry every day. Unsurprisingly, the competition is fierce. A whopping 70% of startups fail within two years of launch. To get ahead, businesses need to leverage their teams to deliver innovative, reliable, and scalable products quickly. However, it can be hard to support productive and efficient development without the right insights. Tracking the right software intelligence KPIs can help. That’s why we’re taking a deep dive into the extremely popular, but often misunderstood, code quality metric: Code Complexity.
What is code complexity in software development?
Code complexity can be one of the more challenging software intelligence metrics to understand because there are so many ways to define and measure it. Complexity covers several different properties of software applications that affect internal operations making it difficult to pin down a single definition of complexity. Complexity can range from the number of logical paths in a block of code to complex interactions between files in an application.
When tracking complexity, it’s also important to distinguish between code that is complicated (i.e., difficult to understand, and thus, maintain or refactor) and code that is complex (files, modules, and objects are interconnected or dependent on several other files, modules, or objects). Complicated and complex code can both have a severe impact on development efficiency and productivity.
With all that said, it’s important to understand that not all complexity is bad. When it comes to software code, complexity can be either Essential or Accidental.
Essential Complexity vs. Accidental Complexity
Essential complexity refers to unavoidable complexity that crops up because of conscious decisions made in the development process. The intended use of the software as well as customer requirements can often have a strong impact on the complexity of an application. For example, the software behind a self-driving car’s navigation and acceleration system will almost certainly be more complex than the software used to build a mobile racing game app.
Accidental complexity, on the other hand, is unintentional complexity that comes from sloppy coding or poor decision-making in the development process. Sub-standard code leads to ad-hoc fixes, quick workarounds, or other behaviors that go against software best practices. This kind of complexity can often be compounded by tight deadlines, inexperienced developers, changing requirements, and poor communication in teams.
How is code complexity measured?
As applications grow and the number of code contributors increases, it’s natural for the codebase to become more complex. In fact, complexity can be thought of as a necessary part of software development. It’s only when complexity starts to grow out of control that it becomes a threat.
So, how can you tell if your software is becoming overly complex?
Several metrics are currently used to measure complexity in software applications. 3 of the most popular metrics are:
Cyclomatic ComplexityCyclomatic complexity is one of the most widely used complexity metrics. It’s based on the use of control flow graphs to illustrate logical pathways in code. Cyclomatic complexity calculates the number of logical paths that exist in an area of code and uses these values as an estimation of testability. The more linearly independent paths there are in a block of code (e.g., multiple if-elif-else statements or for loops in a block of code with deep nesting), the more complex it becomes.
One of the advantages of cyclomatic complexity is its ease of calculation. It uses a simple algorithm to obtain a reliable measure of complexity.
V(G) = Maximum number of linearly independent paths i.e. the Cyclomatic Complexity
E = Total number of edges
N = Total number of nodes
P = Total number of predicate nodes (conditional nodes)
When cyclomatic complexity is high, the difficulty and cost of testing also increase. The more branches there are, the more time must be spent on testing to achieve complete coverage. While cyclomatic complexity is great for estimating testability and is a useful indicator for error-prone code, it’s not as good at providing insights into maintainability.
Cognitive ComplexityAs the name implies, cognitive complexity refers to the difficulty one might have in understanding the code. As code becomes more cognitively complex, it also tends to become harder for humans to read and keep track of. This increases the likelihood of errors and makes refactoring or modifications challenging.
Like cyclomatic complexity, cognitive complexity takes the linearly independent paths in an area of code into consideration. However, this method calculates complexity by using different values for the paths that the code can branch into. While cyclomatic complexity is focused on the number of branches, cognitive complexity is concerned with how the number of branches, breaks in the flow or “readability”, and the depth of nesting in conditional statements can make understanding code more difficult.
The Halstead complexity is another metric that aims to provide a more comprehensive understanding of computational complexity in applications. It’s based on Maurice Howard Halstead’s measurable qualities of software code.
Halstead complexity is often an aggregate of Halstead metrics like:
- Program Length
- Potential Minimal Volume
- Program Level
- Program Difficulty
- Programming Effort
- Language Level
- Intelligence Content
- Programming Time
This metric is relatively easy to implement and can be done statically, meaning it can be done without running the program. It’s also useful for predicting defect rates and maintenance efforts. Another advantage is that it can be applied to any programming language easily.
Additional Low-level metrics
While these are the 3 most popular methods for measuring complexity in development, others can also be used. At Foreworth, for example, we use a variety of additional low-level metrics in conjunction with cyclomatic complexity, cognitive complexity, and Halstead complexity. By taking all complexity metrics into account, we can provide a clear view of the factors influencing complicated and complex code.
To build a more comprehensive and inclusive view of complexity in source code, we recommend looking at metrics like:
- Rate of duplicated code
- Lack of cohesion between files
- Coupling between objects
- Depth of inheritance between classes
Other important considerations to track include architectural issues like:
- Unstable interfaces
- Unhealthy inheritance
- Modularity violations
- Circular dependencies
Tracking code complexity in your application
If you’re aiming to improve visibility in your software applications, tracking complexity is a great place to start. Even without a well-rounded software intelligence platform, it’s still possible to track code complexity on your own.
To do this effectively, you’ll probably want to start by identifying the areas in your code that are likely sources of complexity. You can do this by checking in with your QA and Developer teams to identify code quality “hotspots” where defects or bugs tend to occur. Areas where bugs are commonly caused by excessive complexity or developer error tend to be the usual suspects.
Once you’ve identified these “hotspots”, you’ll need to spend some time (and developer capacity) on:
- Collecting and cleaning data.
- Applying a healthy combination of complexity methodologies.
- Verifying the results and making them available in a report or dashboard.
The downsides to this, however, are the higher relative cost in time and developer capacity, the added risk of human error, and the challenges that setting up data capture, cleaning, and storage pipelines can introduce.
Why should you be tracking code complexity?
Like most other KPIs used in software development code, complexity should be used to gain a deeper understanding of the application under development. Tracking complexity in your codebase offers several unique benefits including:
Improving development cost estimations
It has a direct relationship to development productivity in terms of how team effort is allocated. Over complexity can often negatively impact efficiency and software performance, requiring code to be refactored. However, the more complex code is, the more difficult and consequently more expensive it can be to resolve or refactor.
Tracking code complexity helps with gauging the growth of complexity in your applications. Working to balance your team’s code output with complexity in the system can help to manage maintenance costs.
Supporting decision-making in development
When code becomes too complex to work with safely, it can cause serious problems for your applications. By measuring complexity in your application’s modules, you can gain a better idea of the approximate cost of maintenance versus the cost of completely rewriting overly complex code.
Understanding the relative costs of maintenance versus rework can help guide decision-making when it comes to allocating mission-critical resources.
Providing insights to improve planning
Without the right insights, it‘s more difficult to strategically plan out your development efforts. Developing new features or adding new functionalities to existing applications need to be considered within the context of existing complexity. This becomes even more difficult without the right insights.
By measuring complexity in your applications in a consistent and ongoing way, you can stay up to date with complexity in your application. The added visibility from tracking code complexity can help improve future planning in development.
Improving visibility and code quality
Consistently measuring code complexity also helps with improving code quality. The fact is that overly complex code is a ticking timebomb waiting to go off. Excessive complexity in the code base has a strong correlation with defects or bugs. These defects can, in turn, lead to critical failures or security vulnerabilities.
Understanding where complexity is most concentrated in your application can help with strategically allocating testing efforts early in development. With early detection, it’s much easier to address defects or vulnerabilities before they become critical issues.
How to use Code Complexity to improve your development efforts
So, what can you do with complexity KPIs once you have them?
The first thing we’d recommend is identifying areas of high complexity in your code base. You should then check them against your current code quality metrics (e.g., defect detection rates, defect distribution, etc.) to determine where to focus your investigations. From there, we recommend:
- Measuring the impact of complexity-based defects on your development ROI. This can be done by estimating the cost of resolving bugs found in the areas of code identified. Use this information to prioritize where to focus your remediation efforts.
- Narrowing your focus to those areas where complexity is highest to get to the root of bugs and defects encountered. This means identifying the specific modules or files where complexity and defects detection rates are both high.
- Estimating the time and cost of resolving complexity issues, either for refactoring or completely reworking the code in overly complex files. Compare the time and cost of repair with the ongoing costs of over-complexity in these areas. Use this to determine how critical it is to repair or rework existing code.
- Identifying development team talent with the necessary experience in this area of the application (or similar experience in development). Allocate the necessary resources to carry out remediation efforts.
- Monitoring progress on areas of code being reworked and their impact on the rest of the application. Conduct adequate testing to ensure that rework on problematic files does not compromise existing functionality in the application.
Code complexity can be complex
While individual code complexity measures can be easy enough to calculate and report, they’re best when used together. Each of the complexity metrics we’ve described can offer valuable insights, but they only offer single perspectives of complexity in your code base. Combining different complexity measures and using them in tandem with code quality KPIs is a much more holistic approach to tracking productivity and efficiency in development.
Although this can be a time-consuming and occasionally costly endeavor, it doesn’t have to be. There are a variety of software intelligence tools on the market that can help. Foreworth, for example, provides a 360º view of your software development efforts, including the complete list of code complexity metrics from top to bottom. Learn more about how we can help maximize the ROI on your development efforts here.
About the authorJuan Pablo González
Working as Foreworth’s Chief Technical Officer, Juan Pablo (JP) manages the company’s technical strategy. With nearly 20 years of experience in software development, he ensures the development process at Foreworth is meeting its keys objectives and technical requirements.More info →