The short answer
Quick answer: Technical debt is the extra work you create for your future self by choosing a quicker, messier solution now. Like financial debt, it is a tool. Borrowing lets you get something sooner, such as shipping before a deadline or testing an idea before investing in it, and that can be worth far more than the cost. It becomes a problem when it is taken on carelessly, hidden, or never repaid, because you pay interest: every later change in that area takes longer. Good teams borrow deliberately, keep track of what they owe, and pay down the parts that cost them the most.
Where the term comes from
Ward Cunningham introduced the metaphor in 1992 to explain to non-technical colleagues why a team needed time to rework code that already functioned. Martin Fowler's TechnicalDebt article sets out the idea:
- The principal is the work needed to do it properly.
- The interest is the extra effort every change costs while the shortcut remains.
The metaphor works because it speaks the language of trade-offs. Nobody thinks a mortgage is a moral failing.
The four kinds
Fowler's technical debt quadrant sorts debt on two axes: deliberate or inadvertent, and prudent or reckless.
| Reckless | Prudent | |
|---|---|---|
| Deliberate | "We don't have time for design." | "We must ship now and deal with the consequences." |
| Inadvertent | "What's layering?" | "Now we know how we should have done it." |
- Deliberate and prudent is the useful kind: a conscious choice with a reason and a plan.
- Deliberate and reckless is cutting corners without weighing the cost.
- Inadvertent and reckless is a mess made through lack of skill.
- Inadvertent and prudent is unavoidable. Even good teams learn, a year in, what the design should have been.
Not every mess deserves the name. Code that is simply careless is not a strategic loan.
When taking on debt is the right decision
You do not know if the thing is worth building
Most new features and products are experiments. Building a perfect, scalable version of something no one wants is the most expensive outcome of all. A rough version that answers the question quickly is the better investment.
The deadline has real value
A launch tied to an event, a contract, a regulatory date or a competitor can be worth more than clean code. Ship, then tidy.
The code is short-lived
A prototype, a one-off migration script or a demo does not need the engineering of a core system.
You will know more later
Designing for requirements you have guessed often produces the wrong abstraction, which is harder to remove than no abstraction. Sometimes the simple, slightly duplicated version is correct until the pattern becomes clear.
The interest is low
Debt in code that is rarely changed costs almost nothing. An ugly module that has worked untouched for five years is not a priority, whatever it looks like.
Startups depend on this. Many successful companies began with a simple, monolithic codebase and reworked it once they knew what they had. See monolith vs microservices.
When it turns bad
- The interest compounds. Shortcuts are built on shortcuts until every change touches fragile code.
- Nobody knows it is there. Untracked debt surprises you at the worst moment.
- It sits in the busiest code. Debt in a file changed every week is paid every week.
- "Later" never comes. The time to repay is always next quarter.
- It reaches the foundations: the data model, security, or the ability to deploy safely.
Warning signs
- Simple changes take much longer than they used to.
- Fixing one bug creates another.
- Parts of the system nobody dares touch.
- New team members take months to become productive.
- Estimates keep being missed. See why most software projects run late.
- Releases are stressful and infrequent.
Forms it takes
| Kind | Example |
|---|---|
| Code | Duplication, tangled functions, misleading names |
| Design | Components that know too much about each other |
| Tests | Little automated testing, so every change is risky |
| Dependencies | Frameworks and libraries several versions behind |
| Infrastructure | Manual deployments, servers configured by hand |
| Documentation | Knowledge that exists only in one person's head |
| Data | Inconsistent schemas, columns nobody understands |
Poor names are a small, constant tax of this kind; see why naming things is so hard.
Managing it
Make it visible
Record deliberate shortcuts when you take them: a ticket, a short note in the code that explains why and links to the ticket, and a line in the design record. Debt you can list is debt you can plan around.
Prioritise by interest
Do not sort by how ugly the code is. Ask where it slows you down:
| Rarely changed | Often changed | |
|---|---|---|
| Low pain | Ignore | Tidy opportunistically |
| High pain | Fix when next touched | Fix first |
Version control history shows which files change most. Those are where cleaning pays.
Pay continuously
- Leave code a little better than you found it, each time you work in it.
- Refactor as part of the feature that needs it, not as a separate project.
- Reserve steady capacity for maintenance, so it does not depend on winning an argument each time.
- Automate the safety net. Tests and a reliable pipeline make refactoring low-risk. See how CI/CD pipelines work.
Avoid the big rewrite
Throwing the system away and starting again is tempting and usually a mistake. The old code contains years of fixes for cases nobody remembers. A rewrite takes longer than planned, during which the old system must still be maintained, and the new one arrives with fresh debt. Prefer incremental replacement: build the new part alongside, move traffic over piece by piece, and remove the old part when it is no longer used.
Explaining it to non-engineers
"The code is messy" persuades no one. Describe the effect:
- "Changes to checkout take three weeks; with two weeks of clean-up they would take one."
- "This library stops receiving security fixes in March."
- "Every release needs a day of manual testing because this area has no automated tests."
State the principal, the interest and the risk, in terms of time, money and customers. That is the debt metaphor doing the job it was invented for.
Not everything is debt
Calling every disliked piece of code "tech debt" drains the term of meaning. A bug is a bug. A missing feature is a missing feature. An old but stable system that does its job is not a liability just because it is unfashionable. Reserve the term for design shortcuts that make future change more expensive.
AI-generated code
AI coding tools can produce working code very quickly, and can produce debt just as quickly: duplicated logic, inconsistent patterns, and code the team does not fully understand. The same tools are also useful for repayment, such as writing tests for old code and carrying out mechanical refactoring. Review standards matter more, not less, when code is cheap to generate.
Frequently asked questions
What is technical debt in simple terms?
The future cost of choosing a faster, lower-quality solution today. You save time now and pay extra effort on later changes.
Is technical debt always bad?
No. Taken deliberately, for a good reason, with a plan to repay, it is a sensible trade. It is harmful when it is careless, invisible or never repaid.
How do you measure technical debt?
There is no exact measure. Useful signals are how long changes take, how often they cause defects, and which parts of the code are changed most and feared most.
Should we stop feature work to pay it down?
Rarely. Steady, continuous repayment alongside feature work is more effective than large clean-up projects.
Conclusion
Technical debt is a financing decision. Borrowing against the future is often right when speed or learning is worth more than polish, and it is often how products get to exist at all. The skill lies in borrowing on purpose, knowing what you owe, and repaying where the interest is highest, before the debt begins making your decisions for you.
Related articles
- Why Most Software Projects Run Late
- Why Is Naming Things So Hard in Programming?
- Monolith vs Microservices: Why Companies Switch (Both Ways)
- How CI/CD Pipelines Ship Code Automatically
