The 11 Types of Technical Debt (With Real Examples)
Developers can identify 11 types of technical debt with real examples, detection tips, and a framework for prioritizing what to fix first.

Developers can identify 11 types of technical debt with real examples, detection tips, and a framework for prioritizing what to fix first.
Most arguments about technical debt are really arguments about definitions. One engineer means the messy 800-line function, another means the framework three versions behind, and a third means the test suite nobody trusts. They're all right, and they all describe different types of debt that require different responses. Naming the type is the first step to fixing it, because "we have tech debt" is unactionable while "we have test debt in the payments service" tells you exactly what to do.
This guide breaks down the eleven types you'll actually encounter, grounded in the research taxonomy that catalogs them, with a concrete example of each and a way to find it in your own codebase. The aim isn't to memorize a list. It's to give your team a shared vocabulary precise enough that "we have tech debt" becomes a specific, ownable problem with a clear fix.
Technical debt is the future cost of a shortcut taken today. Ward Cunningham introduced the metaphor in his 1992 OOPSLA experience report to explain to business stakeholders why working software sometimes needs rework: you can ship faster now by borrowing against future maintainability, but like any loan, the borrowed time accrues interest until you pay down the principal.
The metaphor is useful precisely because it's financial. It lets an engineering team talk about code quality in the language a budget owner understands, which is why the rest of this article focuses so much on naming types: each type is a different kind of loan, with a different interest rate and payoff plan.
Before the types, one orthogonal distinction. Martin Fowler's Technical Debt Quadrant sorts debt not by where it lives but by how it was taken on, crossing deliberate vs. inadvertent with prudent vs. reckless:
| Deliberate | Inadvertent | |
|---|---|---|
| Prudent | "We must ship now and deal with consequences." | "Now we know how we should have done it." |
| Reckless | "We don't have time for design." | "What's Layering?" |
This matters because the type tells you what the debt is, while the quadrant tells you how worried to be. Prudent-deliberate code debt is a scheduled loan. Reckless-Inadvertent architecture debt is the kind that sinks projects. As Fowler notes, "a mess is a reckless debt which results in crippling interest payments." Keep the quadrant in mind as you read the types below; the same type can sit in any quadrant.
A widely cited academic source here is the ontology of technical debt terms by Alves et al., which catalogs 13 distinct types. A 2024 survey applying that ontology classifies real GitHub issues into those 13, "including architectural, test, build, and code debt, among others." The eleven below are the ones most engineering teams encounter in practice, each with a real example and a detection approach. (We also group a few closely related categories where practitioners usually manage them together, and one naming note: the ontology calls the last type "people debt", while "knowledge debt" is the more common industry phrasing for the same idea.)

The classic. Duplicated logic, overly long functions, poor naming, and high cyclomatic complexity. Example: a processOrder() function that grew to 600 lines because every new payment method bolted on another branch. Detect it with complexity and duplication metrics on high-churn files.
Structural decisions that no longer fit the system. Example: a shared database that three services write to directly, so no team can change a schema without coordinating with the other two. The most expensive type to fix and the hardest to see in a code scanner, because the problem lies in the relationships, not any single file.
Smaller-scale than architecture debt: weak abstractions, leaky interfaces, and missing patterns within a module. Example: a User class that handles authentication, billing, and notification preferences, so any change to one affects the others. Detect it by watching which classes change for unrelated reasons.
Missing, flaky, or low-coverage tests. Example: the integration suite that's been marked @skip for eight months because it's slow, leaving the most critical path unverified. The insidious part is that test debt makes every other type more dangerous to repay, since you can't refactor safely without a safety net.
Docs that are missing, wrong, or stale. Example: a runbook that references an AWS account decommissioned last year. In the AI era, this type is becoming more expensive because outdated docs now mislead both coding agents and humans.
Slow, brittle, or over-complex build and CI pipelines. Example: a fifteen-minute build with a flaky test that fails one run in five, taxing every engineer on every commit. Build debt is pure friction, paid by the whole team continuously.
Outdated or manually-managed infrastructure. Example: a production server still on an OS version that stopped receiving security patches, kept alive because nobody documented how it was provisioned. Often overlaps with security debt.
Known bugs that are deferred rather than fixed. Example: the backlog of "minor" issues that each gets worked around until the workarounds themselves become load-bearing. Defect debt is the type already visible in your issue tracker, making it the easiest to measure and ignore. The trap is that deferred defects rarely sit still; they spawn workarounds, and those workarounds spawn dependents, until removing the original bug means untangling three features that have quietly come to rely on the broken behavior.
The gap between what the software does and what it's now supposed to do. Example: a feature built for a pricing model the company abandoned two years ago, still running because removing it feels risky. This is the type that encodes a 2014 business rule nobody remembers.
Inefficient or outdated ways of working that slow the team. Example: a manual release checklist that takes an afternoon and gets skipped under deadline pressure. Process debt lives between the code and the org chart, which is why scanners never catch it.
Critical understanding is concentrated in too few heads. Example: one senior engineer is the only person who understands the reconciliation job, and the runbook is "ask her." When she's on vacation, the knowledge debt's interest comes due. Detect it by mapping which modules have a single recent author.
A note on what's missing: vendor lists often split out security debt and data debt as separate types, and both are real. We've folded them into infrastructure and architecture debt here, but if your domain is security- or data-heavy, treat them as first-class.
Not equally, and not predictably. The type that hurts is the one that is in code you change often. Architecture debt in a frozen legacy module costs almost nothing; the same debt in your most active service taxes every sprint. This is why a flat list of types is only half the picture, and why prioritizing debt by interest rather than by category beats any universal ranking.
That said, two types punch above their weight. Test debt is a force multiplier because it makes repaying every other type of riskier. And architecture debt compounds, because every new feature built on a bad foundation deepens it. If you have to rank, rank those two first in any code that's still under active development.
A useful mental model is interest rate versus principal. Some debt has a high principal but a low rate, like a large, ugly, but stable module that works fine and rarely changes. The principal is enormous, but you almost never pay interest, so leaving it alone is the correct financial decision. Other debt has a tiny principal but a punishing rate, like a small race condition in the payment path that causes a costly incident every few weeks. The fix is a day of work, but the interest is brutal. Teams that prioritize by total principal, the "biggest mess first" instinct, consistently pay down the wrong debt. The right move is to find the high-rate items regardless of size, which means scoring each type by how often its code changes and how much pain it has already caused, not by how alarming it looks in an audit.
Most type lists stop at definitions. The useful next step is detection, and most types map to a concrete search or metric you can run today.
For the code-level types (code, design, test debt), metrics do the work: complexity, duplication, and coverage trends point straight at the offenders. For the pattern-based types, search is the tool. Code Search finds every usage of a deprecated library (documentation and infrastructure debt), every call into a legacy module (architecture debt), and every reference to a feature flag that should have been removed (requirements debt) across every repository at once. The value is turning "we probably have some of this" into a count you can act on.
For tracking those counts over time, Code Insights turns any such query into a trended metric, so "callers of the deprecated auth client" becomes a chart that should only go down. Knowledge debt is the exception that search can't fully solve; for that, look at version-control history to find modules with a single recent author and spread the understanding deliberately.
Once you can name each type and find it, you're ready to manage it. Our technical debt management guide covers the framework for turning this inventory into a paydown plan.
The eleven types aren't trivia. Each one is a different loan with a different interest rate, and the response to test debt looks nothing like the response to requirements debt. Sorting your debt by type turns a vague sense of dread into a list of specific, ownable problems, and most of those problems map to a search or a metric you can run against the codebase today.
If you want to move from naming debt to tracking it, start by making one type queryable. See how Code Insights turns any pattern in your code into a metric you can trend to zero.
What are the 4 types of technical debt? When people say "the 4 types," they usually mean the most common types of debt: code, architecture, test, and documentation. The academic ontology catalogs many more (13 in the Alves taxonomy), but those four cover the bulk of what most teams face day-to-day.
What are the 4 quadrants of technical debt? Fowler's quadrant, crossing deliberate vs. inadvertent with prudent vs. reckless. It describes how debt was taken on, which is orthogonal to the type, which describes where it lives.
What are examples of technical debt? A 600-line function (code debt), a shared database three services write to (architecture debt), a skipped integration suite (test debt), and a runbook pointing at a decommissioned server (documentation debt). Every type has a concrete signature in real codebases.
What causes technical debt? Time pressure is the usual headline, but inadvertent debt, the "now we know how we should have done it" kind, is just as common and comes simply from learning. Cunningham's original point was that some borrowing is rational; the problem is borrowing without a payoff plan.

With Sourcegraph, the code understanding platform for enterprise.
Schedule a demo