Sarthak Garg

When to pay debt, when to take it

Debt is a tool, not a sin.

·9 min read·

Most technical-debt conversations I sit through don't end with a decision. Someone says we have too much technical debt; someone else says we are a startup and debt is the price of speed; a third person says we should reserve twenty percent of every sprint for paying it down. The meeting ends and the code keeps drifting in whichever direction the loudest engineer wanted it to drift.

None of the four frames in circulation helps much. Ward Cunningham coined the phrase "technical debt" in 1992 to explain to his boss why a financial application needed rework; his actual point was narrow: the code reflected the team's earlier understanding of the domain. The team's understanding had moved, and the cost was the gap between the two. Most people have drifted from that point and use "technical debt" as a synonym for sloppy code, which collapses Cunningham's claim into a tone complaint.

Martin Fowler's quadrant of deliberate-vs-inadvertent crossed with prudent-vs-reckless is the most cited frame I see in engineering-management writing. It gives a vocabulary, but not a decision rule. "Be deliberate and prudent" survives any contact with a real planning conversation by saying nothing specific.

The startup-leverage school treats debt as financial leverage against future engineering hours; it is correct in spirit and incomplete in practice: nobody in it names which debts compound viciously and which decay harmlessly. The strong form of "ship faster, fix it later" produces what Fowler would call the reckless-and-deliberate quadrant while sounding like the prudent-and-deliberate one.

The fourth frame is the 20%-per-sprint rule, the standard playbook answer. Some teams pair it with the boy-scout rule, popularized by Robert C. Martin, where every engineer leaves any file they touch a little cleaner than they found it. The pairing answers how much, but it does not answer which.

The leader's job is to answer the which question. Below are the seven decisions I run across the take-and-pay cycle, in order, every time. Each one is a section of this essay.

Reject both frames

Two frames dominate every debt conversation I have been in: the first treats all debt as moral failure; the second treats it as strategic leverage. Both produce reckless debt while sounding prudent.

The perfectionist frame argues for refactoring before shipping the next thing. This burns the cycles an early-stage product needs to discover what to build, and it usually rests on a smuggled assumption that good code is substantially slower to write. In my experience the trade-off is mostly imagined. The exception is a genuine sprint-to-survive moment, which is rare, and which the section on taking debt covers separately.

The leverage-maximalist frame argues that since debt is a feature, you should ship and not look back until you have product-market fit. This is right about the direction and wrong about the absence of any filter. Inside that gap, teams accumulate the debts that kill them and skip the debts that are free to carry.

The first decision is to refuse both frames out loud, in the planning conversation, before anyone reaches for a backlog item. Then go straight to locating the debt.

Locate by layer, judge by reversibility

When debt comes up I make the team name the layer it sits in. The layers I keep returning to:

  • Domain model. The concepts the system reasons in, how the problem is sliced. This is Cunningham's original debt layer.
  • Technology stack. Languages, frameworks, primary database, infrastructure primitives.
  • Architecture. Services, modules, boundaries, the shape of the system.
  • Foundational patterns. How data flows, how data is stored, how dependencies are initialised, error handling, state management.
  • Schema and data contracts. Migrations, indexes, denormalisations.
  • Public API and integration contracts. Versioning, backward compatibility.
  • Test scaffolding and observability primitives.
  • Per-file code.

Naming the layer is necessary because it changes who has to be in the room. It is not sufficient: the same layer can hold very different debts. What separates them is reversibility.

On the first product I worked on, the team picked Angular for early-stage speed, and the choice was correct on the day it was made. Today it is too expensive to port out of:

  • Hiring is harder because fewer candidates are fluent in it.
  • Bundles are heavy.
  • Performance has a ceiling we cannot raise inside the framework.
  • A swap touches the whole organisation.

That debt is stack-layer.

In the same codebase we also picked a message queue, also stack-layer. Swapping it is a weekend of work for one engineer, because the surface area is small, no other decisions are anchored to it, and the contract is simple.

Same layer, opposite cost: the layer tells you where the debt sits, but reversibility sets the cost. If you stop at the layer, you collapse these two debts and get the wrong answer for both.

Run the four thumb rules

Once the layer is named, I run four checks on the candidate debt. I use four because they are independent: a debt can fail one and pass the others.

  1. Reversibility cost. Count the surfaces the debt touches and the people who would have to coordinate to change it. I also fold in hiring drag and the number of other decisions anchored to this one. The Angular call is high on all three: hundreds of surfaces, organisation-wide coordination to change, a narrower hiring pool than alternatives.
  2. Compounding vs decaying interest. Does each new feature deepen the lock-in, or does the debt stay flat? Compounding debts get worse on their own; decaying debts get cheaper to remove because the code around them is naturally rewritten. The framework call compounds with every new screen; a duplicated helper in a file scheduled to be rewritten next quarter decays.
  3. When it fails, what comes down with it. Does this debt take a single feature down, or does it pull unrelated systems with it? A debt that costs only "an engineer wastes an afternoon" is in a different category from one that costs "the product is down at scale."
  4. Time-to-visibility. Is the pain loud now or silent for two years? Loud debts get paid because someone is shouting; silent debts (slowly worsening build times, slowly creeping coupling) accumulate until something snaps. Silent is dangerous.

The Angular call fails all four; the queue choice fails maybe one, with limited downstream damage if it dies under load. Both sit at the same layer; only one is a debt to refuse.

Apply the four rules to decisions large enough to deserve them. Don't run a four-axis check on a duplicated helper function. The next section covers that.

Filter meaningful debt from benign

Most debt backlogs are half taste, so the filter is one question per item. Does it slow the next two or three features, force workarounds in unrelated code, raise incident risk or correctness risk, constrain hiring, sit on the path of next quarter's work, or compound? If yes, it stays. If the item reads as "could be more elegant" or "the files don't all follow the same pattern," drop it.

The cleanest illustration I have is the ideal-code engineer: strong engineer, real taste, wants every file in the codebase written identically. The cost of imposing that is real: engineer hours, review friction, conflict with people who wrote the variants; the benefit is invisible. Stylistic inconsistency in stable code is not debt. It is taste, and the leader should name it as taste in the conversation rather than letting the backlog grow without bound.

Cosmetic consistency is the dominant false positive on the debt backlogs I have audited; rewriting code nobody is going to touch is the second.

When you take debt, name the exit

Speed-mode is the stretch where shipping fast is the only thing that matters: a sprint to first launch, a fire drill, a demo that decides a contract. When you are in it, you don't ask whether to take the debt. You ask when speed-mode ends. The exit is a date, a milestone, or a triggering event: first revenue, first hundred paying customers, a contract signed, a Series A close, six weeks from today.

On the first product I worked on, the initial sprint was correct: speed was the only thing that mattered. The miss was that we never defined the exit. We treated speed-mode as the default, and each release as still inside the sprint. We let the framework call, the architectural shape, and the foundational patterns calcify before anyone paused to ask whether we should.

If you cannot name the exit, you are not taking debt. You are letting the codebase drift and labelling it. The exit has to be specific enough that everyone on the team agrees it has been hit when it happens. "When we have time" and "next quarter" are not exits.

When you pay, pay what next quarter touches and what compounds

The debate about 20% per sprint versus dedicated sprints versus the boy-scout rule alone misses the real question: which debt to pay first.

I order the queue by two priorities. The first is the selective boy-scout rule: pay debt that sits on the path of upcoming features, because you are touching the code anyway and the marginal cost is low. The second is compounding debt: pay it even if next quarter does not touch it, because left alone it gets worse faster than you can outrun it.

Defer debt that decays. The code is going to be rewritten for product reasons inside a couple of quarters, so paying it down now is wasted work.

Most teams pay the loudest debt instead of the most compounding debt; the squeaky-wheel approach feels productive and is often wrong. The loudest debts are usually decaying (someone is on the file, complaining, and about to rewrite it) and the most compounding debts are silent.

In the agent era, the leverage moved up the stack

Most of the per-file code in the codebases I touch now is machine output; the bottom of the layer ladder is mostly automated. The structural decisions at the top (domain model, technology stack, architecture, foundational patterns) are not.

Agents leave compounding debt by default: they produce a strong v1 in two days; four weeks later the application is bug-riddled in ways that are hard to localise, because the structural decisions (where the loop lives, what it iterates over, the data flow, the dependency boundaries) were never authored deliberately. Each new generation is plausible-looking code layered on a substrate nobody owns.

The implication for this playbook is not that it matters less. It matters more. The top three layers are now doing almost all of the long-term damage; the bottom layer is cheap to rewrite, because the agent will rewrite it for you. Spend your attention on the structural decisions: which abstractions exist, where the boundaries sit, how data flows across them. Reversing decisions there is hardest, and you still need an experienced developer to make them well.

Tradeoffs and when not to use this

All seven are overkill on small calls: don't run the four thumb rules on a duplicated helper, and don't make the team name the layer for a one-day refactor. Match the discipline to the stakes; the more irreversible the decision, the more care you should give it.

All seven are dangerous to skip on the take side, even on a small team: the most common mistake I see is taking debt without naming the exit, which calcifies into default; the second most common is paying the wrong debt, the loud one instead of the compounding one.