Living with Technical Debt

Gilles Grousset
inside|app
Published in
5 min readNov 18, 2019

The quality of an application can be likened to an iceberg: there’s what the user sees, which we can call “perceived quality,” and beneath the waterline lies what isn’t visible: “technical quality.” Often overlooked, whether intentionally or not, this hidden area holds technical debt, which, if left unmanaged, can jeopardize product roadmaps and demotivate even the most dedicated developers.

The Analogy of Technical Debt

The term “technical debt” was coined by Ward Cunningham in 1992. Borrowed from the finance context, it is used to express issues related to the design and quality of software. The level of technical debt in software directly impacts velocity and, consequently, the cost of development.

Poor design and quality can limit the code’s maintainability and introduce numerous bugs and regressions with each update. This results in increased development time (due to code complexity and the need for significant changes, often lacking automated testing). Over time, software with unmanaged technical debt will inevitably affect developers’ velocity and become more expensive.

Increasing development costs over time for a similar functional scope indicate a technical debt problem.

Beyond budget and time-to-market concerns, the morale of development teams should not be underestimated. Working on a technically debt-laden project without any measures to address the issue is neither rewarding nor motivating. Falling behind the standards, spending more time on fixes than on features (often in an increasingly stressful environment) does not encourage developers to perform at their best. It can even drive them to leave, which also incurs additional costs.

A project with a high turnover of development teams is also symptomatic of a technical debt problem.

Two Types of Debt

Technical debt is a scourge. But why is it generated? There are many possible causes of technical debt, but they can be grouped into two categories: unintentional debt and intentional debt.

Unintentional debt results from introducing poor practices into the code due to a lack of standards, insufficient architectural considerations, or team inexperience. It is more challenging to control because it’s more difficult to grasp.

Intentional debt, as the name suggests, is contracted intentionally to address a specific problem, typically related to deadlines or budgets. In this case, debt is generated through shortcuts or “quick wins” to “borrow” development time. However, like any loan, it must be repaid later to avoid sacrificing the long-term technical quality of the project (e.g., planning a dedicated sprint to address it).

Measure to Control

All software projects have technical debt. The goal is not to eliminate it entirely but to control and manage it effectively. Since it’s challenging to improve something that cannot be measured, it’s essential to have some indicators for technical debt as early as possible in a project.

There is no foolproof, universal method to measure technical debt, and each project can create its own indicator as long as it remains consistent throughout the project’s lifecycle. It’s also possible to automate this calculation using SonarQube, the standard tool for static code analysis. SonarQube is based on code defects and the SQALE method to produce indicators on technical debt, such as debt ratio and the estimated time to repay it.

It’s important to note that technical debt is not limited to the source code but also encompasses all technical elements surrounding the project, including infrastructure and development and automation tools.

Effectively Addressing Existing Debt

Addressing existing technical debt in a sizable, older project without previous debt analysis can seem daunting at first. However, not all existing debt must necessarily be resolved. In software, there is code that doesn’t change, known as “legacy code”: old, non-modular, and lacking unit tests but proven stable over the years. Such code should not be addressed, as it would introduce unnecessary risks and costs to the project.

It can be more beneficial to encapsulate this code and create a clean interface to prevent contamination with cleaner code in the future. Like non-evolving code, it’s also essential to assess the longevity of indebted code modules and evaluate the need to address debt in modules that will be abandoned or rewritten in the short or medium term.

Regarding the treatment of existing debt, two practices should be considered based on the project’s goals and the impact of the debt. The first is applying Robert C. “Uncle Bob” Martin’s Boy Scout Rule, which involves cleaning up existing debt in each source file you modify during maintenance or software evolution (ideally, writing missing unit tests). This rule, when followed by the entire team, leads to continuous debt correction.

The second practice is implementing a cleanup campaign, dedicated to reducing debt on one or more high-priority code modules. It’s a thankless and tedious task but becomes necessary when the impact of debt becomes too significant to ensure the project’s smooth progress.

A tool like SonarQube can assist in conducting this type of campaign by classifying anomalies by severity and category and tracking assignments and validation of corrections.

Containing the Debt

Whether at the start of a project or following a debt reduction campaign, it’s crucial to avoid falling into what I call the “clean your room” phenomenon (you’ll understand if you have children). In other words, letting new technical debt accumulate unchecked.

To prevent this, it’s necessary to establish (or ensure the proper application of existing rules) a set of standards and norms, including the following:

  1. Define development standards: Create style guides, best practice guides, README files, and other documentation to establish standards and facilitate onboarding of new developers to the project.
  2. Introduce practices favoring quality code: Implement Test-Driven Development (TDD), pair programming, and code reviews.
  3. Set up automated monitoring: Implement testing on a Continuous Integration platform and perform static code analysis.

Our Recommendations

Technical debt is inherent to software development, and it exists everywhere. It has an impact on the long-term development cost of software. Ignoring it is a mistake.

At inside|app, we recommend:

  1. Defining reliable indicators to measure technical debt and, most importantly, monitoring its evolution over time.
  2. Striking a balance between maintainability and overinvestment by accepting some level of debt (e.g., old code or code expected to evolve or disappear soon).
  3. Establishing development standards and practices to ensure the quality level aligns with project or organization expectations.
  4. Making the management of technical debt a motivational tool and challenge for developers, relying on measurement tools, daily feedback, and gamification mechanisms (e.g., using the Quboo tool).

--

--