Technical debt is one of the most used and least precise terms in software development. It covers everything from a comment saying 'TODO: clean this up' to a data model decision that now requires migrating five million rows to fix. These are not the same problem, but they often get discussed as if they are.
In operational systems, meaning software that runs a business rather than supports it, the distinction matters. Not all technical debt is dangerous. Some of it is managed, deliberate, and paid back on schedule. The kind that is not managed creates the incidents, the slowdowns, and eventually the rewrites.
Understanding what the real cost looks like changes how you make decisions about it.
Two kinds of technical debt
The first kind is tactical debt: a shortcut taken consciously, with a plan to revisit it. A hardcoded limit because the dynamic version can wait until the next sprint. A query that could be more efficient but handles current volumes without complaint. A test that covers the core case but not all the edge cases. This kind of debt is normal, manageable, and often the right call.
The second kind is structural debt: decisions baked into the foundations of the system that become expensive to change as the system grows. A data model that can't represent a business rule without a workaround. A module that has absorbed too many responsibilities to be changed without understanding the whole system. An integration that works but that nobody can explain, and that breaks unpredictably when anything adjacent changes. This kind compounds.
The cost of tactical debt is linear. You borrow a week of engineering time now and pay it back later at roughly the same rate. The cost of structural debt is exponential. Every new feature built on a shaky foundation is itself slightly shakier. Every workaround added to accommodate a bad data model makes the next workaround worse. The system slows down not in big visible moments but in the gradual accumulation of friction.
The operational cost that doesn't get counted
Engineering slowdown is the visible cost of technical debt. The costs that don't get measured are often larger.
The first is operational risk. A system with structural technical debt has predictable failure modes, not necessarily in the obvious places, but in the gaps between how the system was designed to behave and how it actually behaves under real conditions. These gaps widen over time as the system changes and the original design assumptions drift further from current reality.
The second is staff cost. Engineers who work in a codebase with heavy structural debt spend a disproportionate amount of time understanding the system before they can change it. New team members take longer to become productive. Experienced engineers burn out faster and leave, taking the mental model with them. The institutional knowledge required to operate the system ends up concentrated in one or two people, which is a risk in its own right.
When debt becomes a rebuilding trigger
The most expensive form of technical debt is the kind that eventually forces a rewrite. A data model designed for a business that no longer exists. A module architecture so tangled that no individual change can be made safely. A test suite so out of date that it provides false confidence rather than genuine coverage.
By the time a rewrite is on the table, the cost of the accumulated debt has typically already been paid many times over in lost engineering velocity. The rewrite itself is then an additional cost on top.
The rewrite also tends to reproduce the same structural problems on a new stack unless the organisation has understood what caused them in the first place. We have seen teams rebuild systems from scratch only to produce a new system with the same class of structural problems within three years, because the incentives and practices that produced the first system had not changed.
Why Software Gets Rebuilt Every Five Years and How to Avoid It →
Managing debt deliberately
The alternative to unmanaged debt is not no debt. It's managed debt: a deliberate understanding of what shortcuts have been taken, what the plan is to address them, and what the risk profile is if they're not addressed.
This requires making debt visible. Not just in engineers' heads, but in a maintained record. What are the known structural problems? What's the cost of living with them? What's the cost of fixing them? When those questions have answers, decisions about where to invest engineering time stop being abstract arguments about quality and become concrete trade-offs with understood stakes.
The systems we build carry technical debt, as all systems do. The difference is that we treat it as a managed liability rather than an unknown one. That means tracking it, sizing it, and building the case for addressing it before it becomes a crisis.
Debt you can see is debt you can manage
Technical debt is not inherently problematic. It becomes problematic when it's invisible, unmanaged, and structural. The businesses that handle it well are the ones that treat it like any other operational liability: something to be identified, sized, and addressed on a rational schedule.
The businesses that don't are the ones that find themselves funding a rewrite they didn't see coming, on a timeline they didn't choose, at a cost that's always higher than the estimate.
More in this series
- The Difference Between a Demo and Production Software
- Why Software Gets Rebuilt Every Five Years and How to Avoid It
- Audit Ready by Design: Building Software That Passes Every Review
- Observability Is Not the Same as Logging
- The Real Cost of Technical Debt in Operational Systems
- Deployment Patterns for Business Critical Systems
- How to Design Software That Scales Without a RewriteComing soon
- Production Readiness Checklist: 12 Things Most Teams SkipComing soon
- Graceful Degradation: What Happens When Part of Your System FailsComing soon