Skip to content

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, or chore.
  • 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, or pyright, along with failing security scans or pinned-but-unsupported dependency versions in pyproject.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:

A 2x2 matrix, x axis Inadvertent to Deliberate, y axis Reckless to Prudent. Now we know better (top left), Must ship now (top right), What is layering (lower left), No time for design (lower right).
Fowler splits debt along two axes: was the shortcut deliberate, and was it prudent.
  • 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.

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.

intermediate best-practices

For additional information on related topics, take a look at the following resources:


By Martin Breuss • Updated May 29, 2026