Every engineering organization accumulates technical debt. The difference between healthy and unhealthy codebases is not the absence of debt but rather how deliberately it is managed. After leading debt reduction efforts across teams of 50+ engineers, here is the framework I use.
Categorizing Technical Debt
Not all technical debt is equal. I classify it into four categories, each requiring a different approach.
Deliberate and prudent: "We know this is not ideal, but shipping this week is more important. We will fix it in the next sprint." This is healthy debt with a clear repayment plan.
Deliberate and reckless: "We do not have time for tests." This is dangerous and usually stems from organizational pressure rather than engineering judgment.
Inadvertent and prudent: "Now that we understand the domain better, we realize our original design was wrong." This is inevitable and a sign of learning.
Inadvertent and reckless: "What is a bounded context?" This indicates a skills gap that needs training, not just code changes.
The Tech Debt Register
We maintain a living document that tracks technical debt items with enough context to make prioritization decisions.
# tech-debt-register.yml
items:
- id: TD-2025-042
title: "Order service uses synchronous HTTP calls to inventory"
category: deliberate-prudent
impact: high
effort: medium
area: order-service
created: 2025-03-15
description: |
When we launched the order service, we used synchronous HTTP calls
to check inventory. This creates tight coupling and cascading failures.
Should be migrated to async messaging with eventual consistency.
risks:
- Cascading failures during inventory service outages
- Increased latency under load (p99 currently 800ms)
proposed_solution: |
Replace synchronous stock checks with event-driven saga pattern.
Inventory service publishes stock events, order service maintains
local read model.
estimated_effort: "2 sprints (1 engineer)"
business_impact: "Service reliability drops to 99.2% during peak hours"
The 20% Rule
We allocate 20% of every sprint to technical debt reduction. This is not optional and not subject to negotiation with product managers. Here is how I enforce it:
- Every sprint planning includes a debt item from the register
- The tech lead selects items based on risk and proximity to current work
- Debt reduction work is tracked with the same rigor as feature work
- We report on debt metrics in sprint retrospectives
// Example: Before debt paydown - tightly coupled controller
public class OrdersController : ControllerBase
{
private readonly HttpClient _inventoryClient;
[HttpPost]
public async Task<IActionResult> PlaceOrder(PlaceOrderDto dto)
{
// Synchronous call that blocks if inventory is down
var stock = await _inventoryClient.GetFromJsonAsync<StockDto>(
$"/api/stock/{dto.ProductId}");
if (stock?.Available < dto.Quantity)
return BadRequest("Insufficient stock");
// ... create order
}
}
// After debt paydown - decoupled with local read model
public class OrdersController : ControllerBase
{
private readonly IStockReadModel _stockReadModel;
private readonly IMessageBus _messageBus;
[HttpPost]
public async Task<IActionResult> PlaceOrder(PlaceOrderDto dto)
{
var available = await _stockReadModel.GetAvailableStockAsync(dto.ProductId);
if (available < dto.Quantity)
return BadRequest("Insufficient stock");
var order = Order.Create(dto);
await _messageBus.PublishAsync(new OrderPlaced(order));
return Accepted(order.Id);
}
}
Measuring Progress
You cannot manage what you do not measure. We track these metrics monthly:
- Debt item count by category and severity
- Mean time to resolve debt items
- Deployment frequency as a proxy for codebase health
- Incident correlation linking outages to known debt items
- Developer satisfaction via quarterly surveys
Getting Buy-In from Leadership
The biggest challenge is not identifying or fixing technical debt. It is convincing leadership that it matters. I frame every debt item in business terms:
- "This debt item caused three incidents last quarter, each costing four hours of engineering time."
- "Reducing deployment time from 45 minutes to 10 minutes saves 300 engineering hours per year."
- "This coupling means every inventory change requires coordinated deploys, slowing feature velocity by 30%."
When leadership understands that technical debt directly impacts delivery speed, reliability, and engineer retention, the 20% allocation becomes an easy sell.
Final Thoughts
Technical debt is not a moral failing. It is a natural consequence of building software under real-world constraints. The key is to be intentional about it: track it, categorize it, allocate time to address it, and measure your progress. Treated as a first-class concern, debt management becomes a competitive advantage rather than a constant source of friction.
Comments (2)
I tried this approach and it works perfectly!
Good question, I'd like to know too.
Well written and easy to follow. Keep it up!
Leave a Comment