technical debt
Technical debt is the accumulated future cost of choosing a quick or expedient software design over a cleaner one that would have taken longer to deliver. Like a financial loan, that shortcut buys speed today and charges interest later in the form of slower changes, more bugs, and harder onboarding until you pay down the principal through refactoring.
Ward Cunningham coined the metaphor in a 1992 OOPSLA experience report on the WyCash portfolio management system. He wrote that “shipping first time code is like going into debt” and that “a little debt speeds development so long as it is paid back promptly with a rewrite.”
How It Shows Up in Practice
Technical debt surfaces in everyday engineering signals long before anyone says the words out loud. A feature that should take an afternoon stretches into a week. A pull request title reads fix: work around the legacy auth module again. A backlog ticket says “rewrite the orders module” and has been pushed forward for three quarters. An incident postmortem traces the root cause to a TODO comment from 2022.
Most teams track debt in the same places they track other work. Common patterns include:
- A dedicated label on issues or pull requests, such as
tech-debt,cleanup, orchore. - A recurring slice of each sprint reserved for paydown, often 10 to 20 percent of capacity.
- An architecture decision record, or ADR, that documents a deliberate shortcut and the conditions under which it should be revisited.
- Static analysis dashboards like SonarQube, CodeClimate, or Codacy that estimate “remediation hours” from code smells and rule violations.
- For a Python project, linter and type-checker output from
ruff,mypy, orpyright, along with failing security scans or pinned-but-unsupported dependency versions inpyproject.toml.
You then decide which debts to service. High-traffic code that changes every week is paid down aggressively because the interest dominates. Stable code that has not been touched in two years is usually left alone, even when it looks ugly, because the principal is never recovered.
Common Variations
Martin Fowler popularized a four-quadrant breakdown that splits debt by intent and judgment:
- Deliberate and prudent: “We must ship now and deal with the consequences.” A conscious tradeoff often documented in an ADR.
- Deliberate and reckless: “We do not have time for design.” The shortcut is known and ignored.
- Inadvertent and prudent: “Now we know how we should have done it.” Debt discovered only after the team learned more about the domain.
- Inadvertent and reckless: “What is layering?” Debt created by gaps in skill or awareness.
A second common split separates three flavors of debt. Code debt covers duplication, dead code, and weak tests. Architectural debt covers wrong boundaries, shared databases, and missing abstractions. Infrastructure debt covers outdated runtimes, unpatched containers, and manual deploys. Tooling can measure the first reliably, the second indirectly, and the third only by audit.
Related Resources
Tutorial
Refactoring Python Applications for Simplicity
In this step-by-step tutorial, you'll learn how to refactor your Python application to be simpler and more maintainable and have fewer bugs. You'll cover code metrics, refactoring tools, and common anti-patterns.
For additional information on related topics, take a look at the following resources:
- Python Code Quality: Best Practices and Tools (Tutorial)
- Ruff: A Modern Python Linter for Error-Free and Maintainable Code (Tutorial)
- Python Constants: Improve Your Code's Maintainability (Tutorial)
- Documenting Python Code: A Complete Guide (Tutorial)
- Python Code Quality: Best Practices and Tools (Quiz)
- Modern Python Linting With Ruff (Course)
- Ruff: A Modern Python Linter (Quiz)
- Defining Python Constants for Code Maintainability (Course)
- Documenting Code in Python (Course)
- Documenting Python Code: A Complete Guide (Quiz)
By Martin Breuss • Updated May 29, 2026