Types of Technical Debt

ModernLift · ·10 min read
Part 2 of 13

There are six practical types of technical debt — code debt, architectural debt, test debt, documentation debt, dependency debt, and knowledge debt. Each has a different cause, a different interest rate, and a different repayment method, which is why naming the type matters — you cannot price or pay down debt you have lumped into a single undifferentiated pile.

Part 1 defined technical debt as the compounding future cost of rework. That definition is correct but too coarse to act on. “We have a lot of tech debt” is the start of a conversation, not a plan — because the debt in your test suite is repaid in a completely different way, on a completely different timeline, than the debt in your architecture. Before you can measure debt (Part 5) or pay it down (Part 9), you have to know which kind you are looking at.

This part breaks technical debt into six types. They are not mutually exclusive — a single system carries all six at once, and they feed one another — but each has a distinct cause, a distinct interest rate, and a distinct repayment method. Naming the type is the first step toward pricing it.

Here is the whole taxonomy on one screen before we take each type in turn. Read the third column as the interest rate, meaning how often the debt costs you, and the fourth as the repayment method, because those two are what actually differ between the types.

TypeWhat it isWhere the interest is paidHow it is repaid
Code debtMessy, duplicated, or dead codeWhen you work in that codeRefactoring, once tests exist
Architectural debtStructural decisions that resist changeOn nearly every change that crosses the bad boundaryRe-drawing boundaries incrementally
Test debtMissing or unreliable automated testsOn every change you have to verify by handRebuilding the test and parity harness
Documentation debtBehavior written down nowhere, or written down wrongWhen the next engineer rediscovers it the hard wayCapturing accurate, current documentation
Dependency debtAging libraries, runtimes, and platformsAll at once, when support ends or a CVE landsUpgrading on a schedule, before the jump grows
Knowledge debtUnderstanding that left with the people who held itWhen you must rediscover behavior by archaeologyCapturing understanding as living specs before people leave

The rest of this part is that table with the reasoning filled in, one type at a time.

1. Code debt

Code debt is the most familiar kind: the debt that lives inside the implementation. Duplicated logic, functions that have grown to hundreds of lines, dead code nobody dares delete, copy-paste that should have been a shared abstraction, naming that lies about what the code does. It is what most people picture when they hear “technical debt.”

Code debt is also the most containable of the six, which makes it the most over-weighted. It is local — a tangled function affects mostly the people working in that function — and it is the easiest to repay, because refactoring code with good tests behind it is routine, low-risk work. The trap is treating code debt as the whole of technical debt. It is the visible tip; the costly debt is usually structural, and lives below.

2. Architectural debt

Architectural debt is debt in the shape of the system — the boundaries between components, the data model, the coupling between modules, the decision to make something a monolith or to split it before the seams were understood. It is the structural decisions that made sense for the system you had and resist the system you now need.

This is usually the most expensive type, for one reason: its interest is paid on nearly every change. A bad function costs you when you touch that function. A bad boundary between two services costs you on every feature that has to cross it. Architectural debt cannot be refactored away in an afternoon, because the structure is load-bearing — changing it means changing everything that leans on it. It is the debt that turns “add one field” into a three-team coordination problem, and it is why tightly coupled systems become so hard to evolve that they cross into legacy.

3. Test debt

Test debt is missing, inadequate, or unreliable automated tests — and the slower, riskier development that results. A system with thin test coverage forces every change to be verified by hand, which is both expensive and unreliable, so changes get smaller, rarer, and more frightening. Flaky tests are their own form of this debt: a suite that cries wolf is quietly ignored, which is the same as having no suite at all, except that you paid to maintain it.

Test debt deserves separate billing because it is a force multiplier on every other type. Code debt with good tests is safe to refactor; code debt without tests is not. Test debt is what makes all the other debts harder to pay down — you cannot confidently change what you cannot confidently verify. In a modernization, rebuilding that safety net is foundational, which is why a test harness is one of the first things established and why proving functional equivalence is its own discipline.

4. Documentation debt

Documentation debt is the gap between what the system does and what is written down about it — and, just as often, the gap between what the docs say and what the code actually does. Stale documentation is worse than none, because it is confidently wrong. The behavior that is documented nowhere — the rounding rule, the special case for one legacy customer, the ordering that a downstream job quietly depends on — is documentation debt that the next engineer pays for by rediscovering it the hard way.

It is distinct from knowledge debt below in a subtle but important way: documentation debt is knowledge that could be written down but isn’t (or is wrong). Knowledge debt is knowledge that has already left.

5. Dependency debt

Dependency debt is the debt that accrues simply because the world moves and the system doesn’t: outdated libraries, end-of-life runtimes, unsupported frameworks, an operating system or database version past its support window. Unlike the others, dependency debt grows even when you never touch the code — the codebase sits still while the ecosystem around it ages out from under it.

This is the debt with the sharpest tail risk. An unpatched dependency is a quiet cost right up until it becomes a critical security finding or a vendor pulls support, at which point the interest payment arrives all at once. It is also the debt most directly tied to the security and compliance pressure that pushes systems toward modernization, and the one that compounds most mechanically: every year deferred makes the eventual upgrade jump larger and riskier.

6. Knowledge debt

Knowledge debt is the most dangerous type because it is the hardest to even see. It is the understanding of why the system does what it does — the context, the history, the deliberately-weird-for-a-good-reason decisions — that lives only in people’s heads and leaves when they do. Documentation debt is knowledge not written down. Knowledge debt is knowledge no longer available to write down.

When the engineer who built the billing engine retires, the debt is no longer code you can read; it is behavior you must rediscover by archaeology. This is why an aging workforce is an accelerant for technical debt and not just a staffing problem — the data is stark. IBM (reported via Fujitsu, 2020) reports the average age of a COBOL developer at roughly 58, with about 10% of the COBOL workforce retiring each year. Every retirement converts living knowledge into knowledge debt on a schedule. Capturing that understanding as living specs before it walks out the door is, in a real sense, the most time-sensitive debt repayment there is.

A second axis: deliberate vs. inadvertent

The six types describe where debt lives. A second, orthogonal lens describes the intent behind it — Martin Fowler’s technical-debt quadrant, which crosses two questions: was the debt deliberate or inadvertent, and was it prudent or reckless?

RecklessPrudent
Deliberate“We don’t have time for design.”“We must ship now and will deal with the consequences.”
Inadvertent“What’s layering?”“Now we know how we should have done it.”

The two most important cells are the diagonal. Deliberate-prudent debt is leverage used well: a recorded, intentional trade-off with a repayment plan, exactly the good borrowing Part 1 described. Reckless-inadvertent debt is a team that does not know it is borrowing — and it is where most ruinous debt actually comes from, because debt you cannot see is debt you will never repay. Any of the six types can sit in any quadrant; the quadrant tells you how the debt was incurred, which matters for prevention, while the type tells you where it lives, which matters for repayment.

Why the taxonomy earns its keep

This is not classification for its own sake. The type determines the repayment method, and using the wrong method wastes the effort:

  • Code debt is repaid by refactoring — but only safely once test debt is addressed first.
  • Architectural debt is repaid by re-drawing boundaries, which is structural work best done incrementally rather than in a big-bang rewrite.
  • Dependency debt is repaid by upgrading on a schedule, before the jump grows dangerous.
  • Knowledge debt is repaid by capturing understanding as durable specs — and it is the one type that gets strictly more expensive to repay with every passing month.

Lump all six into one “tech debt” backlog and you will tend to pay down the visible, cheap code debt while the expensive architectural and knowledge debt compounds untouched. Telling the types apart is what lets you spend repayment effort where the interest rate is highest.

The taxonomy isn’t a law of nature

These six categories cover the debt that matters in practice, but the boundaries between them blur, and other taxonomies slice differently — some separate “process debt” or “infrastructure debt,” some fold test and documentation debt together. The categories are a tool for thinking and pricing, not a law of nature. Do not spend a planning meeting arguing whether a given problem is “really” code or architectural debt. The point of naming the type is to choose the right repayment method and estimate the right cost — if the name does not change what you would do, the classification has done its job and you can move on.

Where this leads

We can now name the six places debt lives. But naming what debt is and where it lives does not explain how it got there — and you cannot prevent what you do not understand the cause of. Part 3, What Causes Technical Debt, traces the specific forces — business, technical, and human — that generate each of these six types, because the most cost-effective debt management is preventing the debt you can and repaying only what you must.

Frequently asked questions

What are the main types of technical debt?
Six types cover most real systems — code debt (messy or duplicated code), architectural debt (structural decisions that resist change), test debt (missing or unreliable tests), documentation debt (knowledge that exists only in code), dependency debt (aging libraries, runtimes, and platforms), and knowledge debt (understanding that left with the people who held it). Each is repaid differently.
Which type of technical debt is the most expensive?
Architectural and knowledge debt are usually the costliest. Code debt is local and refactorable; architectural debt is systemic, so its interest is paid on nearly every change. Knowledge debt is the hardest to even see — when the people who understood the system are gone, the debt is no longer code you can read but behavior you must rediscover, which is why recovering it is a discipline of its own.
Is there a difference between deliberate and accidental technical debt?
Yes, and it cuts across all six types. The Fowler quadrant separates debt that is deliberate from inadvertent, and prudent from reckless. Deliberate-prudent debt is a recorded, intentional trade-off; reckless-inadvertent debt is a team that does not know it is borrowing. The quadrant describes the intent behind debt; the six types describe where in the system it lives.
Is all technical debt bad?
No. Some technical debt is a sound, deliberate trade-off. Martin Fowler's quadrant separates debt that is deliberate and prudent, meaning a recorded decision to ship now and repay later with a plan, from debt that is reckless or inadvertent. Deliberate-prudent debt is leverage used well and is often the right call under a real deadline. The dangerous kind is the debt a team takes on without realizing it, because debt you cannot see is debt you will never repay. The goal is not zero debt. It is knowing which debt you hold, why, and how you will pay it down.
All 13 parts of Technical Debt: Measure It, Price It, Pay It Down →