Every engineering team says it has technical debt, and almost none can say how much. That gap is the whole problem: 'the codebase is a mess' is a feeling, and feelings lose every argument against a roadmap with revenue attached to it. So the debt compounds quietly — features take longer each quarter, the same three people are the only ones who can touch billing, onboarding a new engineer takes two months instead of two weeks — and none of it appears in any document a board member reads. The teams that actually get debt paid down aren't the ones with the strongest opinions about clean code. They're the ones who learned to express debt as money and time, tie it to specific outcomes the business already cares about, and then pay it down continuously instead of asking for permission to stop.
Debt isn't bad code — it's a trade you stopped tracking
The original metaphor is precise and worth reclaiming: technical debt is a deliberate shortcut taken to ship sooner, accepted with the understanding that interest will be paid later. That's a legitimate and often correct business decision — shipping in six weeks with a hardcoded assumption can be worth far more than shipping in six months without one. The problem is almost never the shortcut; it's that nobody wrote it down, so the loan became permanent and invisible. This distinction matters because it changes who owns the conversation: deliberate debt is a business decision with a repayment plan, while accidental mess is an engineering-quality issue, and the two need different remedies. Ninety percent of what teams label 'tech debt' is actually the second thing — code that was never designed, just accreted. Separating the two is the first useful act, because deliberate debt can be scheduled, and accidental mess has to be prevented at the source through review standards, testing, and design time that nobody currently budgets for.
Quantify it in money and time, not story points
The move that unlocks funding is translating debt into the units your CFO already uses. Start with cycle-time drag: pick five representative features shipped this year, estimate how long each would have taken in a clean version of the system, and the delta — expressed as engineer-weeks, then as loaded salary cost — is your annual interest payment. Add the incident tax: pull your last twelve months of production incidents, mark which ones trace back to a known structural weakness, and cost them in engineering hours plus any customer credits or churn. Add the onboarding tax: the difference between how long a new engineer takes to be productive in your system versus a healthy one, multiplied by your hiring plan. Add the bus-factor risk: list the components exactly one person can safely modify — that isn't a cost, it's an exposure, and it's the number that makes boards uncomfortable in a useful way. When you can say 'this costs us roughly $400K a year in lost throughput and caused four of last year's seven customer-visible outages,' you're no longer asking for a favour; you're presenting an investment with a return.
Map debt to outcomes, not to a wishlist
The second reason debt work doesn't get funded is that it's usually proposed as a list of things engineers find distasteful, which reads to everyone else as preference. Reframe it around the thing the business is already trying to do. If the goal is enterprise deals, the debt that matters is whatever blocks SSO, audit logging, or your SOC 2 evidence. If the goal is a big launch, it's whatever won't survive the traffic. If the goal is faster shipping, it's the module that appears in the most pull requests and the most incidents. This also gives you a ranking function: score each item by how much interest it charges (frequency of contact × drag per contact) against the cost to fix, and work the top of that list. Most teams have a long tail of debt that is real, annoying, and genuinely not worth paying — code in a stable corner nobody touches is charging you almost nothing, however ugly it is. Ugliness is not interest. Contact frequency is.
Pay it down continuously, because the 20% rule mostly fails
The standard advice — reserve 20% of capacity for debt — is directionally right and almost always collapses the first time a deadline gets tight, because unallocated time is the first thing sacrificed. What survives is debt work with the same visibility and commitment as feature work: named items in the same backlog, with the same acceptance criteria and the same status in planning. Two patterns do most of the real work. The first is opportunistic improvement — every time you touch a file, leave it better, which routes effort automatically to the code you actually contact. The second is the strangler pattern: rather than rewriting a bad subsystem, put a new implementation alongside it, route traffic across incrementally, and delete the old path when nothing calls it. Both share the property that makes them survive contact with a roadmap — they deliver value continuously and can be paused at any point without leaving you stranded halfway through a migration. That last property is what makes them safe; the big-bang rewrite has the opposite one, which is why so many of them die at 70% complete with both systems running and nobody willing to finish.
The rewrite question
Occasionally a subsystem really is past saving, and pretending otherwise wastes years. The honest test is whether incremental improvement is converging: if six months of careful refactoring has made the thing measurably easier to change, keep going. If every improvement is immediately swamped by new problems, or the underlying model is simply wrong for what the product now does, incremental work is a treadmill. Even then, rewrite the smallest coherent piece rather than the system — pick a bounded capability, define its interface, build the replacement behind a feature flag, migrate traffic gradually, and prove it in production before deleting anything. And go in with the failure mode named out loud: full rewrites fail because they must reimplement years of accumulated edge cases nobody documented, while the old system keeps moving. If you can't articulate what specifically will be different this time, the answer is to keep strangling.
How Infiniti Tech Partners tackles technical debt
We're usually brought in at the point where a team knows the system is slowing them down but can't get the work funded, or can't spare the people to do it. We start by measuring rather than opining: cycle-time drag on real features, the incident record traced back to structural causes, the bus-factor map of who can safely touch what — turned into an annual cost figure and a ranked list scored by interest charged against cost to fix. Then we pay it down in the way that survives a roadmap: named items in your backlog, strangler-pattern migrations behind flags, and improvements routed to the code you actually contact, so nothing depends on a quarter where feature work stops. We're equally willing to tell you which debt to leave alone. Because we deliberately run a small number of engagements at a time, our senior capacity is planned a quarter out — if this is work you're hoping to get to before year end, the useful first step is a short call. We'll do the quantification exercise with you and leave you with the numbers whether or not you bring us in for the paydown.
Frequently asked questions
How do you quantify technical debt in business terms?
Translate it into money and time. Measure cycle-time drag by estimating how long five real features took versus how long they'd take in a clean system, then convert the delta to engineer-weeks and loaded salary cost. Add the incident tax (twelve months of incidents traced to structural causes, costed in hours plus customer credits), the onboarding tax (extra ramp-up time multiplied by your hiring plan), and the bus-factor exposure (components exactly one person can safely change). 'This costs roughly $400K a year and caused four of last year's seven outages' is an investment case, not a complaint.
What's the best way to pay down technical debt without pausing the roadmap?
Reserving 20% of capacity usually fails because unallocated time is the first thing sacrificed under deadline. What survives is debt work with the same visibility as feature work — named backlog items with acceptance criteria and planning status. Two patterns do most of the work: opportunistic improvement (leave every file you touch better, which routes effort to code you actually contact) and the strangler pattern (build the replacement alongside the old subsystem, route traffic across incrementally, delete when nothing calls it). Both deliver value continuously and can be paused safely at any point.
When is a full rewrite the right call instead of refactoring?
The honest test is whether incremental improvement is converging. If six months of careful refactoring has made the system measurably easier to change, keep going; if every improvement is swamped by new problems or the underlying model is simply wrong for what the product now does, incremental work is a treadmill. Even then, rewrite the smallest coherent piece rather than the system — bounded capability, defined interface, replacement behind a feature flag, gradual traffic migration. Rewrites fail because they must reimplement years of undocumented edge cases while the old system keeps moving.
Related reading
Monolith to Microservices Without Freezing the Roadmap
How to migrate a monolith to microservices incrementally — using the strangler pattern — without a risky big-bang rewrite or a multi-quarter feature freeze that stalls the business.
EngineeringSearch in Your SaaS: Postgres, OpenSearch, or a Vector Store
Most teams reach for a search cluster too early and a vector database too eagerly. How to tell which search problem you have before you operate one.
EngineeringWebhooks That Don't Lose Events: Building Outbound Events Customers Trust
A webhook looks like a POST request and behaves like a distributed system. Delivery guarantees, signing, retries, and the ops surface nobody budgets for.