Code complexity is a measure of how hard it is to understand, test and change a computer program.
Software can be thought of as a collection of logic in the form of algorithms that is used to make decisions about data. As the software operates, it makes decisions (sometimes billions in a single second) on how to handle inputted data. Complexity represents how difficult it is for developers to understand how these decisions are made, and how hard it is to test and modify the way these decisions are made.
Overly complex code might be too long, deeply nested or overengineered, with fancy design patterns used for simple problems that don’t require them.
Complex code is harder to understand, as developers have difficulty comprehending more code than is necessary for a given use case. It’s harder to test, as high complexity reduces testability because every complex decision path must be evaluated. It’s harder to change, as fixing one minor bug might break other processes in the code.
Complex code is often the result of suboptimal processes, like shifting project requirements, tight deadlines and a lack of code reviews. It can also arise from poor developer habits, like poor planning, copy-pasting code without understanding it and developer ego—sometimes developers write unnecessarily complex code simply to show off.
Developers usually want to reduce code complexity, however simplicity should not come at he expense of correctness or usefulness. Sometimes adding a layer of abstraction might increase complexity but make the software easier to extend or reuse in the future. Code should be only as complex as it needs to be in order to solve the problem it’s intended to solve.
Code complexity is a core part of overall code quality. Here are some of its most common characteristics.
Deep nesting: Multiple levels of if statements or loops
Over-abstraction: Abstract classes, factories, or an excessive depth of inheritance used without a clear purpose
Massive files or functions: Routines that try to do too many things at once instead of splitting tasks into smaller pieces
Tightly-coupled components: Changing one part breaks unrelated parts elsewhere
Obscure naming: Variables and functions with vague or confusing names
Premature optimization: Writing complicated code before performance has been revealed as a problem
Sprawling dependencies: Too many external libraries or complex dependency trees used for basic tasks
To resolve code complexity, it must first be measured.
Cyclomatic complexity is a software metric that measures the structural complexity of a program by counting its independent execution paths.
Cyclomatic complexity was introduced by Thomas McCabe in 1976. It uses control flow graphs to evaluate decision points and control structures like loops and conditional statements.
There are several approaches involved in calculating cyclomatic complexity. The decision point method involves simply counting the number of control flow statements (such as if, while, for, case, or catch statements) in a function and adding 1 point. The graph formula uses M = E - N + 2P, where E is edges, N is nodes, and P is connected components in the control flow graph. The base value method involves counting up individual branches of code.
The resulting score indicates the minimum number of test cases needed to execute every independent path through the code. It provides a minimum number of test cases needed to achieve full branch coverage.
Halstead complexity metrics were introduced by Maurice Halstead in 1977 to measure the implementation complexity of computer programs. Instead of just counting lines of code (LOC), Halstead metrics analyze the vocabulary of a program by measuring its “Halstead volume,” derived by quantifying distinct operators (tokens that perform actions) and operands (tokens that represent data).
Cognitive complexity measures how difficult a piece of code is for a human to read and understand, with the goal of reducing the overall cognitive load placed on developers. In 2016 Sonar, a software company then called SonarSource, developed the cognitive complexity metric as a modern alternative to older mathematical measurements. It focuses on the psychological effort required to comprehend code rather than just counting mathematical decision paths.
Cognitive complexity penalizes nesting (the practice of placing one programming structure inside another) because it’s harder for developers to understand. It ignores shorthands—readable language shortcuts that group multiple statements together—because these are easier for humans to read. Cognitive complexity increases when code breaks the normal linear flow of execution through constructs such as loops, conditionals, exception handling and recursion.
Lines of code is simply a count of the rows of text in piece of code. Large size does not automatically mean code is overly complex, but more lines of code often yields a higher probability of bugs and errors, statistically speaking.
The maintainability index was originally developed by Paul Oman and Jack Hagemeister in 1992. It’s a composite software metric that quantifies how easy a piece of code is to understand, modify and maintain. It combines both Halstead and cyclomatic complexity along with lines of code.
Code refactoring alters the internal structure of code without modifying its external behavior or impacting its functions. It’s like tidying a room every day to make a more comprehensive end-of-week room cleaning easier and faster.
Here are some common examples of code refactoring:
Breaking large functions into smaller ones
Reducing cyclomatic/cognitive complexity
Eliminating duplicate code
Renaming variables, methods and classes for clarity
Simplifying conditional logic
Improving class design
Reducing coupling and improving modularity
Removing dead or unreachable code
Applying appropriate design patterns
Reinforcing adherence to coding standards
General cleanup to improve readability and maintainability
AI coding assistants are effective at reducing complexity because they easily identify code smells, assist with debugging, suggest simplifications and perform repetitive refactoring that human engineers often find tedious, all while preserving the code’s underlying behavior.
A simple prompt like “Analyze this function for cognitive complexity and suggest improvements” is a good start. AI can also explain the code to help human engineers verify understanding before changes are made. Refactoring can be done incrementally in small, reviewable steps.
Another approach might be to run a static code analysis tool like SonarQube within a CI/CD pipeline to identify complex source code. Then, ask the AI to review the flagged code and put together a refactoring plan. Then, apply the changes and re-run the analysis to confirm a decrease in complexity.
IBM Bob is an agentic development platform that identifies bugs and areas for improving software quality. It then suggests targeted fixes that align with a team’s established coding conventions, helping simplify and accelerate code refactoring. Its “Review” mode goes beyond simple linting tools like ESLint to not just statically analyze code for potential errors, but to infer developer intent, flagging issues early and explaining its reasoning.
For those dealing with legacy code, IBM Bob Premium Package for Z blends generative AI and automation to help developers clean up technical debt.
Accelerate software delivery with IBM Bob™, your AI partner for secure, intent-aware development.
Develop, deploy and manage AI applications faster with enterprise-ready tools.
Reimagine legacy systems with intelligent AI modernization.