/
Lecture on Technical Debt 2025-02-05

Lecture on Technical Debt 2025-02-05

Presented at a lecture in a course at the University of Gothenburg.

What is Technical Debt

 

Technical Debt (TD) is a term used in software development to describe non-optimal solutions, often as a shortcut to meet a limited time frame, which can lead to increased maintenance in the future. Cunningham (1992) first introduced the concept of TD to describe coding practice where developers employ quick-and-dirty solutions to address pressing problems. Left unattended, these solutions would cause problems in the future. He leveraged the metaphor of financial debt to help financial sector managers understand the consequences of certain software development practices: “Shipping first-time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite… The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt.” (Cunningham, 1992).

TD is a crucial topic in software development, and the importance of managing it effectively has been recognized . As software development continues to evolve and grow, so does the need to address TD and as per today TD has been devided into multiple sub-areas.

Avgeriou et al. (2016) defined technical debt as "a collection of design or implementation constructs that are expedient in the short term, but set up a technical context that can make future changes more costly or impossible. Technical debt presents an actual or contingent liability whose impact is limited to internal system qualities, primarily maintainability and evolvability" (Avgeriou et al., 2016). The three main concepts that are included in this definition are: debt, interest, and principal (Avgeriou et al., 2016), meaning that the debt is the existence of none-optimal solutions in a system that has an additional cost, an interest, that must be repaid because of the debt. The principal is described as the cost to either refactor the system to remove the accumulated debt, or the cost of developing the system without the debt.

Fowler (2009) looked further into the technical debt metaphor and pointed out that it could be divided into four sections: reckless/prudent and deliberate/inadvertent. The different parts of the quadrant is explained as follows. Reckless-deliberate refers to reckless debt is due to messiness, which means that the mess results in a lot of interest that needs to be repaid. Reckless-deliberate debt occurs when a development team is aware of good design practices but chooses not to follow them because they do not think they have the time for writing clean code, although clean code and a good design helps with faster development. Reckless-inadvertent refers to reckless debt that also is inadvertent, meaning that a team is ignorant to good practices. The accumulation of technical debt is then due to them not realizing that they are taking on the debt. Prudent debt is not due to mess but rather choices that are made regarding design flaws. Prudent-inadvertent debt occurs when an experienced development team follow a good design strategy but finds out during the development what kind of design strategy they should have chosen instead. This realization is prudent-inadvertent debt and even the best development teams will accumulate technical debt when working on a project. If the interest produced by the choices that are made are small enough then it might not be something worth repaying, meaning that it is about whether the short-term benefit is worth the repayment needed in the future, a prudent-deliberate debt.

In addition to Fowler explaining technical debt as a quadrant based on factors such as awareness (reckless/prudent) and intention (deliberate/ inadvertent), Kruchten et al. (2012) points out that technical debt also can originate from emerging technologies. What is meant by this is that the TD is not generated internally because of a wrong choice, but rather externally as a result of the change in context as technology evolves (Kruchten et al., 2012), and is something that only can be seen in retrospect.

Sub-categories of Technical Debt 

By today, the main sub-categories of Technical Debt are:

  • Code Debt - bugs and stuff

  • Design Debt - example: not object oriented

  • Architectural Debt - example: scalability issues as the number of users grows

  • Test Debt - example: missing test-cases leads to missed bugs

  • Documentation Debt - missing, inconsistent, outdated, or incomplete documentation

  • Defect Debt - known defects not fixed

  • Infrastructure Debt - example: postponed infrastructure upgrades leads to downtime

  • Requirements Debt - mismatched system functionality and real-world needs

  • People Debt - the wrong people in the wrong place

  • Build Debt - building and running unnecessary code and tests

  • Process Debt - example: what the process was designed to handle may be changed

  • Automation Test Debt - poorly maintained test scripts, outdated test cases, lack of proper test coverage

  • Usability Debt - example: poor interface design and complex navigation

  • Service Debt - crappy web-services

  • Versioning Debt - example: unnecessary code forks

  • Security Debt - solutions that compromises security of systems

Debt

Description

Code Debt

“Shipping first-time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite.” Cunningham (1992)

“Refers to the problems found in the source code (poorly written code that violates best coding practices or coding rules) that can negatively affect the legibility of the code making it more difficult to maintain” (Rios et al., 2018)

Design Debt

“Tough time-to-market constraints and unanticipated integration or evolution issues lead to design tradeoffs that usually cause flaws in the structure of a software system” (Marinescu, 2012)

“Refers to debt that can be discovered by analyzing the source code and identifying violations of the principles of good object-oriented design” (Rios et al., 2018)

Architectural Debt

Architectural Debt “has a significant influence and impact on system success and, left unchecked, it can cause expensive repercussions” (Besker et al., 2016)

“Refers to the problems encountered in product architecture, which can affect architectural requirements.” (Rios et al., 2018)

Test Debt

Test Debt “occurs due to shortcuts (i.e., wrong or non-optimal decisions) related to testing” (Samarthyam et al., 2017)

“Refers to issues found in testing activities that can affect the quality of those activities” (Rios et al., 2018)

Documentation Debt

“Refers to problems encountered in software project documentation looking for missing, inconsistent, outdated, or incomplete documentation.” (Rios et al., 2018)

Defect Debt

“The trade-off between the short-term benefit of postponing bug fixing activities and long-term consequence of delaying those activities is interpreted as defect debt” (Akbarinasaji et al., 2016)

“Refers to known defects […] that the development team agrees should be fixed but, due to competing priorities and limited resources, have to be deferred to a later time” (Rios et al., 2018)

Infrastructure Debt

“Refers to infrastructure issues that, if present in the software organization, can delay or hinder some development activities” (Rios et al., 2018)

“Some examples of this kind of debt are delaying an upgrade or infrastructure fix” (Alves et al., 2014)

Requirements Debt

“Refers to tradeoffs made with respect to what requirements the development team needs to implement or how to implement them” (Rios et al., 2018), in other words, it “refers to the distance between the optimal requirements specification and the actual system implementation” (Li et al., 2015)

People Debt

“Refers to people issues that, if present in the software organization, can delay or hinder some development activities” (Rios et al., 2018)

“An example of this kind of debt is too few people having the necessary expertise due to delayed training and/or hiring” (Ahmad & Gustavsson, 2022).

Build Debt

“Refers to issues that make the build task harder, and unnecessarily time consuming” (Rios et al., 2018), like “building and running unnecessary code and tests” (Morgenthaler et al., 2012).

Process Debt

“Refers to inefficient processes, e.g. what the process was designed to handle may be no longer appropriate” (Rios et al., 2018), “a kind of sub-optimal development activity that may lead to the appearance of technical debt” (Lahti et al., 2022).

Automation Test Debt

“Refers to the work involved in automating tests of previously developed functionality to support continuous integration and faster development cycles” (Rios et al., 2018) and “to the accumulation of issues related to test automation, such as poorly maintained test scripts, outdated test cases, and lack of proper test coverage” (Wiklund et al., 2012).

Usability Debt

“Refers to inappropriate usability decisions that will need to be adjusted later” (Rios et al., 2018).

“Examples of this debt are lack of usability testing, poor interface design, or complex navigation structures that increase cognitive load for users” (Alves et al., 2016).

Service Debt

“Refers to the inappropriate selection and substitution of web services that lead to mismatch of the service features and applications’ requirements. This kind of debt is relevant for systems with service-oriented architectures” (Rios et al., 2018)

Versioning Debt

“Refers to problems in source code versioning, such as unnecessary code forks” (Rios et al., 2018)

Security Debt

Refers to "a particular solution that compromises the security of the system, e.g., introducing a particular breach of security" (Silva et al., 2016) – "a technical debt containing a security risk" (Rindell et al., 2019).

Apart from these generic categories of TD in the generic context of software engineering, TD has been explored in several specific technical contexts of software engineering, with examples summarized below.

Debt

Context

Description

Database Design Debt

Database design

“The immature or suboptimal database design decisions that lag behind the optimal/desirable ones” (Al-Barak & Bahsoon, 2016)

Normalization Debt

Database design

“... tables below the 4th normal form can be subjected to debts as they potentially lag behind the optimal, where debt can be observed on data consistency and performance degradation as the database grows” (Al-Barak & Bahsoon, 2016)

Enterprise Architecture Debt

Enterprise Architecture

“Enterprise Architecture Debt is a metric that depicts the deviation of the currently present state of an enterprise from a hypothetical ideal state.” (Hacks et al., 2019)

Privacy Debt

GDPR

“... the amount of efforts (resources) required for identifying, governing, controlling, communicating and protecting the software system along the software development lifecycle” (Larrucea et al., 2021)

Data Debt

AI

“This TD type refers to deficiencies related to the collection, management, and usage of data, both for training and production” (Bogner et al., 2021)

Model Debt

AI

“This AI-specific debt type regards suboptimal practices in the design, training, and management of AI models” (Bogner et al., 2021)

Configuration Debt

AI

“This debt type describes deficiencies around the configuration mechanisms of AI-based systems” (Bogner et al., 2021)

Ethics Debt

AI

“... comprises deficiencies around ethical aspects of AI-based systems, such as algorithmic fairness, prediction bias, or a lack of transparency and accountability” (Bogner et al., 2021)

Domain Debt

Functional Design

“Domain debt is the mis-representation of the application domain by an actual system” (Störrle & Ciolkowski, 2019)

Real world examples

Examples from my own experiance.

  • Code Debt - a My Pages solution for a large telecom operator was not thread safe and when a customer logged in the data owned by other customers was shown - was stopped after 15 minutes, but was recognized by the press and gave a lot of bad-will for the company.

  • Design Debt - a web-solution where the same integration-code was copied to all pages needing the integration - very costly to update and ended up in a complete re-write of the application.

  • Architectural Debt - was part of a team building a channel integration platform in the mid 2000. The platform was built as a monolith and in the end got impossible to extend since everything was entangled. The replacement of the platform with a microservices based solution started 2010-isch

  • Test Debt - the Code Debt example above relates also to Test Debt - had the solution been tested with simultaniously logged in users the bug would have been found

  • Documentation Debt - have seen this a lot, espeacially in non-documented web-services which makes integration a trail-and-error game

  • Defect Debt - maintained a system that chrashed every night - did not have so big impact so rather than to fix the defect making it chrash we built a script restarting the system every morning

  • Infrastructure Debt - Copied an entire web-application once since no one dared to touch the server it run on (it had not been patched for years) and the application had to be re-written due to changes in integrations

  • Requirements Debt - Really common - have a fresh example where a solution has been re-written 3 times depending on incorrect requirements

  • People Debt - Unfortnetly also quite common. The most dangerous people are the ones that actually belive they have competence in a field, but do not, they can produce a lot of damage

  • Build Debt - The platform described in Architectural Debt required all dependencies to be built on every commit, making the build process very slow

  • Process Debt - Seen a really critic Process Debt that led to that citizens was not treated according to law (finincial support that was not approved) - the debt was found and fixed, luckily before the press got to know about it

  • Automation Test Debt - a very popular e-service solution in open source has only one vendor, since other vendors do not dare to contribute due to no test automation - it gets impossible to know if anything breaks on a commit

  • Usability Debt - Very common, especially in large systems originated from the 1990s, like Siebel

  • Service Debt - Common in older systems - overly complex web-services designed inside-out instead of API first

  • Versioning Debt - Have a recent example of a team that delays updating third-party libraries and dependencies due to tight deadlines. Due to the increased security risks the development is now halted so the debt can be fixed

  • Security Debt - TietoEvry recently got hacked, affecting multiple customers negatively, because they had not changed standard passwords on their servers

Strategies to Avoid and Mitigate Technical Debt

Code Debt

How to Avoid:
Follow clean coding principles (SOLID, DRY, KISS).
Use automated code reviews & linting tools (e.g., SonarQube).
Conduct peer code reviews to catch potential bugs early.

How to Mitigate:
Refactor legacy code incrementally instead of rewriting everything at once.
Prioritize fixing critical code smells that cause maintainability issues.

Design Debt

How to Avoid:
Enforce object-oriented principles (OOP) and design patterns (Factory, Singleton).
Use domain-driven design (DDD) for a scalable architecture.

How to Mitigate:
Refactor high-maintenance code to follow modular design principles.
Apply automated refactoring tools (e.g., IntelliJ’s refactoring features).

Architectural Debt

How to Avoid:
Plan for future scalability by adopting microservices, cloud-based solutions, or containerization.
Use load testing tools (e.g., JMeter, Gatling) to identify architectural bottlenecks.

How to Mitigate:
Introduce load balancers, caching mechanisms, and horizontal scaling solutions.
Gradually migrate monolithic systems to more scalable architectures.

Test Debt

How to Avoid:
Follow test-driven development (TDD).
Automate unit, integration, and regression testing.

How to Mitigate:
Implement a test coverage monitoring tool (e.g., JaCoCo, Coveralls).
Incrementally write missing test cases for critical functionality.

Documentation Debt

How to Avoid:
Maintain living documentation using tools like MkDocs, Swagger, or Confluence.
Require documentation updates as part of code changes (e.g., pull requests must include updated docs).

How to Mitigate:
Conduct documentation audits and set up automated documentation generation.
Use AI-driven documentation tools (e.g., ChatGPT for code explanations).

Defect Debt

How to Avoid:
Implement continuous bug tracking and prioritization (JIRA, Bugzilla).
Adopt zero-bug policies for major releases.

How to Mitigate:
Schedule bug-fixing sprints.
Maintain a technical debt register to track high-priority defects.

Infrastructure Debt

How to Avoid:
Use cloud-based infrastructure that scales automatically.
Maintain infrastructure as code (IaC) with Terraform or Ansible.

How to Mitigate:
Gradually migrate legacy infrastructure in small, controlled phases.
Perform capacity planning & disaster recovery tests.

Requirements Debt

How to Avoid:
Use agile requirement gathering (e.g., user stories & acceptance criteria).
Conduct regular user feedback sessions.

How to Mitigate:
Prioritize fixing critical feature gaps in upcoming releases.
Implement feature toggles to test new functionalities with users before full rollout.

People Debt

How to Avoid:
Ensure balanced skill sets in development teams.
Encourage continuous training & knowledge sharing.

How to Mitigate:
Reallocate underutilized employees to better-fit roles.
Improve onboarding & mentorship programs.

Build Debt

How to Avoid:
Optimize build pipelines (e.g., CI/CD with parallel execution).
Automate dependency management (e.g., Gradle, Maven).

How to Mitigate:
Implement incremental builds to avoid recompiling unchanged code.
Remove unused dependencies & redundant code blocks.

Process Debt

How to Avoid:
Regularly review and adapt processes based on team feedback.
Maintain a lean development process with clear KPIs.

How to Mitigate:
Introduce agile retrospectives to identify inefficiencies.
Implement process automation tools to eliminate bottlenecks.

Automation Test Debt

How to Avoid:
Regularly review and update test automation suites.
Ensure automated tests are stable and reliable.

How to Mitigate:
Implement self-healing test automation frameworks (e.g., Testim, AI-driven testing).
Track test flakiness and remove unreliable tests.

Usability Debt

How to Avoid:
Follow user-centered design principles.
Conduct usability testing before deployment.

How to Mitigate:
Prioritize UX improvements in future iterations.
Implement A/B testing to validate UI changes.

Service Debt

How to Avoid:
Enforce API standards (e.g., REST, GraphQL best practices).
Monitor API performance and reliability.

How to Mitigate:
Refactor low-performance APIs.
Use API gateways and caching mechanisms for efficiency.

Versioning Debt

How to Avoid:
Follow semantic versioning and maintain branching strategies (e.g., GitFlow).
Regularly update dependencies with tools like Dependabot.

How to Mitigate:
Merge stale branches and remove unnecessary forks.
Maintain a versioning roadmap to prevent outdated dependencies.

Security Debt

How to Avoid:
Enforce secure coding practices (e.g., OWASP Top 10).
Use automated security scanning tools (e.g., Snyk, Checkmarx).

How to Mitigate:
Conduct regular security audits.
Implement continuous penetration testing.

Final Takeaways

Preventing TD requires proactive planning, automation, and best practices.
Mitigating TD involves prioritizing fixes, continuous improvement, and strategic refactoring.
Tracking TD in a technical debt register ensures visibility and long-term control.

Related papers

Related content

Utveckling
More like this
Principer för införande av DevOps-team i Sundsvalls kommun
Principer för införande av DevOps-team i Sundsvalls kommun
More like this
Anteckningar - Arbete framåt rörande Lön och pension (2022-01-11)
Anteckningar - Arbete framåt rörande Lön och pension (2022-01-11)
More like this
21-44 Digital innovationsplattform
21-44 Digital innovationsplattform
More like this
21-04 Ärendehantering Lön och Pension
21-04 Ärendehantering Lön och Pension
More like this
Öppen källkod-hantering
Öppen källkod-hantering
More like this