technical-debt-management

Curated resources to complement your reading
A velocidade da sua equipe caiu 60% em 6 meses. Features simples agora levam 3 semanas. Todo deploy quebra algo. Desenvolvedores passam mais tempo corrigindo bugs do que construindo funcionalidades.
Sua dívida técnica está esmagando vocês.
Enquanto isso, Shopify faz mais de 100 deploys por dia com 5.000 engenheiros. Stripe mantém 99,999% de uptime mesmo crescendo rapidamente. GitHub refatorou seu monolito sem parar o desenvolvimento de features.
Todos gerenciam dívida técnica sistematicamente.
Não evitando—isso é impossível. Medindo, priorizando e pagando estrategicamente enquanto mantêm a velocidade de entrega.
Este artigo apresenta o playbook que reduz a carga de manutenção em 67%, aumenta a frequência de deploys em 3x e previne o temido "reescrever do zero".
// ❌ Você não consegue ver o problema
class OrderService {
processOrder(order: any) {
// 847 linhas de if/else aninhados
if (order.type === 'standard') {
if (order.country === 'BR') {
if (order.total > 100) {
if (order.shippingMethod === 'express') {
// ... 200+ linhas de profundidade
}
}
}
}
// Complexidade ciclomática: 94 (deveria ser <10)
// Cobertura de testes: 12%
// Última modificação: 47 vezes em 3 meses
// Mas ninguém sabe disso!
}
}
Custos ocultos:
O problema: Equipe não rastreia complexidade, cobertura ou frequência de mudanças. Dívida é invisível até causar incidentes.
# Backlog de dívida técnica (nunca priorizado)
TODO: Refatorar UserService (adicionado há 2 anos)
TODO: Adicionar testes ao PaymentController (há 18 meses)
TODO: Remover API v1 deprecada (há 1 ano)
TODO: Atualizar dependências (há 6 meses)
# Realidade: Dívida cresce 23% por trimestre
Efeito dos juros compostos:
Ponto de ruptura: Eventualmente dívida > desenvolvimento de features. Equipe propõe "reescrever do zero" (R$10M+, timeline de 18 meses).
Sprint Planning:
✅ Feature A: 8 story points
✅ Feature B: 5 story points
✅ Feature C: 3 story points
❌ Dívida técnica: 0 story points
Resultado:
- Velocidade neste sprint: 16
- Velocidade próximo sprint: 14 (-12%)
- Velocidade em 6 meses: 6 (-62%)
Espiral da morte:
Dados reais: Equipes que alocam 0% de tempo para dívida perdem 8-12% de velocidade por trimestre.
// ✅ Tornar dívida visível com métricas
interface CodeHealthMetrics {
complexity: {
average: number; // Meta: <10
max: number; // Meta: <15
filesOver20: number; // Meta: 0
};
coverage: {
statements: number; // Meta: >80%
branches: number; // Meta: >75%
criticalPaths: number; // Meta: >95%
};
duplication: {
percentage: number; // Meta: <3%
duplicatedLines: number;
duplicatedBlocks: number;
};
dependencies: {
outdated: number; // Meta: 0 versões major atrasadas
vulnerabilities: number; // Meta: 0 high/critical
unused: number; // Meta: 0
};
churn: {
highChurnFiles: string[]; // Arquivos alterados >10x/mês
avgChangesPerFile: number;
};
}
// Exemplo de saída
const metrics: CodeHealthMetrics = {
complexity: {
average: 12.3, // ⚠️ Acima da meta
max: 47, // 🔴 Crítico
filesOver20: 12 // 🔴 Crítico
},
coverage: {
statements: 45, // 🔴 Crítico
branches: 32, // 🔴 Crítico
criticalPaths: 67 // 🔴 Crítico
},
duplication: {
percentage: 8.2, // ⚠️ Acima da meta
duplicatedLines: 3400,
duplicatedBlocks: 147
},
dependencies: {
outdated: 23, // ⚠️ Moderado
vulnerabilities: 5, // 🔴 Crítico (2 high, 3 medium)
unused: 8 // ⚠️ Baixa prioridade
},
churn: {
highChurnFiles: [
'src/services/OrderService.ts', // Alterado 34x em 3 meses
'src/utils/validation.ts' // Alterado 28x em 3 meses
],
avgChangesPerFile: 3.2
}
};
Ferramentas para medição:
// ✅ Converter métricas em impacto no negócio
interface DebtCost {
timeToAddFeature: number; // horas
bugFixTime: number; // horas
deployFrequency: number; // por semana
incidentRate: number; // por mês
engineerSatisfaction: number; // escala 1-10
}
// Antes da redução de dívida
const before: DebtCost = {
timeToAddFeature: 120, // 3 semanas
bugFixTime: 33.6, // 4,2 dias
deployFrequency: 1, // Semanal
incidentRate: 8, // 8 incidentes/mês
engineerSatisfaction: 4.2 // Moral baixo
};
// Cálculo do custo anual
const engineerCost = 180_000; // Custo anual (salário + benefícios)
const teamSize = 10;
const hoursPerYear = 2080;
const debtCostPerYear = {
// Tempo perdido em desenvolvimento lento
slowFeatures: (120 - 40) * 52 * teamSize * (engineerCost / hoursPerYear),
// = R$360K/ano
// Tempo perdido em correção de bugs
slowBugFixes: (33.6 - 8) * 48 * teamSize * (engineerCost / hoursPerYear),
// = R$106K/ano
// Resposta a incidentes
incidents: 8 * 4 * teamSize * (engineerCost / hoursPerYear),
// = R$28K/ano
// Rotatividade (custo de substituição ~150% do salário)
turnover: 2 * engineerCost * 1.5,
// = R$540K/ano
total: 1_034_000 // R$1M/ano desperdiçado com dívida
};
Insight chave: Dívida técnica custa ~R$100K/engenheiro/ano em tempo desperdiçado e rotatividade.
// ✅ Quadrante de Dívida de Fowler + pontuação Impacto/Esforço
interface DebtItem {
title: string;
type: 'imprudente-deliberada' | 'prudente-deliberada' |
'imprudente-inadvertida' | 'prudente-inadvertida';
impact: 1 | 2 | 3 | 4 | 5; // 5 = maior
effort: 1 | 2 | 3 | 4 | 5; // 5 = mais esforço
score: number; // impacto / esforço
}
const debtBacklog: DebtItem[] = [
{
title: 'Adicionar testes ao PaymentController (0% cobertura)',
type: 'imprudente-deliberada', // Entregue sem testes
impact: 5, // Caminho crítico, bugs frequentes
effort: 2, // Direto de testar
score: 5 / 2 // = 2.5 (ALTA PRIORIDADE)
},
{
title: 'Refatorar OrderService (847 linhas, complexidade 94)',
type: 'imprudente-inadvertida', // Cresceu organicamente
impact: 5, // Bloqueia todas as features de pedido
effort: 4, // Refatoração significativa
score: 5 / 4 // = 1.25 (MÉDIA PRIORIDADE)
},
{
title: 'Atualizar React 17 → 18',
type: 'prudente-deliberada', // Upgrade planejado
impact: 2, // Bom ter (concurrent mode)
effort: 3, // Trabalho de migração necessário
score: 2 / 3 // = 0.67 (BAIXA PRIORIDADE)
},
{
title: 'Corrigir vulnerabilidade crítica de segurança na autenticação',
type: 'imprudente-inadvertida', // Não sabíamos da CVE
impact: 5, // Risco de breach
effort: 1, // Patch disponível
score: 5 / 1 // = 5.0 (CRÍTICO - FAZER AGORA)
}
];
// Ordenar por score (decrescente)
const prioritized = debtBacklog.sort((a, b) => b.score - a.score);
Ordem de prioridade:
// ✅ Regra dos 20% (modelo Spotify, Google)
interface SprintAllocation {
totalCapacity: number;
featureWork: number;
debtWork: number;
debtPercentage: number;
}
const sprint: SprintAllocation = {
totalCapacity: 50, // story points
featureWork: 40, // 80%
debtWork: 10, // 20%
debtPercentage: 20
};
// Quais itens de dívida cabem em 10 pontos?
const sprintDebtItems = [
{ title: 'Corrigir vulnerabilidade auth', points: 1 }, // Crítico
{ title: 'Adicionar testes PaymentController', points: 5 }, // Alto impacto
{ title: 'Extrair classe OrderValidator', points: 3 }, // Início do refactor
{ title: 'Atualizar 3 dependências críticas', points: 1 } // Segurança
// Total: 10 pontos
];
Resultados após 3 meses:
Descoberta contraintuitiva: Gastar 20% do tempo em dívida aumenta a velocidade de features porque o código fica mais fácil de modificar.
// ✅ Deixe o código melhor do que encontrou
// Antes: Você está adicionando um novo método de pagamento
class PaymentProcessor {
process(order: any) { // Encontrado: tipo any, sem validação
if (order.paymentMethod === 'card') {
// 100 linhas de lógica de cartão
} else if (order.paymentMethod === 'paypal') {
// 100 linhas de lógica PayPal
}
// Você precisa adicionar PIX aqui
}
}
// Depois: Refatorar enquanto adiciona feature
interface Order {
paymentMethod: PaymentMethod;
amount: number;
currency: string;
}
interface PaymentStrategy {
process(order: Order): Promise<PaymentResult>;
}
class PaymentProcessor {
private strategies: Map<string, PaymentStrategy>;
async process(order: Order): Promise<PaymentResult> {
const strategy = this.strategies.get(order.paymentMethod);
if (!strategy) {
throw new Error(`Método de pagamento desconhecido: ${order.paymentMethod}`);
}
return await strategy.process(order);
}
}
// Adicionar PIX agora é trivial
class PixStrategy implements PaymentStrategy {
async process(order: Order): Promise<PaymentResult> {
// Limpo, isolado, testável
}
}
Impacto: Melhoria gradual a cada feature. Sem tempo dedicado para refatoração.
// ✅ Substituir sistema legado gradualmente
// Monolito legado
class LegacyOrderService {
createOrder(data: any) { /* 500 linhas */ }
updateOrder(id: any, data: any) { /* 300 linhas */ }
cancelOrder(id: any) { /* 200 linhas */ }
// ... mais 20 métodos
}
// Passo 1: Criar novo serviço para UM método
class OrderService {
async createOrder(order: CreateOrderDTO): Promise<Order> {
// Implementação limpa com tipos, testes, validação
const validated = await this.validate(order);
const created = await this.repository.create(validated);
await this.eventBus.publish(new OrderCreatedEvent(created));
return created;
}
}
// Passo 2: Facade roteia para novo ou antigo
class OrderServiceFacade {
constructor(
private newService: OrderService,
private legacyService: LegacyOrderService
) {}
createOrder(data: any): Promise<Order> {
// Roteia para novo serviço
return this.newService.createOrder(data);
}
updateOrder(id: any, data: any) {
// Ainda usando legado
return this.legacyService.updateOrder(id, data);
}
}
// Passo 3: Migrar um método por sprint
// Sprint 2: Migrar updateOrder
// Sprint 3: Migrar cancelOrder
// ...
// Sprint 10: Deletar LegacyOrderService completamente
Timeline: 10 sprints para substituir completamente. Sem reescrita "big bang". Features entregues continuamente.
## Planejamento Trimestral
- Sprint 1-3: Normal (80% features, 20% dívida)
- Sprint 4: Sprint de Dívida (100% redução de dívida)
- Sprint 5-7: Normal (80% features, 20% dívida)
- Sprint 8: Sprint de Dívida (100% redução de dívida)
## Sprint 4 (Sprint de Dívida) Objetivos
1. ✅ Reduzir complexidade média de 12,3 → <10
2. ✅ Aumentar cobertura de 45% → 65%
3. ✅ Eliminar 15 arquivos de alta complexidade
4. ✅ Atualizar todas as dependências (23 desatualizadas)
5. ✅ Remover 3.400 linhas duplicadas
Resultados:
Antes do gerenciamento de dívida:
T1: 50 pontos/sprint
T2: 43 pontos/sprint (-14%)
T3: 35 pontos/sprint (-30%)
T4: 26 pontos/sprint (-48%)
Depois com orçamento de 20% para dívida:
T1: 40 pontos/sprint (20% em dívida)
T2: 44 pontos/sprint (+10%)
T3: 48 pontos/sprint (+20%)
T4: 52 pontos/sprint (+30%)
Insight: Investir 20% do tempo em dívida aumenta a velocidade líquida em 30%.
Antes:
Depois:
Resultado: Entrega de features 70% mais rápida em média.
Antes:
Depois:
Economia anual: R$276K apenas em resposta a incidentes.
Empresa: Plataforma e-commerce, 25 engenheiros, R$250M faturamento anual
Antes (T1 2025):
Intervenção (T2 2025):
Semana 1-2: Medição
Semana 3-4: Priorização
T2-T4 2025: Execução
Resultados (T1 2026, 12 meses depois):
ROI:
✅ Dívida é localizada (<30% do codebase) ✅ Arquitetura é fundamentalmente sólida ✅ Equipe tem capacidade (20% do tempo disponível) ✅ Negócio tolera melhoria gradual
❌ Dívida é pervasiva (>60% do codebase) ❌ Arquitetura é fundamentalmente quebrada (tech stack errada, paradigma errado) ❌ Custo de manutenção > custo de reescrita ❌ Segurança/compliance impossível de alcançar
Reescritas famosas:
Aviso: Reescritas falham 70% das vezes. Só considere se realmente necessário.
Dívida técnica não é ruim. Dívida técnica não gerenciada é ruim.
Principais aprendizados:
Comece segunda-feira: Instale o SonarQube. Meça sua dívida. Até sexta, você terá um backlog priorizado e cálculo de ROI.
Se você não consegue medir, não consegue gerenciar. E se não consegue gerenciar, ela vai gerenciar você.
Sr Software Engineer
Tech debt invisível afunda projetos. Descubra como quantificar, priorizar e pagar dívida técnica de forma sistemática sem parar desenvolvimento de features.
47-point checklist to catch bugs, security risks, and performance issues before launch.
Production-tested templates trusted by developers. Save weeks of setup on your next project.
Modular packages for founders and engineering leads. Every engagement includes diagnosis, documentation, and direct access.
2 advisory slots for Q2