The Interest Rate on Your Codebase: A Financial Framework for Technical Debt

The Interest Rate on Your Codebase: A Financial Framework for Technical Debt

Table of Contents

1. About   technicalDebt softwareEngineering

tech-debt-banner.jpeg

Figure 1: JPEG produced with DALL-E 4o

Every engineer I've worked with has used the phrase "technical debt", but everyone seems to have a different notion of what this phrase actually means. I've also found the phrase to be quite controversial, sparking knee jerk reactions that cut short any meaningful discussions around how to improve codebases and the systems they precipitate.

In general, I feel the term has become a catch-all for anything in a codebase that someone doesn't like – messy code, missing tests, old frameworks, that one service nobody wants to touch, that slow CI in a repo that everyone has to touch…. This imprecision isn't just sloppy language – it's actively harmful. When everything is "tech debt," nothing is, and teams lose the ability to reason clearly about what to fix, when to fix it, and whether to fix it at all.

When everything is "tech debt," nothing is, and teams lose the ability to reason clearly about what to fix, when to fix it, and whether to fix it at all.

This post reclaims the financial metaphor that Ward Cunningham originally intended1, extends it with precise mappings to financial instruments, and proposes concrete management strategies that scale from a two-person startup to a thousand-engineer enterprise.

The key insight elicidated in this post, and the main subject of my argument, is that the real danger isn't the debt itself – it's the interest. This post will be useful to you because, if you are like most teams, you likely don't know your interest rate.

1.1. Executive Summary

Technical debt is a deliberate deviation from an optimal design that yields short-term value at the cost of increased future work.

Like financial debt, it has principal (the cost to fix), interest (the ongoing drag), and an interest rate (the frequency with which you interact with the debt multiplied by the friction it causes).

The problem isn't taking on debt – it's failing to manage the interest. This post provides a framework for classifying, measuring, and paying down technical debt at any organizational scale.

2. What Is Technical Debt, Actually?   definition history

2.1. The Origin Story

In 1992, Ward Cunningham presented an experience report at OOPSLA describing the development of a financial portfolio management system in Smalltalk. He needed to justify ongoing refactoring to his business stakeholders, so he reached for a an unironically relevant metaphor they'd understand1:

Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite… The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. – Ward Cunningham

Notice what he didn't say. He didn't say "writing bad code is like going into debt." He said shipping code before you fully understand the problem domain is like going into debt. The distinction matters enormously.

Cunningham was describing something specific: the team had built a working system, and through the process of building it, they had learned things about the domain that the code didn't yet reflect. The code worked. It was well-written. But it encoded an earlier, incomplete understanding of the problem. The "debt" was the gap between what the team now understood and what the code expressed.

This is a much more subtle – and much more useful – concept than "the code is messy."

2.2. A Precise Definition

Building on Cunningham's original insight, and drawing from a decade of subsequent refinement by Martin Fowler, Philippe Kruchten, and others, here's the definition I use:

Technical debt is a deliberate or accidental deviation from an optimal design that yields short-term value at the cost of increased future work.

Technical debt is a deliberate or accidental deviation from an optimal design that yields short-term value at the cost of increased future work.

Furthermore, technical debt has four essential properties:

  1. It has principal – the cost to remediate or redesign the code to its optimal state
  2. It accrues interest – the ongoing drag on velocity, reliability, and developer experience caused by working around the suboptimal design
  3. It is context-dependent – what constitutes "optimal design" changes as requirements evolve, scale increases, or the team's understanding deepens
  4. It is not inherently bad – like financial debt, it can be a rational tool when used deliberately

That last point is critical. A mortgage isn't reckless. Neither is shipping a prototype with hardcoded configuration to validate a business hypothesis. The question isn't whether to take on debt. It's whether you know you're doing it, and whether you have a plan to service it.

2.3. What Technical Debt Is Not

Precision matters here. Conflating these concepts with tech debt dilutes the metaphor to uselessness.

  • Bugs aren't tech debt because bugs are liabilities, not loans – you didn't choose them for short-term value. They're defects. Fix them on their own merit.
  • Legacy systems aren't tech debt because age \(\neq\) debt. A 15-year-old COBOL system that runs reliably isn't debt if nobody needs to change it. It's legacy – and only becomes debt if it impedes change.
  • Missing features aren't tech debt because "we haven't built X yet" isn't a loan you took out. It's product scope. Put it on the backlog.
  • "Bad code" isn't tech debt because subjective aesthetics aren't debt unless they cause measurable drag. If the only problem is that you don't like the style, use a linter.
  • Missing documentation isn't necessarily tech debt. It's only debt if it increases the cost of future work. Otherwise it's operational risk – might be debt, might not.
  • Using an old framework isn't necessarily tech debt. It's only debt if upgrading would reduce future costs enough to justify the remediation. Otherwise it's version lag – assess on a case-by-case basis.

The litmus test: does this thing have both a principal (cost to fix) and an interest rate (ongoing cost of not fixing it)? If it only has a principal and no meaningful interest, it's not debt – it's just cleanup work. If it has interest but no meaningful principal (you can't fix it), it's a constraint, not debt.

graph TB
    subgraph "Is It Really Technical Debt?"
        direction TB
        START["Identified Issue"]
        Q1{"Was it a design choice<br/>(deliberate or inadvertent)?"}
        Q2{"Does it cause ongoing<br/>friction (interest)?"}
        Q3{"Can it be remediated<br/>at a known cost (principal)?"}

        DEBT["✅ Technical Debt<br/>Manage it with the<br/>financial framework"]
        NOTDEBT1["❌ Not Debt<br/>It's a defect — fix it<br/>on its own merit"]
        NOTDEBT2["❌ Not Debt<br/>It's cleanup, not debt service"]
        NOTDEBT3["❌ Not Debt<br/>It's a constraint,<br/>not a financial instrument"]

        START --> Q1
        Q1 -->|"Yes — a tradeoff<br/>was made (knowingly<br/>or not)"| Q2
        Q1 -->|"No — it's a bug<br/>or defect"| NOTDEBT1
        Q2 -->|"Yes — it slows<br/>future work"| Q3
        Q2 -->|"No — it just<br/>looks ugly"| NOTDEBT2
        Q3 -->|"Yes"| DEBT
        Q3 -->|"No — there's no<br/>clear fix"| NOTDEBT3
    end

3. The Financial Analogy, Done Right   financialAnalogy

The power of Cunningham's metaphor is that finance has centuries of well-developed theory for managing debt. But most engineers use the analogy at the level of "debt = bad, pay it off" – which is about as sophisticated as saying "mortgages are bad, never buy a house."

Let's map the concepts precisely.

3.1. The Instrument List

  • Principal is the cost to remediate or redesign. "Refactoring the auth module will take 3 sprints."
  • Interest is the ongoing maintenance cost, velocity drag, and incident risk. "Every feature touching auth takes 2x longer and breaks in staging."
  • Interest rate is the frequency of change multiplied by the system friction. High for core modules that every team touches, low for rarely-touched utilities.
  • Refinancing is a large-scale refactor or migration – moving from a monolith to services, upgrading the ORM. You replace one debt instrument with another that has better terms.
  • Bankruptcy is a full system rewrite. "We're throwing it away and starting over." Sometimes necessary, usually avoidable.
  • Credit line is the team's tolerance for complexity before velocity collapses. It varies by team size, skill, and tooling – and it's finite.
  • Cash flow is engineering capacity: the person-hours available for debt service. It's your sprint capacity minus feature commitments.
  • Amortization is gradual debt paydown alongside feature work. "Every PR in auth must leave the code better than it found it."
  • Debt-to-equity ratio is the proportion of your codebase that is suboptimal vs. well-designed. High ratios mean fragility; low ratios mean resilience.
  • Credit rating is the team's track record of paying down debt. Teams that consistently ship debt paydown earn trust for future borrowing.

A key insight falls out of this mapping:

High-interest debt is debt that compounds every time you touch it.

High-interest debt is debt that compounds every time you touch it. The auth module you hack around daily costs orders of magnitude more than the deployment script you run quarterly – even if the deployment script's remediation cost (principal) is lower. This is why prioritizing debt paydown by "ease of fix" is backwards. You should prioritize by interest rate.

3.2. Types of Technical Debt \(\approx\) Types of Financial Debt

Not all debt is created equal, and not all debt should be treated the same way.

Here are a few high level categories:

3.2.1. Low-Interest, Long-Term Debt

Financial analog: a 30-year fixed mortgage.

This is stable code that is architecturally imperfect but rarely touched. For example, this could be a data ingestion pipeline written in Python 2 that runs once a month and processes CSV files. If it works and nobody modifies it, then the interest is near-zero because the frequency of interaction is near-zero.

Management strategy: leave it alone. Seriously. Refactoring this is like paying off a 2% mortgage early when you have 20% credit card debt. It's mathematically silly.

3.2.2. High-Interest Revolving Debt

Financial analog: credit card debt.

This is the core module that every team touches daily and everyone hates. For example, the ORM that requires three workarounds per query, or the authentication layer that breaks in every test environment, or the deployment pipeline that takes 45 minutes and fails 20% of the time.

I once worked on a team where the API gateway had been "temporarily" written as a single Express.js middleware file. Every team in the company routed through it. Every feature that touched authentication, rate limiting, or request validation required modifying that file. We had merge conflicts on it daily. The file was 3,000 lines long. The principal to fix it – decomposing it into proper middleware layers – was maybe two weeks of focused work. We deferred it for eighteen months because no sprint ever had "room." By the end, we estimated that the accumulated interest – in merge conflicts, debugging time, and incidents caused by accidental regressions in that file – had cost us closer to six months of engineering time. Two weeks of principal. Six months of interest. That's a 1,200% annual rate.

Management strategy: pay this down immediately. Every day you don't costs more than the day before, because interest compounds with each interaction. This is where the "just ship it" mentality becomes catastrophically expensive.

3.2.3. Variable-Rate Debt

Financial analog: an adjustable-rate mortgage.

This is debt whose cost changes based on external factors. A system that works fine at current scale but will collapse at 10x. Hardcoded assumptions about single-region deployment. A data pipeline that assumes English-only text.

The interest rate is low today but will spike when conditions change – a new market, a compliance requirement, a viral moment. The danger is that by the time the rate spikes, the principal has grown too.

Management strategy: cap your exposure. You don't need to fix it now, but you need to know about it and have a plan. Architecture decision records (ADRs) are your friend here – they document the assumptions that, when violated, will trigger a rate increase.

3.2.4. Hidden Debt (Off-Balance-Sheet)

Financial analog: off-balance-sheet liabilities (the kind that caused the 2008 financial crisis).

This is the scariest kind. Tribal knowledge that exists only in one engineer's head. Undocumented invariants that the code silently depends on. A deployment process that only works because of a specific order of operations that nobody has written down.

Hidden debt is the technical equivalent of off-balance-sheet liabilities – the kind that brought down Lehman Brothers. You don't know about it until it explodes.

Management strategy: incognito leverage too dangerous – make it visible. Do bus factor analysis, documentation sprints, runbooks, chaos engineering – anything that surfaces hidden assumptions. You can't manage debt you can't see.

3.3. The Debt Lifecycle

Technical debt doesn't appear and disappear in a single event — it follows a lifecycle. The diagram below traces debt from the moment it's incurred through a decision point where teams choose one of five responses. Notice the feedback loops: deferred debt compounds back into accruing interest, and even "resolved" debt eventually gives way to new debt as the system evolves.

graph LR
    INCUR["Debt Incurred<br/>(design shortcut taken)"]
    ACCRUE["Interest Accrues<br/>(ongoing friction)"]
    DETECT["Debt Detected<br/>(pain threshold reached)"]
    ASSESS["Debt Assessed<br/>(principal + interest estimated)"]
    DECIDE{"Decision Point"}
    PAY["Pay Down<br/>(refactor/redesign)"]
    REFINANCE["Refinance<br/>(large migration)"]
    ACCEPT["Accept & Monitor<br/>(interest rate is low enough)"]
    BANKRUPT["Declare Bankruptcy<br/>(full rewrite)"]
    DEFER["Defer<br/>(interest continues)"]

    INCUR --> ACCRUE
    ACCRUE --> DETECT
    DETECT --> ASSESS
    ASSESS --> DECIDE
    DECIDE -->|"High interest,<br/>manageable principal"| PAY
    DECIDE -->|"High interest,<br/>large principal"| REFINANCE
    DECIDE -->|"Low interest"| ACCEPT
    DECIDE -->|"Unmaintainable"| BANKRUPT
    DECIDE -->|"No capacity"| DEFER
    DEFER -->|"Interest compounds"| ACCRUE
    PAY -->|"Debt resolved"| INCUR
    REFINANCE -->|"Lower interest rate"| ACCEPT

4. Fowler's Quadrant: Why Teams Accumulate Debt   classification martinFowler

Martin Fowler's Technical Debt Quadrant provides the best taxonomy I've seen for why debt gets created. It classifies debt along two axes: deliberate vs. inadvertent and prudent vs. reckless2.

Each quadrant requires a different response:

  • Prudent + Deliberate: This is strategic debt. It's the mortgage. Document it, schedule the paydown, and move on. This is the only quadrant where the financial analogy fully applies, because it involves a conscious tradeoff.
  • Prudent + Inadvertent: This is learning debt. It's the most sympathetic and the most unavoidable. You can't know the right design until you've tried the wrong one. The paydown here is refactoring to reflect your improved understanding – exactly what Cunningham described.
  • Reckless + Deliberate: This is negligent debt. Teams that knowingly ship garbage and call it "velocity" are running up credit card debt with no intention of paying it off. This is the quadrant that gives technical debt a bad name.
  • Reckless + Inadvertent: This isn't really debt at all – it's a knowledge gap. The team doesn't know enough to recognize the shortcuts they're taking. The fix isn't refactoring; it's education, mentorship, and pairing. This includes experienced engineers working in unfamiliar domains – backend engineers building frontends, infrastructure engineers writing business logic. The gap is contextual, not personal.

4.1. Why Debt Is Rational

It's important to resist the temptation to moralize about technical debt. In many contexts, taking on debt is the right decision:

  • Speed to market: A startup with 6 months of runway doesn't need a perfectly designed system. It needs product-market fit. The debt is rational because the alternative is bankruptcy – the real kind.
  • Uncertain requirements: When you don't yet know what the system needs to do, investing in a "perfect" architecture is waste. Build the thing, learn what it should have been, then refactor. This is Cunningham's original insight.
  • Asymmetric risk: If the upside of shipping fast is 100x and the downside of the debt is 2x, the expected value calculation is trivial.
  • Exploration and prototyping: Prototype code should be debt-heavy. That's the point. The mistake is promoting prototype code to production without acknowledging the accumulated debt.

Tech debt is often a sign of rational decision-making under uncertainty – not incompetence.

This is the framing I use with stakeholders, and it changes the conversation immediately. Instead of "we need to clean up the code" (which sounds like an engineering vanity project), it becomes "we need to manage our financial exposure" (which sounds like responsible governance).

5. Interest, Not Principal, Is the Real Danger   interestRate measurement

Most teams think about technical debt in terms of principal – "how hard would it be to fix this?" They estimate the cost of a refactor, put it on the backlog, and let it sit there because it never outranks the next feature.

But the principal is static. The interest is what kills you. And interest compounds.

5.1. Measuring Interest (Without Fake Precision)

Avoid metrics theater. Lines of code, cyclomatic complexity, and lint scores are proxies at best and noise at worst. The following signals are more useful for measuring the interest on your technical debt:

  • Change amplification measures how many files or systems must be touched per feature. Track PR size and the number of changed files over time. If the trend is up, interest is compounding.
  • Time-to-confidence measures how long before an engineer trusts that a change won't break something. Measure the gap from "code complete" to "merged with confidence." Long gaps mean high friction.
  • Error surface area measures the number of ways a change can fail. Count unique failure modes in CI/CD and staging. The more ways things break, the higher the interest.
  • Onboarding half-life measures how long it takes a new engineer to become productive. Track days-to-first-meaningful-PR per new hire. If it's getting longer, your codebase is getting harder to understand.
  • Fear factor measures which systems nobody wants to touch. Survey your engineers: which parts of the codebase do they avoid? Fear is a leading indicator of high-interest debt.
  • Rework rate measures code that is added then quickly modified or deleted. This is DORA's new fifth metric – now officially tracked3.

These signals map directly to interest, not principal. A system with high change amplification and a high fear factor is accruing massive interest, regardless of how "easy" the fix would be.

5.2. The Compounding Effect

Interest on technical debt compounds in ways that are genuinely analogous to financial compound interest. Consider a module with a 10% "tax" – every feature touching this module takes 10% longer than it should.

Hacking around debt like trying to kill a mythological hydra – the effort is herculean, but the problem only grows with each swing of the engineer's sword.

In isolation, 10% sounds manageable. But compound it:

  • Feature A takes 10% longer because of the debt
  • Feature B also touches the module, and also adds workarounds, slightly increasing the friction
  • Feature C touches the module and now takes 15% longer because of the accumulated workarounds from A and B
  • Feature D's developer, frustrated by the mess, copies the module instead of extending it – creating a new debt instrument
  • Features E through Z now have two versions of the module to maintain

This is how a 10% tax becomes a 50% tax in 18 months. I've seen this exact pattern play out at multiple companies. Hacking around debt like trying to kill a mythological hydra – the effort is herculean, but the problem only grows with each swing of the engineer's sword. If the banner image of this blog post reminds you of a terrifying hydra, then I've accomplished my goal of being illustrious.

The chart above models three scenarios over five years. The red line shows what happens when debt is left unmanaged – a 10% velocity tax compounds into a 50%+ tax within a few years. The yellow line shows moderate, consistent debt service. The green line shows aggressive front-loaded paydown that levels off once the worst debt is addressed.

The takeaway: the earlier you start servicing debt, the cheaper it is. Just like a mortgage.

5.3. Lehman's Laws: Physics Agrees with Finance

The compounding effect isn't just an analogy – it's a law of software evolution.

Lehman's laws of software evolution, established empirically in the 1970s and validated repeatedly since, state that4:

  1. Continuing Change: A system must be continually adapted or it becomes progressively less satisfactory
  2. Increasing Complexity: As a system evolves, its complexity increases unless work is explicitly done to reduce it
  3. Self Regulation: System evolution is a self-regulating process with statistically determinable trends

Law #2 is the kicker. Complexity doesn't increase because engineers are careless. It increases because that's what happens to systems that interact with the real world. Entropy is the default. Order requires energy. This is as true in software as it is in thermodynamics.

Entropy is the default. Order requires energy.

Technical debt, in this framing, is unreduced complexity – the delta between the system's current complexity and the minimum complexity needed to serve its current purpose. Interest is the ongoing cost of carrying that excess complexity. And Lehman tells us that without deliberate effort, that delta only grows.

6. Management Strategies by Company Stage   strategy organizational

So the debt compounds. The interest accrues. Lehman's second law tells us complexity grows by default. The natural question is: what do you actually do about it?

The right approach depends heavily on context. A strategy that's right for a 500-person engineering org can be lethal for a 5-person startup, and vice versa.

6.1. Small Teams and Startups (2-20 engineers)   startups

6.1.1. Principles

  • Accept higher interest rates – your cost of capital is survival, not elegance
  • Avoid irreversible debt – you can tolerate messy internals, but not locked-in architectural commitments
  • Prefer debt that can be deleted – the best refactor is rm -rf

6.1.2. Tactics

  1. Build throwaway-by-design systems. If you acknowledge that v1 exists to find product-market fit, not to be your production architecture for the next decade, you can make fundamentally different design decisions. Use the simplest thing that works. Monolith over microservices. A single database over sharded clusters. Flat files over message queues.
  2. Keep boundaries clean even if internals are messy. The API contract between services matters. The internal implementation of each service does not – yet. This is the "clean interfaces, dirty internals" pattern, and it's the best debt strategy for startups because it preserves optionality while allowing speed.
  3. Track known debt explicitly. A TECH_DEBT.md in the repo root with a list of known shortcuts is worth more than a Jira board nobody checks. Each entry should note: what the shortcut is, why it was taken, and what would trigger the need to fix it.
  4. Favor deletion over refactoring. The best thing about startup code is that most of it can be thrown away. When a module becomes too debt-heavy, ask: "Would it be faster to rewrite this from scratch, now that we understand the problem?" At small scale, the answer is usually yes.

6.1.3. What to avoid

  • Don't invest in elaborate "tech debt tracking" tools – the overhead exceeds the benefit at this scale
  • Don't over-abstract "for the future" – you don't know what the future looks like yet
  • Don't take on irreversible debt (choosing the wrong database, the wrong cloud, the wrong primary language) just to move fast

6.2. Growth-Stage Companies (20-200 engineers)   growth scaleups

This is where debt management gets serious, and where I've seen the most damage done. At one growth-stage company I worked with, the engineering team tripled in a year. The codebase they inherited from the startup phase was a classic "clean interfaces, dirty internals" system – which had been fine at 8 engineers. But at 30, the dirty internals became a coordination bottleneck. Three teams would independently hack around the same internal limitation in three different ways, creating three new debt instruments per quarter. The startup-era debt didn't grow linearly with headcount – it grew quadratically, because every new engineer was another node in the collision graph.

You're past the point where everyone knows everything, but not yet at the scale where platform teams and formal governance make sense. This is the most dangerous stage.

6.2.1. Principles

  • Start budgeting for debt service – treat it like a line item, not an afterthought
  • Avoid compounding interest – stop building new features on top of known-debt foundations
  • Make debt visible – if leadership can't see it, they can't prioritize it

6.2.2. Tactics

  1. Allocate fixed capacity for debt service. 15-25% of engineering capacity, every sprint, non-negotiable. This is not a "20% time" passion project. This is interest payments. McKinsey's research found that organizations with systematic debt management freed up engineers to spend up to 50% more time on value-generating work5.

  2. Attach debt remediation to feature work. The most efficient way to pay down debt is to do it when you're already in the code. If Feature X touches the auth module, the sprint plan includes both Feature X and "improve auth module test coverage." This is the "boy scout rule" formalized: leave the campground cleaner than you found it.
  3. Adopt Architecture Decision Records (ADRs). When you make a deliberate debt decision, write it down. An ADR should capture: the context, the decision, the consequences (including known debt), and the conditions under which the decision should be revisited. ADRs are your institutional memory for why the code looks the way it does6.
  4. Establish ownership for core systems. Every high-interest system should have an owner – a team or an individual responsible for its health. Unowned systems accumulate debt fastest because nobody feels the pain enough to prioritize fixing them.

6.3. Large Organizations (200+ engineers)   enterprise platform

At enterprise scale, technical debt becomes systemic risk. Individual modules matter less than system-level friction: API boundaries, deployment pipelines, shared libraries, and the organizational structures that produce them.

6.3.1. Principles

  • Debt becomes systemic risk – a single high-interest module can bottleneck the entire organization
  • Coordination cost > code quality – the overhead of keeping 50 teams aligned often exceeds the cost of the debt itself
  • Debt hides in interfaces – the most expensive debt lives at system boundaries, not inside modules

6.3.2. Tactics

  1. Platform teams as "internal lenders." Platform engineering teams function like a bank: they provide well-maintained, low-friction infrastructure that product teams "borrow" from. In exchange, product teams agree to use the platform's APIs and conventions, keeping the system-level architecture coherent. This is the Team Topologies model in debt terms.
  2. Enforce contracts at boundaries. At scale, the cost of drift between teams dwarfs the cost of individual code quality. Schema languages, API contracts, integration tests at boundaries, and automated compatibility checks are all forms of debt prevention at the interface level7.
  3. Track change amplification across organizational boundaries. When a change in Team A's service requires changes in Teams B, C, and D, that's a debt signal. The organizational structure is creating friction, and per Conway's Law8, the software architecture will mirror (and reinforce) that friction.
  4. Plan refinances, not heroic rewrites. The "big rewrite" is the engineering equivalent of declaring bankruptcy. Sometimes it's necessary, but usually it's better to refinance – systematically reducing the interest rate through incremental migration. The Strangler Fig pattern9 is the canonical approach: build the new system alongside the old, redirect traffic incrementally, and let the old system wither.

6.4. Capacity Allocation by Stage

Notice how the allocation to debt service increases with company size. This is counterintuitive – you'd think larger companies with more resources could afford less proportional debt spending. But larger companies have more accumulated debt, more system boundaries where debt hides, and higher coordination costs. The 5% a startup spends is "fix it when you see it." The 25% an enterprise spends is "dedicated teams, formal processes, migration projects."

7. Refactoring as Refinancing, Not Cleanup   refactoring

One of the most powerful reframings in this entire financial analogy:

Refactoring is not "paying off" debt. It's refinancing it.

When you refinance a mortgage, you don't eliminate the debt. You replace one debt instrument with another that has better terms – lower interest rate, more predictable payments, better aligned with your current financial situation.

Good refactoring does the same thing:

Financial Refinancing Technical Refactoring
Lowers interest rate Reduces ongoing friction and maintenance cost
Improves optionality Makes future changes easier and less risky
Preserves cash flow Doesn't require a full stop to feature development
May increase principal temporarily The refactored code might be larger or more complex initially
Reduces monthly payments Each feature touching the refactored area takes less time

This reframing matters because it changes the conversation with stakeholders. "We need to stop feature work for 3 months to clean up the code" is a hard sell. "We're refinancing – we'll reduce our ongoing maintenance costs by 30%, freeing up 2 engineers' worth of capacity for new features" is a business case.

7.1. Good Refinancing vs. Bad Refinancing

7.1.1. Good refinancing

  • Incremental refactors attached to feature work – lower interest rate without stopping feature delivery
  • The Strangler Fig pattern for large systems – build the replacement alongside the original, migrate traffic gradually, decommission the old
  • Parallel systems with measured migration – run old and new simultaneously, compare results, switch over when confidence is high

7.1.2. Bad refinancing

  • Big-bang rewrites – the engineering equivalent of a Netscape-style full rewrite10. You lose domain knowledge, you lose battle-tested edge case handling, and you introduce new bugs. The principal might drop, but the risk premium spikes.
  • Refactoring with no measurable improvement – if you can't articulate what metric improves after the refactor, you're not refinancing; you're rearranging furniture
  • Cosmetic refactors (renaming, reformatting) disguised as debt paydown – this is the equivalent of refinancing a 5% mortgage to a 4.9% mortgage: technically better, practically irrelevant
graph TB
    subgraph "Refactoring Decision Framework"
        direction TB
        START["Proposed Refactor"]
        Q1{"Does this reduce<br/>measurable friction?"}
        Q2{"Can it be done<br/>incrementally?"}
        Q3{"Does it preserve<br/>existing functionality?"}
        Q4{"Does the team have<br/>sufficient context?"}

        GOOD["✅ Good Refinancing<br/>Proceed with measurement"]
        MAYBE["⚠️ Consider Strangler Fig<br/>Build alongside, not instead"]
        RISKY["⛔ High Risk<br/>Document a migration plan first"]
        NOPE["❌ Not a Refactor<br/>It's a rewrite — treat it as one"]

        START --> Q1
        Q1 -->|"Yes"| Q2
        Q1 -->|"No"| NOPE
        Q2 -->|"Yes"| Q3
        Q2 -->|"No"| MAYBE
        Q3 -->|"Yes"| Q4
        Q3 -->|"No"| RISKY
        Q4 -->|"Yes"| GOOD
        Q4 -->|"No"| RISKY
    end

8. AI-Generated Code: The New Debt Accelerator   artificialIntelligence codeQuality

No discussion of technical debt in 2026 is complete without addressing the elephant in the IDE: AI code assistants.

GitHub Copilot, Claude, and their cousins have fundamentally changed the economics of code production. Writing code is now cheaper and faster than ever. But cheaper production doesn't mean cheaper ownership – and this is where the financial analogy becomes urgent.

8.1. The Data

GitClear's 2025 research on AI-assisted code quality found11:

  • Code duplication (cloned blocks of 5+ lines) increased 8-fold between 2022 and 2024
  • Refactoring activity dropped from 25% of changed lines in 2021 to less than 10% in 2024
  • Code churn (code added then quickly modified or deleted) rose steadily, reaching an estimated ~7% by 2025

Google's 2024 DORA report corroborates: teams with higher AI adoption reported faster code review and improved documentation, but the same cohort showed a measurable decrease in delivery stability3.

In financial terms: AI assistants have massively increased the rate at which teams can issue new debt, while simultaneously reducing the rate at which existing debt gets serviced.

8.2. Why AI Amplifies Debt

The mechanism is straightforward:

  1. AI makes it trivially easy to add code – reducing the perceived cost of new functionality
  2. AI makes it no easier to understand existing code – the cognitive overhead of working in a complex codebase hasn't changed
  3. AI-generated code tends toward the generic and duplicative – it doesn't know your architecture, your conventions, or your existing abstractions
  4. Developers using AI skip the "understand before modifying" step more frequently – the code is generated faster than understanding develops

This creates a specific new type of debt that deserves its own name: comprehension debt.

AI assistants have massively increased the rate at which teams can issue new debt, while simultaneously reducing the rate at which existing debt gets serviced.

Comprehension debt is the gap between the size of a codebase and any individual engineer's ability to hold a working mental model of it. Traditional technical debt is about code that's wrong – suboptimal designs that create friction. Comprehension debt is about code that might be individually correct but is collectively unintelligible. Each AI-generated function may work perfectly, but when 40% of the codebase was written by an LLM that doesn't know your team's conventions, the overall architecture drifts toward incoherence – ten different patterns for error handling, six ways to call the same API, three duplicate utility functions that almost-but-do-not-quite do the same thing.

The insidious property of comprehension debt is that it can't be addressed by traditional refactoring. You can't "fix" a codebase that nobody can hold in their head. The debt isn't in any individual module – it's in the aggregate. The principal doesn't show up in any static analysis tool, and the interest manifests as slower onboarding, more bugs from unexpected interactions, and a growing sense among senior engineers that "nobody really knows how this system works anymore."

8.3. Mitigations

  • Treat AI-generated code as a PR from a junior engineer: review it with the same rigor, question design decisions, check for duplication against existing code. I may change my tune as AI code review tools improve, but I would also resist the urge to have something like CodeRabbit do your PR reviews, especially since this would fail to narrow the comprehension gap
  • Maintain strong architectural guard rails: linters, architecture tests (like ArchUnit), import restrictions, and pre-commit hooks that prevent the most common AI-introduced anti-patterns
  • Increase refactoring capacity proportionally: if AI is helping you produce code 2x faster, you need 2x the refactoring capacity to prevent the debt ratio from spiking
  • Track AI-specific quality metrics: code duplication rate, churn rate, and the ratio of net-new code to refactored code

9. The Organizational Failure Mode   organizationalDebt conwaysLaw

Here's the uncomfortable truth that most engineers don't want to hear:

Technical debt is often a management and incentive problem, not a technical one.

9.1. Conway's Law as a Debt Generator

Conway's Law states that organizations design systems that mirror their own communication structures8. The implication for technical debt is profound: organizational friction becomes architectural friction becomes technical debt.

If teams A and B can't communicate efficiently, the interface between their services will accrue debt – duplicated logic, mismatched assumptions, undocumented contracts. The debt isn't caused by poor engineering. It's caused by poor organizational design. And no amount of refactoring will fix it, because the structure that created the debt will recreate it.

The Inverse Conway Maneuver – deliberately restructuring teams to match the desired architecture – is the only way to address this class of debt. It's also politically difficult, which is why this debt tends to persist.

9.2. Incentive Misalignment

Technical debt grows fastest in organizations with these incentive patterns:

Incentive Pattern Debt Effect Fix
Rewarding only shipping Teams optimize for feature velocity, ignoring maintenance Include quality metrics in performance evaluation
No ownership past initial delivery Code is "thrown over the wall" – nobody feels the interest "You build it, you own it" culture
No time allocated for maintenance Debt service gets crowded out by feature work Fixed capacity allocation (the 15-25% rule)
Architecture decisions without accountability Decision-makers don't bear the cost of their choices ADRs with named authors and review dates
Promotion criteria that ignore technical health Senior engineers chase visible impact, not sustainable systems "Infrastructure impact" as a promotion criterion

Tech debt grows fastest where engineers feel powerless to address it. If your process doesn't allocate time for debt service, debt will accumulate. This is not a technical problem. It's a governance problem.

The most pernicious version of this: organizations that say they value quality but measure only velocity. Engineers quickly learn which metric actually matters, and they optimize accordingly.

10. A Framework for Action   framework decisionMaking

Let's pull everything together into a practical framework.

10.1. Step 1: Inventory Your Debt

You can't manage what you can't see. Build a simple inventory:

Field Description
Name Human-readable label (e.g., "Auth module monolith")
Type Low-interest / High-interest / Variable-rate / Hidden
Principal Estimated effort to remediate (T-shirt: S/M/L/XL)
Interest rate How often teams interact with this debt (Daily / Weekly / Monthly / Rarely)
Quadrant Fowler classification (Prudent+Deliberate, etc.)
Owner Team or individual responsible
Trigger Conditions under which this debt must be addressed

Don't over-engineer this. A spreadsheet or a markdown file (or org-mode file if you're cool like me 🤪) in the repo is fine. The point is visibility, not process.

10.2. Step 2: Prioritize by Interest Rate

Sort your inventory by interest rate, not by principal. The debt that every team interacts with daily costs more than the debt that one team encounters quarterly, even if the quarterly debt has a higher remediation cost.

The formula is simple:

\[ \text{Debt Priority Score} = \text{Interest Rate} \times \text{Number of Affected Teams} \times \text{Interaction Frequency} \]

This isn't meant to be precise – it's meant to force the right conversation.

10.3. Step 3: Allocate Capacity

Decide on a fixed percentage of engineering capacity for debt service. This should be:

  • Non-negotiable: it doesn't get borrowed for feature work when deadlines loom. The moment debt service becomes "optional," it stops happening – the same way savings stop when they're "whatever's left after spending." Treat it like a payroll obligation, not a discretionary budget.
  • Visible: leadership should see how the capacity is being used. A monthly one-pager showing which debt was serviced, what metrics improved, and what's next builds institutional trust. Over time, this evidence base is what lets you increase the allocation when it's needed.
  • Measured: track whether debt service is actually reducing interest payments. If you allocated 20% to debt service last quarter and velocity didn't improve, either you're paying down the wrong debt or the measurement window is too short.

In practice, I've seen this work best when the allocation is protected at the team level, not the org level. Telling 50 teams "the org is allocating 20% to debt" is meaningless. Telling each team "20% of your sprints go to debt service, and here's how we'll track the impact" creates accountability.

10.4. Step 4: Execute and Measure

For each debt paydown initiative, define:

  1. What metric improves? (Change amplification decreases, onboarding time drops, incident rate falls)
  2. By how much? (Even a rough target helps)
  3. By when? (Timeboxed – if it's not showing results in 2-3 sprints, reassess)

Track these before and after. This builds the institutional evidence base for continued debt service investment.

Here's a concrete example: suppose you're refactoring the authentication module. Before starting, measure: average PR size for auth-touching features (say, 15 files), average time from "code complete" to "merged" (say, 4 days), and staging failure rate for auth changes (say, 25%). Set targets: reduce PR size to <8 files, merge time to <2 days, staging failures to <10%. After two sprints of focused refactoring, measure again. If the metrics moved, you have evidence. If they didn't, you're either fixing the wrong thing or the refactor isn't done yet.

10.5. Step 5: Prevent New High-Interest Debt

Paydown without prevention is a treadmill. Implement:

  • ADRs for deliberate debt decisions – every time the team knowingly takes on debt, write a one-page record: what shortcut was taken, why, what the expected interest rate is, and what conditions should trigger paydown. This is the difference between a mortgage (documented, scheduled) and credit card debt (invisible, compounding).
  • Code review standards that explicitly evaluate debt introduction – add "does this PR introduce or increase technical debt?" as a standard review question. If yes, require a comment explaining the tradeoff.
  • Architecture tests that prevent structural regression – tools like ArchUnit (Java), Dependency Cruiser (JavaScript), or even simple import-graph checks in CI can enforce "module A should not depend on module B" constraints. These automated guardrails catch drift before it compounds.
  • Post-incident reviews that trace root causes back to specific debt – when an incident occurs, ask: "Was known technical debt a contributing factor?" If the answer is yes three times for the same debt item, it gets promoted to the top of the priority list regardless of its planned schedule.

11. Conclusion: Structure It, Don't Eliminate It   synthesis

Let me end with the thesis I promised at the start:

You don't eliminate technical debt. You structure it.

Every financial system carries debt. Healthy companies have mortgages, lines of credit, and corporate bonds. The difference between a healthy company and a bankrupt one isn't the presence of debt – it's the management of it. Healthy companies know their debt-to-equity ratio, their interest obligations, and their capacity to service existing debt before taking on more.

The same is true for codebases.

A healthy codebase has known, documented debt with managed interest rates, owned by specific teams, prioritized by business impact, and systematically paid down. An unhealthy codebase has the same amount of debt but nobody can tell you what it is, how much it costs, or who's responsible.

Every system carries debt. The question is who pays, when, and how predictably.

The financial analogy isn't perfect. No analogy is. But it gives us a vocabulary – principal, interest, refinancing, credit rating – that makes the invisible visible and the abstract concrete. It transforms "we have a lot of tech debt" from a vague complaint into a specific, actionable diagnosis: what is the debt, what is the interest rate, and what is our plan for servicing it?

Start there. The rest follows.

Cunningham's original insight was that shipping code creates a gap between what you've built and what you now understand. Thirty-four years later, the insight holds – but the gap has widened. We ship faster, understand later, and compound the difference daily. The metaphor he gave us is still the best tool we have for closing it. Use it precisely, and it will serve you well.

12. tldr

This post reclaims Ward Cunningham's original financial metaphor for technical debt and extends it into a comprehensive framework for managing code quality at any organizational scale.

The core argument begins with a precise definition: technical debt is a deliberate or accidental deviation from an optimal design that yields short-term value at the cost of increased future work. Critically, not everything that frustrates engineers is debt—bugs are liabilities, legacy systems are only debt if they impede change, and "bad code" is a style preference unless it causes measurable drag.

The post maps technical concepts to financial instruments with precision: principal is remediation cost, interest is ongoing friction, and the interest rate is interaction frequency multiplied by system friction. This leads to a taxonomy of debt types—low-interest long-term debt (like a mortgage), high-interest revolving debt (like credit cards), variable-rate debt whose costs spike under changing conditions, and dangerous hidden debt that lurks off the balance sheet.

Martin Fowler's quadrant classifies debt by whether it was deliberate or inadvertent, prudent or reckless—each requiring different responses. The post argues that debt is often rational, especially for startups validating product-market fit or teams exploring uncertain requirements.

The central thesis emerges in the section on interest: the real danger isn't the debt itself but the compounding interest. The post provides practical signals for measuring interest—change amplification, time-to-confidence, fear factor, and rework rate—and illustrates how a 10% velocity tax can become 50% in 18 months through compound effects that Lehman's Laws predict are inevitable without deliberate countermeasures.

Management strategies vary by company stage: startups should accept higher interest rates while avoiding irreversible debt, growth-stage companies must allocate 15-25% of capacity to debt service, and enterprises need platform teams functioning as internal lenders with enforced contracts at system boundaries.

The post reframes refactoring as refinancing rather than cleanup—replacing one debt instrument with another that has better terms—and distinguishes good refinancing (incremental, measurable) from bad (big-bang rewrites, cosmetic changes).

A timely section addresses AI-generated code as a new debt accelerator, introducing the concept of "comprehension debt"—the gap between codebase size and any engineer's ability to maintain a working mental model. The data shows AI tools have increased code production while reducing refactoring activity, creating a dangerous imbalance.

Organizational factors often matter more than technical ones: Conway's Law means organizational friction becomes architectural friction becomes debt, while incentive misalignment (rewarding only shipping, no ownership past delivery) accelerates accumulation.

The post concludes with a five-step framework: inventory your debt, prioritize by interest rate rather than principal, allocate fixed non-negotiable capacity for debt service, execute with measurable targets, and prevent new high-interest debt through ADRs and architecture tests. The final message: you don't eliminate technical debt, you structure it—just as healthy companies carry managed debt with known obligations and clear ownership.

Footnotes:

1

Ward Cunningham first described the debt metaphor in his 1992 OOPSLA experience report, "The WyCash Portfolio Management System." He later clarified in a 2009 video that the metaphor was specifically about the gap between the team's evolving understanding and the code's current design – not about deliberately writing poor code.

2

Martin Fowler's Technical Debt Quadrant (2009) built on Cunningham's original metaphor by distinguishing between deliberate/inadvertent and prudent/reckless debt. This taxonomy is now widely used in industry.

3

Google's DORA (DevOps Research and Assessment) program has tracked software delivery performance for over a decade. The 2024 State of DevOps report introduced rework rate as a fifth key metric and found mixed results from AI adoption: improved throughput but decreased stability. See: dora.dev.

4

Lehman's laws of software evolution were first proposed by Meir M. Lehman in 1974 and refined through the 1990s. The laws describe empirically observed properties of "E-type" systems (software that solves real-world problems). The key insight – that complexity increases unless explicitly counteracted – has been validated across decades of research. See: Wikipedia: Lehman's Laws.

5

McKinsey's 2023 report "Breaking technical debt's vicious cycle to modernize your business" found that technical debt consumes a significant share of IT budgets – CIOs surveyed reported that as much as 40% of their technology estate was composed of tech debt. Organizations with systematic debt management freed engineers to spend up to 50% more time on value-generating work. One CIO reported reducing the "debt tax" on engineer time from 75% to 25%.

6

Architecture Decision Records (ADRs) were popularized by Michael Nygard in a 2011 blog post. The format is simple: Context, Decision, Consequences. The key value is recording the context so future engineers understand why the decision was made, not just what it was.

7

For a deep dive on schema languages as an interface-level debt prevention strategy, see my companion post: The Schema Language Question: Avro, JSON Schema, Protobuf, and the Quest for a Single Source of Truth.

8

Conway's Law, introduced by Melvin Conway in 1967, states that "organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations." Research from MIT and Harvard Business School found "strong evidence to support the mirroring hypothesis." See: Wikipedia: Conway's Law.

9

The Strangler Fig pattern was named by Martin Fowler in his 2004 blog post, inspired by the strangler fig trees he observed in Australia. The pattern involves building a new system around the edges of the old, gradually routing traffic from old to new, until the old system can be decommissioned.

10

Joel Spolsky's "Things You Should Never Do, Part I" (2000) argues that full rewrites are "the single worst strategic mistake that any software company can make," citing Netscape's decision to rewrite their browser from scratch as a cautionary tale. The argument is that working code, however ugly, embodies years of accumulated knowledge about edge cases and failure modes that a rewrite will inevitably lose.

11

GitClear's "AI Copilot Code Quality: 2025 Data Suggests 4x Growth in Code Clones" analyzed over 200 million lines of changed code to measure the impact of AI assistants on code quality metrics. The findings suggest that AI tools are accelerating code production while reducing refactoring activity, leading to increased duplication and higher churn rates.