Refatoração: quando parar de adicionar features e começar a limpar o código
1. O Ciclo da Dívida Técnica: Como a Pressão por Features Acumula Bagunça
1.1 O paradoxo da produtividade
No início de um projeto, cada nova feature parece simples. O código é enxuto, as dependências são poucas e a equipe entrega rapidamente. Com o tempo, porém, a base de código cresce e a velocidade de entrega diminui. Esse é o paradoxo da produtividade: quanto mais features você adiciona sem refatorar, mais lentas se tornam as entregas futuras.
1.2 Sinais clássicos de código "podre"
Três indicadores são particularmente perigosos:
- Complexidade ciclomática alta: métodos com muitas ramificações condicionais
- Duplicação de código: a mesma lógica replicada em múltiplos lugares
- Acoplamento excessivo: uma classe depende de dezenas de outras para funcionar
1.3 A metáfora da dívida
Ward Cunningham comparou a dívida técnica a uma dívida financeira. Adiar a refatoração é como pagar apenas os juros: você continua entregando, mas cada vez mais devagar. Se não pagar o principal, o sistema quebra.
2. Indicadores Objetivos: Quando o Código Grita por Refatoração
2.1 Métricas de código
Três métricas são essenciais para decisões objetivas:
- Coesão (Cohesion): uma classe deve ter uma única responsabilidade. Baixa coesão indica que ela faz muitas coisas diferentes
- Acoplamento (Coupling): classes com muitas dependências externas são frágeis a mudanças
- Complexidade de McCabe: mede o número de caminhos independentes em um método. Valores acima de 10 são um alerta
2.2 Cheiros de código (Code Smells)
Alguns padrões são facilmente identificáveis:
- God Class: uma classe que faz tudo
- Long Method: métodos com mais de 30 linhas
- Shotgun Surgery: uma mudança simples exige alterações em vários lugares
2.3 A regra do Boy Scout
A regra é simples: "Deixe o código mais limpo do que você o encontrou." Isso se torna obrigatório quando:
- Você precisa modificar um trecho para adicionar uma feature
- O tempo para entender o código já supera o tempo para implementar a mudança
- A equipe gasta mais de 30% do sprint apenas entendendo código existente
3. O Dilema do "Feature Freeze": Parar ou Não Parar?
3.1 Refatoração contínua vs. sprints dedicados
A refatoração contínua é mais sustentável: a cada alteração, você melhora um pequeno trecho. Sprints dedicados de limpeza podem ser necessários quando a dívida técnica está crítica, mas exigem negociação com stakeholders.
3.2 Negociando com stakeholders
Para convencer a equipe de negócios, use argumentos quantitativos:
- "Este trecho de código está causando 40% dos bugs reportados"
- "Refatorar reduzirá o tempo de entrega da próxima feature em 50%"
- "O custo de manutenção atual é 3x maior que o custo de refatorar"
3.3 A técnica do Strangler Fig
Em vez de parar completamente as features, você pode aplicar o padrão Strangler Fig: gradualmente, substitua partes do sistema legado por novas implementações, sem interromper o fluxo de entregas.
// Exemplo de Strangler Fig: substituir gradualmente
// Versão antiga (legado)
public class PagamentoAntigo {
public void processar(double valor) {
// lógica antiga e complexa
}
}
// Nova implementação (substituição gradual)
public class PagamentoNovo {
public void processar(double valor) {
// lógica nova e simplificada
}
}
// Roteador que direciona gradualmente
public class PagamentoRouter {
public void processar(double valor) {
if (featureToggle.isAtivo("novo-pagamento")) {
new PagamentoNovo().processar(valor);
} else {
new PagamentoAntigo().processar(valor);
}
}
}
4. Estratégias Práticas de Refatoração Sem Riscos
4.1 Operações atômicas seguras
As operações mais seguras são pequenas e testáveis:
- Extrair método: transformar um bloco de código em um método separado
- Renomear variável: dar nomes que expressem a intenção
- Mover classe: realocar funcionalidade para a classe correta
4.2 Testes como rede de segurança
Nunca refatore sem testes. Se o código não tiver cobertura, escreva testes de caracterização primeiro:
// Teste de caracterização antes de refatorar
public class CalculadoraTest {
@Test
public void testSoma() {
// Teste que captura o comportamento atual
int resultado = new Calculadora().soma(2, 3);
assertEquals(5, resultado);
}
}
4.3 Refatoração orientada a padrões
Substituir condicionais aninhadas por polimorfismo é uma das refatorações mais poderosas:
// Antes: condicionais aninhadas
public double calcularFrete(String tipo, double peso) {
if (tipo.equals("normal")) {
return peso * 1.5;
} else if (tipo.equals("expresso")) {
return peso * 3.0;
} else if (tipo.equals("internacional")) {
return peso * 5.0 + 10.0;
}
return 0;
}
// Depois: polimorfismo com Strategy
public interface FreteStrategy {
double calcular(double peso);
}
public class FreteNormal implements FreteStrategy {
public double calcular(double peso) { return peso * 1.5; }
}
public class FreteExpresso implements FreteStrategy {
public double calcular(double peso) { return peso * 3.0; }
}
public class FreteInternacional implements FreteStrategy {
public double calcular(double peso) { return peso * 5.0 + 10.0; }
}
5. Quando a Refatoração NÃO é a Resposta
5.1 Refatorar código que será descartado
Se um trecho de código será substituído em breve, refatorá-lo é desperdício. A pergunta-chave é: "Este código ainda estará aqui daqui a 3 meses?"
5.2 A tentação da reescrita total
Reescrever do zero raramente funciona. O novo sistema geralmente replica os mesmos erros, e o custo de migração é subestimado. A abordagem incremental é quase sempre melhor.
5.3 Refatoração sem testes
Refatorar sem testes é como fazer cirurgia com os olhos vendados. Se você não pode verificar se o comportamento foi preservado, está apenas quebrando o código de forma mais organizada.
6. O Papel da Cultura de Equipe na Manutenção do Código Limpo
6.1 Code reviews focados em qualidade
Code reviews devem ir além da lógica: questione a coesão, o acoplamento e a complexidade. Pergunte: "Este método poderia ser extraído?" e "Esta classe tem responsabilidade demais?"
6.2 Responsabilidade coletiva
A regra do Boy Scout deve ser um compromisso da equipe, não individual. Quando todos melhoram o código que tocam, a dívida técnica diminui continuamente.
6.3 Automatizando a detecção
Ferramentas como linters e analisadores estáticos devem rodar no CI/CD:
# Configuração de CI para detectar code smells
# .github/workflows/quality.yml
name: Code Quality
on: [push]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run linter
run: npm run lint
- name: Run complexity analysis
run: npx complexity-report src/
7. Checklist Decisório: Um Fluxo para Saber se é Hora de Refatorar
7.1 Perguntas-chave
Antes de refatorar, responda:
- Este código será alterado nos próximos 3 meses?
- A refatoração reduzirá o tempo de entrega da próxima feature?
- Existem testes que cobrem este trecho?
- A refatoração pode ser feita em pequenos passos?
7.2 Matriz risco vs. retorno
Priorize trechos com:
- Alto impacto na produtividade da equipe
- Baixo risco de quebra (boa cobertura de testes)
- Alta frequência de alterações
7.3 Exemplo prático
// Código analisado: método com complexidade alta
public class ProcessadorPedidos {
public void processar(Pedido pedido) {
if (pedido.getTipo().equals("VIP")) {
if (pedido.getValor() > 1000) {
// lógica para VIP com valor alto
} else {
// lógica para VIP com valor baixo
}
} else if (pedido.getTipo().equals("normal")) {
if (pedido.getValor() > 500) {
// lógica para normal com valor alto
} else {
// lógica para normal com valor baixo
}
} else {
// lógica padrão
}
}
}
// Decisão: refatorar usando polimorfismo
// Motivos:
// - Este código é alterado a cada sprint (alta frequência)
// - Complexidade ciclomática = 4 (acima do ideal)
// - Existem testes unitários que cobrem todos os cenários
// - A refatoração pode ser feita em 3 passos seguros
Referências
- Refactoring Guru — Refatoração: guia completo — Catálogo completo de técnicas de refatoração com exemplos práticos e diagramas
- Martin Fowler — Refactoring: Improving the Design of Existing Code — O livro clássico sobre refatoração, com catálogo de técnicas e princípios
- Microsoft Docs — Code Metrics — Documentação oficial sobre métricas de código como complexidade ciclomática e profundidade de herança
- SonarSource — Code Smells: The Complete Guide — Guia prático para identificar e corrigir cheiros de código com ferramentas de análise estática
- Robert C. Martin — Clean Code: A Handbook of Agile Software Craftsmanship — Princípios fundamentais para escrever código limpo e manter a qualidade ao longo do tempo
- Atlassian — Technical Debt: A Complete Guide — Guia prático para gerenciar dívida técnica em equipes ágeis, com estratégias de comunicação com stakeholders