SyncTrix logoSyncTrix
All articles
Platform10 min read

Technical debt: telling the kind worth paying down from the kind worth keeping

Not all technical debt should be repaid. Here's how to identify which debt is actually costing you and how to fund the work without a rewrite.

By Marcus Hale
Technical debt: telling the kind worth paying down from the kind worth keeping

Technical debt is the most overused term in engineering, applied equally to a deliberate shortcut taken under deadline and to code someone simply dislikes. The distinction matters because they warrant completely different responses, and treating aesthetic preference as debt is how teams burn quarters on refactors that change nothing a customer or an engineer would notice.

01Debt is only debt if it charges interest

The metaphor is precise and worth taking seriously. Debt means you accepted a future cost for a present benefit, and you pay interest until it is repaid. That interest is real and measurable: features take longer, changes are riskier, onboarding is slower, incidents are more frequent.

Code that is ugly but stable, well-understood, and rarely modified charges no interest. Rewriting it converts a working system into a new one with fresh bugs, for no return. The question is never whether code is good but whether its current state is costing you something you can name.

02Categories that behave differently

Grouping everything as debt obscures that these have different urgency and different remedies. Some compound aggressively while others stay flat indefinitely.

TypeInterest ratePriority
Missing tests in changing codeHigh - compounds fastAddress first
Unsupported dependency versionsHigh - security and blockingSchedule immediately
Architecture blocking a roadmap itemHigh while that item mattersFund with the feature
Duplicated logic divergingMedium - bugs fixed once, not twiceConsolidate opportunistically
Inconsistent styleNear zeroAutomate, do not hand-fix
Ugly but stable and untouchedZeroLeave it alone
Debt types and their behaviour

03Let the change pattern decide

The most practical prioritisation combines two signals: how often a piece of code changes, and how painful each change is. Code that changes weekly and is difficult to modify is where debt costs you most, and paying it down returns value immediately.

Code that has not been touched in two years costs nothing regardless of its quality, because nobody is paying the interest. Your version control history gives you the change frequency directly, and your engineers can tell you where the pain is. The intersection is your priority list, and it is usually much shorter than the list of things people complain about.

  • Overlay change frequency with modification difficulty.
  • Prioritise the intersection; ignore stable code regardless of quality.
  • Track how often incidents originate in the same components.
  • Ask where onboarding engineers get stuck - that is real, measurable friction.

04Fund it inside feature work

Requesting a dedicated refactoring quarter usually fails, and when it succeeds it produces a large risky change with no incremental value. The approach that works is improving code as you touch it for other reasons - the component you are modifying anyway gets tests, gets clarified, gets its interface tidied.

This spreads the work across normal delivery, ties improvement to code that is demonstrably active, and avoids the political difficulty of asking for time with no customer-visible output. It requires estimates that include the cleanup rather than treating it as unpaid overhead, which is a planning norm rather than a technical practice.

05Rewrites are almost always the wrong answer

The instinct that a system is beyond repair and should be rebuilt is common and usually wrong. The existing system encodes years of accumulated fixes for real problems - edge cases, integration quirks, regulatory requirements - and most of that knowledge exists nowhere else. A rewrite rediscovers it painfully, in production.

Meanwhile feature development stalls, the rewrite takes several times its estimate, and the business runs two systems in parallel for far longer than planned. Incremental replacement - strangling the old system component by component behind a stable interface - is slower to start and dramatically more likely to finish. Reserve full rewrites for cases where the underlying platform is genuinely unsupportable, and even then, migrate incrementally.

  • Prefer strangling the old system to replacing it wholesale.
  • Keep the old path running until the new one is proven under real traffic.
  • Migrate one component at a time behind a stable interface.
  • Treat the existing system's odd behaviour as encoded requirements, not mistakes.

Topics

technical debtrefactoringlegacy codecode qualityengineering strategyrewrite vs refactor

Marcus Hale

Lead Architect · SyncTrix

Writes about the engineering decisions behind production systems - architecture, delivery and the trade-offs that only show up at scale.

Building something like this?

SyncTrix engineers AI, SaaS, platform and cloud systems for enterprises and high-growth teams. Tell us what you're shipping and we'll scope it with you.