Technical debt is the implied cost of rework caused by choosing an expedient solution now instead of a better approach that would take longer. Some debt is deliberate and necessary. Some is accidental neglect. Knowing the difference determines when to pay debt down and when to accept it.
What technical debt is
Technical debt is not bad code. It is the future cost of changes made today. Debt accumulates when shortcuts are taken to ship faster, or when knowledge improves but old code remains unchanged.
Deliberate debt
Deliberate debt is a conscious trade-off: ship now with known limitations to validate an idea or meet a deadline. Teams recognize the debt and plan to address it later.
Accidental debt
Accidental debt results from insufficient knowledge, changing requirements, or neglect. Accidental debt is discovered later when it slows development.
How technical debt accumulates
Debt grows when teams prioritize features over maintenance, lack time for refactoring, or work without architectural vision.
Shortcuts under pressure
Deadlines incentivize shortcuts: skipping tests, hardcoding values, duplicating logic. Shortcuts become debt when not revisited.
Outdated patterns
Codebases outlive the patterns they were built on. New language features, libraries, and practices emerge. Old code becomes debt when it no longer fits current standards.
Incomplete migrations
Migrations that are started but never finished create inconsistency: two authentication systems, two logging libraries, mixed naming conventions. Incomplete migrations compound maintenance costs.
The cost of technical debt
Debt slows development, increases defect rates, and demoralizes teams.
Slower development
High-debt codebases require more time to understand and modify. Simple changes require touching multiple files. Fear of breaking things slows velocity.
Increased defects
Complex, inconsistent code produces more bugs. Fixes in one area break another. Testing becomes harder.
Team morale
Working in high-debt codebases frustrates engineers. Engineers spend time fighting the system instead of building features. Talented engineers leave.
When to address technical debt
Not all debt requires immediate attention. Prioritize debt that actively hinders progress.
Debt slowing feature development
If a codebase area is frequently modified and each change is slow or risky, pay down debt there first. Refactor before the next feature, not in isolation.
Debt causing incidents
Debt that causes production issues or security vulnerabilities must be addressed immediately. Fragile deployment scripts, missing error handling, and hardcoded secrets create operational risk.
Debt blocking scale
Architectural debt that prevents scaling (monolithic structure, synchronous processing, single database) requires refactoring before growth demands it.
When to live with technical debt
Some debt is acceptable. Paying down all debt is not always the best use of time.
Stable code that rarely changes
Code that works and is not frequently modified does not need refactoring. Stability is valuable.
Experimental features
Features being validated do not need production-grade implementation. If the feature is removed or reworked, refactoring effort is wasted.
End-of-life systems
Systems scheduled for replacement should not receive major refactoring investment. Maintain them minimally until retirement.
Paying down technical debt
Incremental refactoring
Refactor in small steps alongside feature work. Large refactoring projects stall. The "boy scout rule" — leave code better than you found it — compounds over time.
Dedicated refactoring time
Reserve time for debt reduction: 20% of sprint capacity, one week per quarter, or dedicated refactoring sprints. Make debt visible in planning.
Write tests first
Before refactoring, add tests. Tests provide safety and verify behavior is preserved.
Preventing technical debt
Code reviews
Code reviews catch shortcuts before they become debt. Reviewers ask: "Will this be easy to change later?"
Document trade-offs
When taking shortcuts, document them. Leave TODO comments, file tickets, or add architectural decision records (ADRs). Future engineers need context.
Refactoring as part of feature work
Normalize refactoring. Refactoring should not require permission or justification. It is part of professional software development.
Technical debt is a tool
Technical debt is not inherently bad. It is a trade-off. Deliberate debt accelerates learning and delivery. Accidental debt and neglected debt slow teams down. Pay down debt that hinders progress. Accept debt in stable or short-lived code. For related concepts, see What Done Means.
Published by the DSSS Engineering Team. For corrections or topic requests, use the contact page.