Glossary
Technical debt is deferred work that slows down future development. Learn how to manage it as a business decision.
At Salesken, we had a 'tech debt' label in Jira with 200+ tickets. When our board asked how much technical debt we had, I couldn't give them a number. That experience taught me that unmeasured debt is invisible debt.
Technical debt is code or architecture that works but isn't optimal. It's the accumulated shortcuts, unfinished refactoring, and design compromises that make a codebase harder to maintain, slower to execute, or more fragile over time.
Technical debt is usually taken on intentionally: "We need to ship fast, so we'll cut corners on tests" or "We'll use a quick hack now, refactor later." Sometimes it's accumulated unintentionally: complexity that grew over time, dependencies that became tightly coupled, or patterns that made sense once but now cause problems.
The metaphor is deliberate: like financial debt, technical debt has a cost. You can borrow time by shipping fast now, but you pay interest (slower development in the future). If you accumulate too much debt, you can find yourself unable to move fast no matter how hard you try.
Technical debt directly affects product velocity. Early in a product's life, velocity is high. The codebase is clean, tests are comprehensive, the architecture is simple. As the product grows, technical debt accumulates. The same feature that took 1 week a year ago now takes 3 weeks. Not because the feature is more complex, but because the codebase is harder to work with.
This is the core trade-off in product development: you can trade velocity now for technical debt, which costs velocity later. Or you can invest in code quality now, which preserves velocity later.
Product teams need to understand this trade-off. "We can ship this feature in 2 weeks if we take on technical debt, or 3 weeks if we don't." That's a choice with consequences. Taking on some debt might be the right call (market opportunity, competitive threat). But not understanding the cost is a mistake.
Code debt: Code that works but is hard to understand or change. Complicated functions, poor naming, missing comments, inconsistent patterns. Fixing requires rewriting.
Test debt: Missing tests or inadequate test coverage. When test coverage is low, changes are risky. Fixing requires writing tests.
Architectural debt: Architectural decisions that made sense once but now cause problems. Tight coupling between modules, circular dependencies, or systems that mix concerns. Fixing requires refactoring.
Documentation debt: Missing or outdated documentation. Systems that only one person understands. Fixing requires writing documentation or knowledge transfer.
Dependency debt: Dependencies on third-party libraries that are outdated, unmaintained, or poorly chosen. Fixing requires upgrading or replacing.
Process debt: Processes that were shortcuts but became permanent. Manually running deployment scripts instead of automating them. Fixing requires process improvement.
Pressure to ship: When there's urgency, people cut corners. Tests are skipped. Code reviews are rushed. Documentation is deferred. These shortcuts create debt.
Growth without refactoring: As the codebase grows, patterns that worked for 10,000 lines don't work for 100,000 lines. Without refactoring, the architecture becomes misaligned with the system's actual needs.
Team turnover: When people leave, the tacit knowledge they had is lost. The code they wrote might become less maintainable because fewer people understand it. This is a form of documentation debt.
Technology change: Technologies that were best-in-class become outdated. Dependencies that were maintained become abandoned. Technology debt accumulates.
Unclear ownership: Code with no clear owner often deteriorates. No one feels responsible for improving it, so technical debt grows.
Technical debt costs compound:
Direct costs: Development is slower. Features take longer to ship. "What would take 1 week in a clean codebase takes 2 weeks in our codebase."
Quality costs: With low test coverage and poorly organized code, bugs are more common. Support burden increases. Customer satisfaction decreases.
Hiring costs: Strong engineers avoid working in codebases with high technical debt. Your hiring costs increase because fewer people want to work on your product.
Risk costs: Changes become riskier. A refactoring that should be straightforward takes weeks because of coupling. An outage that should take 30 minutes to fix takes 4 hours because code is hard to understand.
Opportunity costs: Time spent fighting technical debt is time not spent on new features. You ship fewer new features because the team is constantly fighting technical problems.
1. Measure it. You can't manage what you don't measure. What's your test coverage? How complex is the code? How often do changes cause regressions? Start tracking these metrics.
2. Make it visible. When technical debt is invisible, it doesn't get prioritized. Make it visible: "We have 2 million lines with 15% test coverage. Industry benchmark is 60%. That's a gap."
3. Prioritize strategically. Not all technical debt is equal. Pay down debt that's blocking progress. "This system is slowing us down. Let's refactor." Ignore debt that isn't affecting you. "That code is complex but we rarely change it. Leave it."
4. Balance shipment and debt paydown. Allocate a portion of each sprint to debt paydown. Maybe 20% of capacity goes to technical improvements, 80% to new features. Adjust based on urgency.
5. Prevent new debt. For every piece of new code, decide: are we taking on debt (shortcut now, refactor later), or are we building cleanly? Track these decisions. Over time, you'll see how much debt you're taking on.
6. Build gradually. Ship features incrementally. Refactor as you go. This prevents large-scale technical debt accumulation.
Technical debt isn't always bad. Sometimes taking on debt is the right call:
Market urgency: A competitor is shipping a feature. You ship faster (with debt) and gain market share. Paying down the debt later is worth it.
Exploration: You're not sure if an idea will work. Build fast (with debt), learn, then decide. If it doesn't work, the debt doesn't matter. If it does, then refactor.
Learning: A new engineer is learning the codebase. Code they write might not be optimal. Accepting that debt while they learn is the right trade-off.
The key: Make the choice consciously. "We're taking on debt because of X reason. We'll pay it down by Y date." Know the cost.
"Technical debt is always bad." No. Some debt is strategic. The problem is: unconscious debt (taking it on without knowing the cost) or unlimited debt (never paying it down).
"Good code avoids technical debt." Good code manages technical debt. You can't avoid it entirely. The question is: do you make conscious choices about when to take it on, or does it accumulate unconsciously?
"We can refactor everything." You can't. Refactoring has costs (time, risk of introducing bugs). Prioritize refactoring for systems that matter most to your business.
"Technical debt is a technical problem." It's a business problem. High technical debt = slower shipping = fewer features = lower revenue potential. This should be visible to product leadership.
Q: How much technical debt is acceptable? A: Depends on your business. A startup might accept higher debt (ship fast, iterate). An enterprise might accept less (stability matters more). What matters: you know the level of debt you have and the cost it's imposing.
Q: How do we convince leadership to let us refactor? A: Show the cost of not refactoring: "Our development velocity is declining. We estimated this feature at 4 weeks; it's taking 6 weeks due to technical debt in the payment module. Refactoring that module would make future work faster." Make it a business case.
Q: What's the difference between technical debt and legacy code? A: Legacy code is old code. Technical debt is code (old or new) that has costs. Legacy code might have high technical debt (hard to understand, poorly tested). New code might also have technical debt (hastily written shortcuts).
Keep reading
Related resources