System Design · Architecture

Managing Technical Debt

Telling deliberate shortcuts from accumulated mess, and getting repayment onto a roadmap.

— min read System Design

A Metaphor With A Point

The original metaphor is precise: debt is a deliberate shortcut taken to ship sooner, understood at the time, and repaid later with interest. Shipping fast and cleaning up afterwards is a legitimate strategy — as long as the second half happens.

The word has since expanded to cover everything unpleasant in a codebase, which weakens it. Separating the categories matters because they need different responses.

CategoryResponse
Deliberate and prudentRepay on the schedule you set when you took it
Accidental — the team has learned sinceRefactor as you touch the area
Rot — the world moved, the code did notUpgrade deliberately; it only worsens
Bad codeNot debt. It is a defect, and calling it debt excuses it
Calling everything "tech debt" is how it becomes unfundable. A named, dated shortcut with a cost attached gets scheduled; an amorphous complaint about the codebase never does.

Measuring The Interest

Debt is only worth paying down where it is charging interest. Some of it never will be — a module nobody touches, on a service due for retirement, costs nothing to leave alone.

Interest shows up asMeasure
Changes take longer than they shouldCycle time on that area
Every change breaks somethingChange failure rate, incidents per release
Only one person can touch itBus factor — a risk, not just a cost
Onboarding is slowTime to first meaningful change
Builds and tests crawlPipeline duration — paid on every commit
Fear of deployingDeployment frequency falling
That is the argument that works with the people who fund the work: not "the code is ugly" but "this area takes three times longer to change and causes most of our incidents". Debt is a delivery-speed problem, and delivery speed is a business metric.

Paying It Down

ApproachWhen it works
Opportunistic — improve what you touchContinuous, invisible, and the default
A budget — a fixed share of each cycleSustained improvement without a business case per item
Targeted project with a delivery goalWhen one area blocks something the business wants
Strangler pattern — new alongside old, migrate graduallyReplacing a large component safely
Big-bang rewriteAlmost never
The rewrite is the most seductive and most frequently fatal option. It restarts from zero features against a moving target, the old system still needs maintaining, and every undocumented behaviour it accumulated has to be rediscovered by breaking it.

The strangler pattern is the credible alternative: route a slice of traffic to the new implementation, migrate a piece at a time, keep both running until the old one is unused. It is slower on paper and finishes, which the rewrite frequently does not.

Not Accumulating It

PracticeEffect
Record the shortcut when you take itA dated ticket, not a comment nobody greps for
Boy-scout ruleLeave each file slightly better than you found it
Keep dependencies current continuouslySmall upgrades instead of one impossible jump
Delete dead code and unused flagsThe cheapest debt repayment available
Treat the pipeline as productSlow builds tax every change forever
Review architecture at boundariesCatch drift while it is still cheap to correct
Dependency rot is the debt most teams underestimate. Skipping upgrades for two years turns a routine bump into a migration project, and it is the version you cannot upgrade that eventually forces the rewrite you were avoiding.

Interview Questions

What is technical debt, precisely?

A deliberate shortcut taken to ship sooner, understood at the time and repaid later with interest. Bad code is a defect, not debt — conflating them excuses it.

How do you decide what to pay down?

By the interest it charges: cycle time, change failure rate, incidents and onboarding cost in that area. Debt in code nobody touches is free to leave alone.

How do you make the business case?

In delivery terms rather than aesthetics — "this area takes three times longer to change and causes most of our incidents" is fundable; "the code is ugly" is not.

Why do rewrites usually fail?

They restart at zero features against a moving target while the old system still needs maintaining, and every undocumented behaviour has to be rediscovered by breaking it.

What is the strangler pattern?

Building the replacement alongside the original and migrating a slice at a time until the old one is unused. Slower on paper, and it finishes.

Which debt is most underestimated?

Dependency rot. Deferred upgrades compound until a routine bump becomes a migration project, and an unupgradeable version forces the rewrite you were avoiding.

Quick Quiz

1. Technical debt in the original sense is…
2. Debt is worth paying down when it…
3. The most reliable way to replace a large component is…
4. The business case for debt work should be framed as…
5. The most underestimated form of debt is…