Technical Debt: What It Is, Types, and How to Manage It

Technical debt is the extra work a team faces later because of shortcuts or design choices that no longer fit the system. Like financial debt, tech debt has a principal and interest: the principal is the work needed to fix the problem, and the interest is the extra effort spent working around it.
For engineering leaders, the harder question is which debt is slowing delivery and worth fixing first. This guide covers the main types, their costs, and how to identify, measure, and manage them, with links to detailed guides.
What Is Technical Debt?
Technical debt refers to a design or implementation that makes future work harder than it needs to be. A shortcut might help you meet a deadline today but force the team to repeat a manual step every time a customer signs up. The shortcut saves time once; the workaround keeps charging you.
Ward Cunningham introduced the technical debt metaphor in 1992. His WyCash experience report describes how an initial implementation can speed delivery while leaving cleanup to do. If the team keeps deferring that cleanup, the extra effort of working around the implementation becomes the interest.
What Tech Debt Means in Plain Terms
Suppose your team needs per-customer rate limits before a launch. You plan to build a small limits service, but there isn't time before the deadline. Instead, you ship a hardcoded map of customer IDs in the API gateway config. The product launches on time, and you record the service as follow-up work.
In this example, building the service is the principal. The interest arrives over the next few months: every new enterprise customer needs a config change and a deploy, and support can't see anyone's limits. A second gateway gets a copy of the map, and the copies drift apart. Those recurring costs help you decide when the shortcut is no longer worth keeping.
Technical Debt vs. Bad Code
Poorly structured code and a deliberate shortcut can both make later changes harder, but they call for different responses. If an engineer hasn't learned to structure code yet, refactoring the result won't prevent the same problem next time. The team also needs to address the skills gap.
Robert Martin separates poorly structured code from deliberate borrowing to ship sooner. Martin Fowler includes it as reckless debt. We use Fowler's broader definition here, while keeping the distinction between fixing the code and addressing why it was written that way.
Types of Technical Debt
Technical debt shows up across your whole system, well beyond your code. The usual starting point for sorting it is Martin Fowler's technical debt quadrant, which splits debt into reckless vs. prudent and deliberate vs. inadvertent. Crossing those two axes gives four categories, each with a different reason the debt arose.
Deliberate vs. Inadvertent Debt
Intentional technical debt is the kind you sign for. Your team keeps a nightly batch sync instead of building an event stream because launch is two weeks away, knowing it will cost something later.
Unintentional technical debt creeps in without anyone deciding. A team designs its data model around today's access pattern, and a year later the product needs a different one. Nobody chose that debt, which is why it's harder to manage: the team doesn't know it's there until a change gets expensive.

In Fowler's account, even strong design teams incur prudent, inadvertent debt as they learn what the system actually needs. The reckless, deliberate kind is the one to stop at the door, because the team knows it's skipping design and usually underestimates how soon that will cost more than it saved.
Architectural Debt
Architecture debt is a fundamental choice about how the system is organized that now constrains everything built on it. You see it in a monolith that can't be split, or in a schema that mixes two business concepts so thoroughly that separating them means rewiring half the system. The original choice may have served an earlier constraint, like microservices adopted before the team could operate them or a custom framework that outlived the people who built it.
No quick refactor fixes these, because the problem sits in the boundaries between modules, services, and data stores rather than inside any one file, which is also why code-level scanners rarely flag it. A live view of your architecture built from what's actually running helps you spot the decisions that are starting to constrain you before they become hard to reverse.
Code Debt
Code debt includes messy functions, spiraling complexity, duplicated logic, and missing tests. It slows changes within a module; refactoring and tests let you address that implementation work without necessarily changing the wider architecture.
Infrastructure and DevOps Debt
Think of a deployment pipeline of manual scripts only one person understands, or monitoring stitched together from tools that don't talk to each other. Infrastructure changed by hand also drifts from its templates over time, a familiar kind of configuration drift. Infrastructure debt makes rollbacks harder and scaling riskier, and the developers writing application code rarely see it. It might look like a team discovering dozens of Node.js versions across its services, none chosen on purpose, so one package upgrade becomes a coordinated change across every deployment.
Security Debt
Picture a known security vulnerability in an API you can't fix without breaking your largest customer's integration. Security debt's interest tends to stay hidden until an incident. It also stacks on top of architectural debt, because when every change is risky, security fixes get deferred along with everything else.
Documentation and Test Debt
Documentation debt is knowledge that lives only in people's heads, like a runbook that was never written or an architecture decision whose reasoning left with the engineer who made it. You pay for it in onboarding time and in decisions that get relitigated.
Test debt is the safety net you skipped, such as critical paths with no coverage, flaky tests everyone reruns until they pass, or a suite so slow people stop running it. Its interest is fear: teams avoid changing code they can't verify, so other debt stops getting paid down too.
What Causes Technical Debt
Check which of these pressures are creating debt in your system:
- Time pressure can make a shortcut attractive. Sprint deadlines, quarterly targets, and customer promises made before engineering was consulted push development teams toward short-term gains over long-term health. The intent to pay it back is real, but the quarter ends, a new deadline arrives, and the debt stays.
- Changing requirements turn yesterday's sound design into today's debt without anyone cutting a corner.
- A lack of architecture visibility means you can't pay down debt you can't see. Teams inherit systems with scattered docs and tribal knowledge, can't tell deliberate choices from accidents, and repeat the same patterns.
- Misaligned incentives keep debt in place. When engineers are measured on velocity and teams on feature count, nobody gets credit for paying debt down.
- Decisions made without data leave teams guessing about the cost of refactoring versus building new.
What Technical Debt Costs
These three estimates describe different costs: developer time, accumulated debt, and diverted budget. Compare them, but don't add them together.
- In Stripe's Developer Coefficient survey, published with The Harris Poll in September 2018, developers reported an average 41.1-hour work week, of which 13.5 hours went to technical debt and another 3.8 to bad code. That's about a third of the week on debt alone.
- CISQ's Cost of Poor Software Quality in the US: A 2022 Report puts accumulated US software technical debt at about $1.52 trillion.
- In a 2020 McKinsey survey of 50 CIOs at financial-services and technology companies with more than $1 billion in revenue, respondents said 10 to 20 percent of the technology budget meant for new products gets diverted to resolving tech debt issues.
Inside a company, the interest shows up as slower delivery, more incidents, longer onboarding, spend on services nobody can safely retire, and security exposure that grows while fixes wait. Maintenance costs climb while new features slip.
Examples of Technical Debt
Technical debt examples include:
- A library left two major versions behind because upgrading breaks a plugin nobody owns.
- A shared database that three services write to, so nobody can change a column without a coordinated release.
- An untested billing module that everyone routes around instead of changing.
- A cron job on one engineer's old VM that the month-end invoicing run quietly depends on.
How to Identify and Measure Technical Debt
You can usually find technical debt before you can measure it. Three groups of signals point to where it lives:
- In the code, look for pull requests that need round after round of review, files people avoid, copy-pasted logic, and hotspots where static analysis reports the highest complexity.
- On the team, ask developers what slows them down most and what they can't change without fear.
- At runtime, watch for slow or risky deploys, rising incident rates, long recoveries, and failures that cascade across services.
To measure technical debt, track a small set of metrics and watch the trend rather than any single threshold:
- Technical debt ratio, which compares the estimated cost to fix known issues with the cost to build the code.
- Cyclomatic complexity, which grows with every branch in a function. Rising complexity in frequently changed files is a strong signal.
- Test coverage trend on the code that changes most, not a blanket target.
- Change lead time and change failure rate, two of the DORA software delivery metrics.
- Failed deployment recovery time, which replaced mean time to recovery (MTTR) in DORA's metrics in 2023 and counts only recovery from failures caused by a change to production.
- Architecture-level signals, such as how many services you run and how many are used, which dependencies are critical, and the blast radius if one fails.
The first five come from code and pipeline tools. Architecture-level signals need a view of the running system: what exists, what connects to it, and what depends on it. That's the view we built Stacks to provide. Its coverage depends on the integrations you connect.
VPC Flow Logs, for example, become observed relationships between components. That lets you investigate dependencies using traffic that was actually seen. We currently support onboarding for AWS estates at companies past the early-stage startup phase.
We cover 12 metrics, how to put a dollar figure on debt, and how to build a dashboard in our guide to measuring technical debt.
How to Manage and Reduce Technical Debt
Managing technical debt comes down to making it visible and choosing deliberately which of it to pay down. Use these four habits to organize repayment.
Agree on What Counts as Debt
Write down what your team means by debt, including the edge cases people argue about, like a 500-line function or an unsupported library. Without a shared definition, some engineers refactor constantly while others ship and move on. Then keep a debt register that records where each item lives, who owns it, and what interest it's charging.
Budget Capacity Every Sprint
Reserve a fixed share of capacity for it so repayment happens steadily instead of in emergency refactors.
Modernize Incrementally
Big-bang rewrites tend to trade old debt for new. The strangler fig pattern fits when a component is too tangled to refactor in place, and you can intercept its traffic at a clear boundary. You build the replacement alongside it and move traffic over in slices. Where you can't route around a component like that, refactoring in place is usually safer.
Govern Architecture Decisions, Not Just Code
If you create debt faster than you pay it down, the problem sits upstream of the code, in how architecture decisions get made. Teams should see when someone adopts a new technology or splits a service, record why in architecture decision records, and review the decisions that are expensive to reverse.
This is the part we built Catio for. Archie, our reasoning agent, helps you work through whether to refactor, rewrite, or replace a component, using the context of your actual system.
Once you choose an approach, a Blueprint can capture the current state, the target state, and the implementation path. Compound, the final stage of our Loop, is designed to catch drift between what's running and what was decided.
For a prioritization framework and a backlog that people actually use, see our playbook for reducing technical debt.
Technical Debt in the Age of AI Coding Agents
Is AI creating technical debt? Yes, when code ships faster than architectural decisions can keep up. An AI coding tool can produce a working service, but if it duplicates an existing capability or ignores the modernization plan, it adds debt even when it passes code review.
Picture five teams, each using an AI coding tool to build its own authentication wrapper. Each one is solid. Now you have five authentication approaches instead of one, and the architecture fragments, not because anyone made a bad call but because none of the agents had the context to make a good one. This debt is well-written code that doesn't fit, and it can ship faster than a review board meets.
Give those agents the system's constraints before they start building. We've written about how coding agents without architectural context make locally correct changes that add up to drift. Using execution-ready specs helps make the intended change explicit, including its scope, constraints, and prior decisions.
Through our MCP server, an agent in Claude Code, Cursor, or VS Code with Copilot can ask about your architecture and pull specs from inside the editor. We don't replace coding IDEs; we define what they execute.
Conclusion
Technical debt is a normal trade-off. It becomes a problem when nobody can see where it lives or what it's costing. Check the boundaries between modules, services, and data stores as well as the code within them. Start with visibility into what's running and how it connects, govern the decisions that are expensive to reverse, and pay debt down steadily rather than in emergency rewrites.
If you're weighing which architecture debt to tackle first, chat with our team and bring the decision you're stuck on.
Frequently Asked Questions
How much technical debt is acceptable?
There's no safe percentage, because where the interest lands matters more than the total. Stable code that you rarely need to modify may not justify cleanup, because the extra work is incurred when you change it. Debt in code that most releases touch charges you on every change, so pay that down first.
Who coined the term technical debt?
Ward Cunningham introduced the metaphor in his 1992 OOPSLA experience report on the WyCash portfolio management system.
Is technical debt always bad?
No. Deliberate, prudent debt is a legitimate strategy when you're racing to validate a product hypothesis and knowingly cut a corner. The trouble starts when it's untracked or never paid down, the way a mortgage is reasonable and maxed-out credit cards with no repayment plan aren't.
What are the 4 quadrants of technical debt?
Fowler's technical debt quadrant crosses deliberate vs. inadvertent with reckless vs. prudent, which gives four kinds. On the deliberate side, reckless debt sounds like "We don't have time for design," and prudent debt like "We must ship now and deal with consequences." On the inadvertent side, reckless is "What's Layering?" and prudent is "Now we know how we should have done it."
How do you deal with technical debt?
Make it visible, then choose what to pay down. Agree on what counts as debt, keep a register with owners, and reserve steady capacity for the items charging the most interest, starting with the code you change most. Prioritize repayment by the cost of leaving each item unresolved, and keep the backlog current.

