Types of Technical Debt
Naïve/Reckless/Unintentional Technical Debt aka Mess
Naïve Debt, Reckless Debt and Unintentional debt are different names for a form of technical debt that accrues due to irresponsible behavior or immature practices on the part of the people involved. In our experience it is very rare to find this kind of technical debt stemming from conscious irresponsible development behavior. However, it is very common to find this kind of debt originating from developers not trained in current and robust development techniques, when little architectural planning has taken place or the toolchain the development team uses is immature.
Myopia is another type of technical debt that can take root during initial development of a major new digital product. When developers or product owners do not look far enough into the future to realize the type of versatility that will be needed for a feature the team can often author short-sighted code that will need to be re-visited later to provide the needed flexibility. An example is choosing to hard code the options in a select list when the end user may want to be able to add options to it in a dynamic fashion.
Unavoidable Technical Debt
A form of technical debt that is usually unpredictable and unpreventable and accrues through no fault of the team building the product. An example of the “unavoidable” flavor of this kind of technical debt would be the need to use of ASP.NET forms just on the brink of Microsoft’s release of the MVC framework – the developer may have had idea a new standard in .NET client-side development was coming but within a small number of years the development community will have moved away from ASP.NET and thus the resulting application laden with Unavoidable Technical Debt.
Strategic Technical Debt
A form of technical debt that is used as a tool to help organizations better quantify and leverage the economics of important, often time-sensitive, decisions. Sometimes taking on technical debt for strategic reasons is a sensible business choice. This is largely a result of conscious decisions around needed speed-to-market or plans to remedy technical debt after meeting a market-imposed (often regulatory in nature) deadline.
From the Trenches: Don’t Forget the Tests
Unit, integration and functional tests are all very important to develop in tandem with the actual software that drives your released product. Some developers are versed in Test Driven Development (TDD) and follow it in day-to-day coding. However, when things get rushed and project deadlines are constrained the development of tests is often the first thing that is skipped “to save time”. Having good test coverage, running tests on every code commit and stopping development until the built up set of tests pass is a key practice to keep technical debt from mounting in your software.
Causes of Technical Debt
There are many root causes for the accumulation of technical debt. While it may appear that technical debt is most often a result of developer carelessness or ignorance, the fact of the matter is that the situation is much more nuanced. Holding such a viewpoint would be like blaming the automotive assembly line worker for your car’s oil changes and brake pad replacement: certainly a car could be built that would not require that maintenance but the cost would be so high no one would buy such a car.
Business pressures, where the business considers getting something released sooner before all of the necessary changes are complete, builds up technical debt comprised of those rushed and/or uncompleted changes.
Short Term Thinking
Lack of process or understanding, where businesses are blind to the concept of technical debt, and make decisions without considering the implications.
Tight Code Coupling
Lack of building loosely coupled components, where functions are not modular, the software is not flexible enough to adapt to changes in business needs.
Isolated Work Efforts
Lack of collaboration, where knowledge isn’t shared around the organization and business efficiency suffers, or junior developers are not properly mentored.
Splintered Code Branches
Parallel development at the same time on two or more branches can cause the buildup of technical debt because of the work that will eventually be required to merge the changes into a single source base. The more changes that are done in isolation, the more debt that is piled up.
Delayed refactoring – As the requirements for a project evolve, it may become clear that parts of the code have become unwieldy and must be refactored to support future requirements. The longer that refactoring is delayed, the more debt that piles up that must be paid at the time of refactoring.
Inexperienced Development Staff
Lack of knowledge, when the developer simply doesn’t know how to write elegant code. A strong culture of software craftsmanship across the development team will keep technical debt down.
Uncoordinated Outsourced Teams
Lack of ownership, when outsourced software efforts result in in-house engineering being required to refactor or rewrite outsourced code.
Lack of a test suite, which encourages quick and risky Band-Aids to fix bugs. A strong test suite for a software product is both a form of technical documentation and also a strong means to ensure future code changes do not break software functionality.
Tools to Measure Technical Debt
Hopefully at this point we have made clear some of the common symptoms and causes of technical debt. Next up is some coverage of tools that can be used to quantitatively sniff out and measure the extent of technical debt in your solutions. Make sure when you use such tools to watch incremental technical debt from check-in to check-in more than hanging your hate on an absolute assessed level of technical debt. The computation for the specific cost to address technical debt is very debatable but the relative changes from one state of the code base to another later version of the code base is a rigorous assessment of the degradation or improvement in the condition of the code.
Software Quality Assessment based on Lifecycle Expectations (SQALE) is an analytical method to assess the evaluation of a software application source code. It is a generic method, independent of the language and source code analysis tools, that normalizes best practice software development techniques across languages. A SQALE score is comprised of 8 indices that measure key factors such as code reusability and changeability, all contributing to an application’s technical debt.
Figure 1: A sample SonarQube dashboard
Figure 2: A deeper dive into a SonarQube run that exposed significant technical debt
Figure 3: A sample snapshot of a quality gate failed by SonarQube
Static Analysis Tools
There are several other software products that are intended to measure many of the factors contributing to technical debt. Examples include cyclomatic complexity (a measure of the number of possible routes through your software logic), static analysis (automated review of source code to identify lacking best practices) and coupling (the extent to which one software routine depends in turn on another to complete its intent). Software development tools such as Crucible and FxCop are often implemented to keep a lid on these ill effects.
Check out Part III Dealing with Technical Debt.