Similar to the financial debt crisis facing many nations, the software industry is facing its own debt crisis caused by the continual development of software without the correct quality-control processes in place. Even industries that were traditionally mechanical are faced with the challenges of developing software like never before, as they strive to stay ahead of the competition. Driven by disparate industries such as automobiles, where 90% of innovation now comes from software-driven features, or the Internet of Things (IoT), which is adding intelligence to many industrial and everyday devices, the volume of software in the world is growing exponentially. Software quality, though, is not keeping pace.
According to Gartner Research, global IT debt could potentially reach US$1 trillion this year.
Technical debt is a metaphor for latent defects introduced during system architecture, system design, or system development. Coined by Ward Cunningham in 1992, technical debt refers to the accumulated liability created when organizations take design and test shortcuts to meet short-term goals. If the “debt” is not paid back, it will eventually make the software impossible to maintain. The higher the technical debt, the lower the ability to innovate and be competitive in the market.
(Related: How to avoid technical debt)
Much of technical debt is caused by business pressure to release new products. The four biggest causes are bad architecture choices, overly complex code, a lack of code documentation, and inadequate testing. As technical debt increases, developers spend a majority of their time fixing bugs and struggling with fragile code, rather than building new features.
As our lives become dependent on embedded software, the demand for “debt-free” predictable behavior is paramount, especially if the safety of users or the environment is at risk. In safety-critical industries such as aviation, rail and automobiles, development standards and regulations have been developed to ensure that applications are completely tested prior to deployment.
Compliance with regulations results in higher-quality software, removing the technical debt associated with “inadequate testing.” However, that does little to address the other three causes of technical debt.
Paying down technical debt
If worldwide technical debt is a trillion-dollar problem, there is no quick solution. Software engineers and manufacturers must design new development processes that ensure that new debt is not added and existing debt is paid down. In order to do so, steps include:
- Understanding the problem by capturing metrics such as code complexity, comment density, and API complexity
- Sharing the results by publishing the data to all stakeholders
- Planning for improvement by focusing effort on the outliers such as splitting functions with the highest complexity
To continue developing the volume of software that modern products require, companies must develop scalable development processes that lead to robust and maintainable codebases relatively free of technical debt. While modern manufacturing methods accept that a first release will never be bug free, and market pressures mean that there will always be some amount of technical debt, keeping technical debt at manageable levels must be the goal of every development group.
Establishing the level of technical debt can be achieved by gathering metrics to provide insight into the scale of the problem. Companies must establish a set of metrics and thresholds for what “debt free” means and an automated way to capture and report these metrics. This will establish a baseline. Since software components tend to be developed over time, in different locations and by different teams, it is likely that the baseline metrics will vary across a codebase.
While this sort of static analysis can provide some of the debt metrics, it does not measure testing completeness, nor does it help determine correctness. Measuring testing completeness is a key component of understanding the scale of technical debt and is most easily measured with code coverage analysis, which records the percentage of executable statements and branch outcomes that have been executed by a test case.
The next step is tackling the biggest source of technical debt: inadequate testing. The first step is to decide what adequate testing means to the organization in terms of correctness and robustness.
A common testing platform makes it easy to implement correctness and robustness tests, but what are we to do with the millions of inadequately tested applications? These applications contain billions of dollars of technical debt, and no one is going to go back and hand-generate tests for these applications.
Automatic test-case generation can improve testing completeness of legacy applications and can be generated from the interface and the logic of an application. While automatic tests do not prove correctness, they are a critical resource to easily finding obsolete, unused or duplicate code, which have built up over years of adding to the software.
The key to reducing technical debt in legacy applications is to refactor components over time. You need to pull together a “baseline” set of tests to capture existing behavior, and allow refactoring to take place with confidence that application behavior has not regressed.
Change-based testing can be used to show what effect the code changes have on the integrity of the whole system. Having automated workflows that identify which subset of tests are required to be run after a “one-line” change replaces the requirement to run a complete set of tests that could take days.
The final piece is reporting: You must be able to present the condition of the codebase’s release readiness, provide clear indications of gaps in testing, show trends over time, and allow managers to predict the resources required to reduce the debt.
Just like monetary debt, ignoring technical debt is dangerous, and debt reduction can only happen with a realistic plan that stops new additions to debt and incrementally reduces debt over time.