Fundamentos de Refactoring
Definição de Fowler
Seção intitulada “Definição de Fowler”Martin Fowler (“Refactoring: Improving the Design of Existing Code”, 2ª ed., 2018) dá duas definições complementares:
- Substantivo: uma transformação nomeada que preserva o comportamento (ex.: Extract Function).
- Verbo: reestruturar software aplicando uma série dessas transformações sem mudar o comportamento observável.
A palavra-chave é comportamento observável. Você reorganiza a estrutura interna — nomes, decomposição, hierarquia — enquanto as entradas produzem exatamente as mesmas saídas e efeitos externos. Se o comportamento muda, não é refactoring: é adição de feature ou correção de bug. Fowler insiste em separar esses dois chapéus — o “chapéu de refactoring” e o “chapéu de adicionar feature” — e nunca usá-los ao mesmo tempo.
A rede de testes como pré-requisito
Seção intitulada “A rede de testes como pré-requisito”Isso amarra este módulo a Test-Driven Development e a Tipos de Teste - Unit, Integration e E2E. Os testes de unidade rápidos são os que dão o feedback em segundos necessário para o loop apertado de refactoring; testes de integração e E2E protegem contornos maiores. As propriedades desejáveis desses testes estão em Princípio FIRST — sobretudo Fast, para que rodar a suíte a cada passo não seja um fardo.
flowchart TD
A[Testes verdes] --> B[Aplicar 1 refactoring pequeno]
B --> C[Rodar testes]
C -->|verde| D[Commit]
C -->|vermelho| E[Reverter passo]
D --> A
E --> A
O ciclo é apertado de propósito: se a suíte quebra, você desfaz um único passo pequeno, não uma hora de trabalho.
Refactoring x reescrita
Seção intitulada “Refactoring x reescrita”Refactoring é uma sequência de passos pequenos e reversíveis, cada um deixando o sistema funcionando. Reescrita joga fora o código existente e o reconstrói — arriscada, cara e sem garantia de paridade de comportamento. A reescrita se justifica quando o código é tão degradado que o custo de entendê-lo supera o de recriá-lo, ou quando a plataforma mudou. Mas, na dúvida, refatore: você mantém o sistema entregável a cada instante.
Refactoring contínuo x “sprint de refactoring”
Seção intitulada “Refactoring contínuo x “sprint de refactoring””Fowler defende o refactoring oportunista e contínuo: você limpa o código à medida que passa por ele para implementar features (a Boy Scout Rule de Clean Code). O modelo oposto — reservar um “sprint de refactoring” trimestral — costuma ser sintoma de dívida acumulada e tende a virar reescrita disfarçada, sem valor de negócio entregue no intervalo. Refactoring saudável é invisível no roadmap porque está diluído em cada tarefa.
O passo Refactor do ciclo TDD
Seção intitulada “O passo Refactor do ciclo TDD”No ciclo Red-Green-Refactor de Test-Driven Development (Kent Beck), o refactoring tem um lugar canônico e agendado:
flowchart LR
R["RED<br/>teste falha"] --> G["GREEN<br/>faz passar<br/>(código feio ok)"]
G --> RF["REFACTOR<br/>limpa com testes verdes"]
RF --> R
O passo Green permite código sujo, contanto que passe. O passo Refactor é onde você remove duplicação e melhora nomes — com a segurança de que a suíte acabou de ficar verde. TDD, portanto, produz a rede de testes e agenda a limpeza no mesmo fluxo. A relação mais ampla entre projeto testável e boa arquitetura está em Testabilidade e Arquitetura.
Exemplo: refactoring em passos seguros
Seção intitulada “Exemplo: refactoring em passos seguros”Antes: lógica duplicada e nome opaco
Seção intitulada “Antes: lógica duplicada e nome opaco”public class RelatorioFinanceiro { public double calcular(List<Lancamento> lancamentos) { double total = 0; for (Lancamento l : lancamentos) { if (l.getTipo() == Tipo.CREDITO) { total += l.getValor() * 1.0; } } for (Lancamento l : lancamentos) { if (l.getTipo() == Tipo.DEBITO) { total -= l.getValor() * 1.0; } } return total; }}Depois: mesmo comportamento, estrutura limpa
Seção intitulada “Depois: mesmo comportamento, estrutura limpa”Cada passo abaixo foi seguido de execução da suíte de testes, mantendo-a verde:
public class RelatorioFinanceiro { public double calcular(List<Lancamento> lancamentos) { return somarCreditos(lancamentos) - somarDebitos(lancamentos); }
private double somarCreditos(List<Lancamento> lancamentos) { return somarPorTipo(lancamentos, Tipo.CREDITO); }
private double somarDebitos(List<Lancamento> lancamentos) { return somarPorTipo(lancamentos, Tipo.DEBITO); }
private double somarPorTipo(List<Lancamento> lancamentos, Tipo tipo) { return lancamentos.stream() .filter(l -> l.getTipo() == tipo) .mapToDouble(Lancamento::getValor) .sum(); }}Quando NÃO refatorar
Seção intitulada “Quando NÃO refatorar”- Código que será descartado — não invista em limpar um módulo que já tem data de remoção.
- Ausência de testes cobrindo o trecho — primeiro escreva testes de caracterização (que fixam o comportamento atual), depois refatore. Refatorar sem rede é arriscar regressão silenciosa.
- Prazo crítico com o chapéu de feature — não misture refactoring com a entrega urgente; separe os commits.
- Reescrita já decidida — se o módulo será reconstruído do zero por outra razão, refatorá-lo é esforço perdido.
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Refatorar sem rodar os testes a cada passo — você perde a capacidade de localizar qual passo quebrou.
- Passos grandes demais — misturar dez transformações num commit torna a reversão inviável.
- Misturar chapéus — mudar comportamento e estrutura no mesmo commit esconde bugs.
- Chamar reescrita de “refactoring” — vender um rewrite arriscado sob o rótulo seguro do refactoring.
- “Sprint de refactoring” — postergar limpeza para uma fase isolada em vez de fazê-la continuamente.
Relações
Seção intitulada “Relações”- Método da tríade: Clean Code e Refactoring
- Meta a atingir: Clean Code
- Diagnóstico: Code Smells
- Catálogo de transformações: Técnicas de Refactoring
- Rede de segurança: Test-Driven Development · Tipos de Teste - Unit, Integration e E2E · Test Doubles - Stub, Spy, Mock e Fake · Princípio FIRST · Testes e TDD
- Projeto testável: Testabilidade e Arquitetura
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. Qual a definição precisa de refactoring segundo Fowler?
Resposta
Alterar a estrutura interna do código sem mudar seu comportamento observável — as mesmas entradas produzem as mesmas saídas e efeitos externos. Como substantivo é uma transformação nomeada que preserva comportamento; como verbo é aplicar uma série dessas transformações.
2. Por que a rede de testes é pré-requisito e não opcional?
Resposta
Porque a única forma de comprovar que uma mudança de estrutura preservou o comportamento é reexecutar testes auto-verificáveis que capturam esse comportamento. Sem eles, “refactoring” vira editar e torcer, com risco de regressão silenciosa. Testes de unidade rápidos são essenciais pelo loop apertado.
3. Qual a diferença entre refactoring e reescrita, e quando cada um se justifica?
Resposta
Refactoring é uma sequência de passos pequenos e reversíveis que mantém o sistema funcionando a cada instante. Reescrita descarta e reconstrói, com risco alto e sem garantia de paridade. Reescrita só se justifica quando o código está degradado demais para ser entendido ou a plataforma mudou; na dúvida, refatore.
4. Onde o refactoring se encaixa no ciclo TDD e por que é seguro ali?
Resposta
No passo Refactor do ciclo Red-Green-Refactor. O passo Green tolera código feio contanto que passe; o passo Refactor limpa esse código com a suíte recém-verde servindo de rede. TDD produz os testes e agenda a limpeza no mesmo fluxo.