Different ways of measuring code complexity

Engineering Metrics

Oct 19, 2023

In the vast realm of software engineering, the quality of code is paramount. But how do we define and measure the quality of code? One of the critical aspects is understanding its complexity. Code complexity can significantly impact the maintainability, readability, and efficiency of software. As Robert C. Martin aptly pointed out, “the ratio of time spent reading versus writing is well over 10 to 1.” This emphasizes the importance of writing clear, concise, and maintainable code.

Understanding and measuring code complexity is essential for both the engineering team and stakeholders. It provides insights into potential bottlenecks, areas of optimization, and overall team performance. Let's delve into the different metrics used to gauge code complexity and understand why they matter.

Cyclomatic Complexity

This metric measures the number of linearly independent paths through a program's source code. In simpler terms, it gauges the number of decisions a program makes. A higher cyclomatic complexity indicates that the software might be harder to maintain and understand.

Lines of Source Code (LOC)

LOC is a straightforward metric that counts the number of lines in a program. While it provides a quick overview, relying solely on LOC can be misleading as not all lines of code contribute equally to complexity.

Lines of Executable Code

Unlike LOC, this metric focuses only on the lines of code that can be executed, excluding comments and whitespace. It offers a more accurate representation of the code that impacts the software's functionality.

Coupling/Depth of Inheritance

Coupling refers to the interdependence between software modules. High coupling can make the code harder to modify. Depth of Inheritance, on the other hand, measures the inheritance levels in object-oriented programming. A deeper inheritance can lead to increased complexity.

Maintainability Index

This metric combines various measurements like cyclomatic complexity, lines of code, and comments to produce an index that indicates how maintainable a piece of software is. A higher index suggests better maintainability.

Cognitive Complexity

Introduced by SonarSource, cognitive complexity measures how hard the code is for a human to understand. It considers loops, conditionals, and other factors that can make code harder to read.

Halstead Volume

Proposed by Maurice Howard Halstead, this metric combines the number of operations and operands in the code. It provides insights into the potential effort required to understand and maintain the code.

Code Churn

Refers to the percentage of a developer's recent edits (over some time period) compared to the total size of the codebase. High churn rates can indicate areas of high complexity or instability in the code.

Rework Ratio

This metric measures the amount of code that has to be reworked or modified after the initial development. A high rework ratio can indicate issues with code quality or the development process.

Why These Metrics Matter

For an engineering organization, understanding code complexity is crucial. It directly impacts the development process, the efficiency of code review, and the overall productivity of the development teams. By monitoring these metrics, engineering leaders can make informed decision-making processes, ensuring that the software is not only functional but also efficient and maintainable.

Moreover, these metrics play a pivotal role in continuous improvement. By identifying areas of high complexity, teams can focus on refactoring and optimization, ensuring that the software remains robust and scalable.

Harnessing Code Complexity Metrics with BuildPulse

To effectively measure and act upon these metrics, tools like BuildPulse Engineering Metrics are invaluable. BuildPulse not only provides comprehensive reporting on metrics like cycle time, pull requests, and dashboards but also acts as a developer copilot, notifying about stale pull requests and automating the review process. By leveraging such tools, engineering teams can ensure that their code remains of the highest quality, and any potential issues are promptly addressed.

In Conclusion

In the intricate world of software development, understanding code complexity is not a luxury but a necessity. It provides a roadmap for engineering teams, guiding them towards producing efficient, maintainable, and high-quality software. By leveraging the right metrics and tools, engineering teams can ensure that their code remains a paragon of excellence, driving business goals, ensuring customer satisfaction, and meeting the desired timeframe for delivery.

FAQ

What is the difference between a flaky test and a false positive?

A false positive is a test failure in your test suite due to an actual error in the code being executed, or a mismatch in what the test expects from the code.

A flaky test is when you have conflicting test results for the same code. For example, while running tests if you see that a test fails and passes, but the code hasn’t changed, then it’s a flaky test. There’s many causes of flakiness.

What is an example of a flaky test?

An example can be seen in growing test suites - when pull request builds fail for changes you haven’t made. Put differently, when you see a test pass and fail without any code change. These failed tests are flaky tests.

What are common causes of flakiness?

Broken assumptions in test automation and development process can introduce flaky tests - for example, if test data is shared between different tests whether asynchronous, high concurrency, or sequential, the results of one test can affect another. 

Poorly written test code can also be a factor. Improper polling, race conditions, improper event dependency handling, shared test data, or timeout handling for network requests or page loads. Any of these can lead to flaky test failures and test flakiness.

End-to-end tests that rely on internal API uptime can cause test flakiness and test failures.

What's the impact of flaky tests?

Flaky tests can wreck havoc on the development process - from wasted developer time from test retries, to creating bugs and product instability and missed releases, time-consuming flaky tests can grind your development process to a halt.

What is the best way to resolve or fix flaky tests?

Devops, software engineering, and software development teams will often need to compare code changes, logs, and other context across test environments from before the test instability started, and after - adding retries or reruns can also help with debugging. Test detection and test execution tooling can help automate this process as well. 

BuildPulse enables you to find, assess impact metrics, quarantine, and fix flaky tests.

What are some strategies for preventing flaky tests?

Paying attention and prioritizing flaky tests as they come up can be a good way to prevent them from becoming an issue. This is where a testing culture is important - if a flaky test case is spotted by an engineer, it should be logged right away. This, however, takes a certain level of hygiene - BuildPulse can provide monitoring so flaky tests are caught right away.

What type of tests have flaky tests?

Flaky tests can be seen across the testing process - unit tests, integration tests, end-to-end tests, UI tests, acceptance tests.

What if I don't have that many flaky tests?

Flaky tests can be stealthy - often ignored by engineers and test runs are retried, they build up until they can’t be ignored anymore. These automated tests slow down developer productivity, impact functionality, and reduce confidence in test results and test suites. Better to get ahead while it’s easy and invest in test management.

It’s also important to prevent regressions to catch flakiness early while it’s manageable.

What languages and continuous integration providers does BuildPulse work with?

BuildPulse integrates with all continuous integration providers (including GitHub Actions, BitBucket Pipelines, and more), test frameworks, and workflows.

Combat non-determinism, drive test confidence, and provide the best experience you can to your developers!

How long does implementation/integration with BuildPulse take?

Implementation/integration takes 5 minutes!

Ready for Takeoff?

Ready for Takeoff?

Ready for Takeoff?