O que é refatoração de código e quando vale a pena refatorar

Refatorar não é reescrever, e confundir as duas coisas é o jeito mais rápido de quebrar um sistema que estava funcionando. A refatoração muda a estrutura interna do código sem mudar o que ele faz para quem usa: mesma entrada, mesma saída, só que com nomes melhores, funções menores e menos repetição. Reescrever do zero é outra decisão, bem mais arriscada.
$ ls ./neste-artigo
- Refatorar antes de adicionar uma funcionalidade nova
- Refatorar código que ninguém vai tocar de novo
- Refatoração num projeto sem nenhum teste automatizado
- Refatoração no meio de uma entrega com prazo apertado
- Quando o código “cheira mal” e você não sabe por onde começar
- Os passos curtos que evitam quebrar o que funciona
Martin Fowler, autor do livro que popularizou o termo, define refatoração como uma mudança feita na estrutura interna do software para deixá-lo mais fácil de entender e mais barato de modificar, sem alterar seu comportamento observável. A definição está no artigo Definition Of Refactoring, de Martin Fowler, e o detalhe que pesa está no fim da frase: se o comportamento mudou, aquilo não foi refatoração.
Quando vale a pena refatorar? A resposta muda bastante conforme a situação em que você está. Abaixo, cinco cenários comuns na vida de um dev júnior, cada um com uma recomendação diferente.
Refatorar antes de adicionar uma funcionalidade nova
Este é o cenário em que a refatoração mais se paga. Você precisa incluir o pagamento por Pix num checkout, abre o arquivo e encontra uma função de 300 linhas com cartão e boleto misturados em ifs aninhados.
Enfiar o Pix no meio dessa função funciona hoje e piora tudo amanhã. O caminho mais seguro é em dois tempos:
- Primeiro, sem mudar nada do que o checkout faz, separe cartão e boleto em funções próprias. Rode os testes.
- Depois, com a estrutura limpa, adicione a função do Pix ao lado das outras.
Kent Beck resumiu essa ideia numa frase que circula muito entre desenvolvedores: primeiro torne a mudança fácil, depois faça a mudança fácil. Os dois commits ficam separados, e quem revisa entende cada um sem esforço.
Refatorar código que ninguém vai tocar de novo
Resposta curta: não refatore.
Um script de migração que roda uma vez, um relatório de um cliente que já saiu, um módulo estável há anos que ninguém abre. Código feio que não muda não custa nada. Refatoração é investimento, e investimento só faz sentido onde vai haver retorno, ou seja, onde alguém vai ler e alterar aquele trecho de novo.
Isso contraria o instinto de quem acabou de aprender boas práticas e quer arrumar tudo o que vê. Segure o impulso e anote o problema; se o arquivo voltar a ser mexido, aí sim é a hora.

Refatoração num projeto sem nenhum teste automatizado
Aqui mora o risco maior. Sem testes, você não tem como provar que o comportamento ficou igual, e a definição de refatoração depende justamente disso.
Na prática, o que costuma funcionar é escrever primeiro os chamados testes de caracterização: testes que registram o que o código faz hoje, mesmo que pareça estranho. Você não está validando se está certo, está fotografando o comportamento atual. Com essa rede de proteção, as mudanças estruturais ficam seguras.
def test_frete_para_cep_do_interior_continua_igual():
# registra o valor que o sistema calcula hoje
assert calcular_frete("12600-000", peso_kg=2) == 31.90Se o valor de hoje está errado, isso é outro trabalho: corrigir um bug. Faça em outro commit, com outro nome, para ninguém achar que a “refatoração” mudou o preço do frete.
Em Python, a biblioteca padrão já traz o módulo unittest da documentação do Python, suficiente para começar sem instalar nada.
Refatoração no meio de uma entrega com prazo apertado
A sprint termina sexta, o código está confuso e você sente vontade de reorganizar tudo. A recomendação aqui é a regra do escoteiro, numa versão modesta: deixe o trecho que você tocou um pouco melhor do que encontrou, sem ampliar o escopo.
- Renomear uma variável
xparavalor_com_desconto: sim, custa segundos. - Extrair um bloco repetido em duas linhas para uma função: sim, se estiver no arquivo que você já está mexendo.
- Trocar a arquitetura do módulo inteiro: não agora. Registre como tarefa técnica e negocie com o time.
Refatorações grandes escondidas dentro de uma tarefa de funcionalidade transformam um pull request simples em 40 arquivos alterados que ninguém consegue revisar direito.
Quando o código “cheira mal” e você não sabe por onde começar
Fowler e Kent Beck chamaram de code smells os sinais de superfície que costumam indicar um problema mais fundo. O texto Code Smell, no site de Martin Fowler explica a ideia: o cheiro não é o problema em si, é a pista para investigar. Os mais comuns para quem está começando:
| Sinal no código | Refatoração típica | Ganho imediato |
|---|---|---|
| Função longa, que faz várias coisas | Extrair função | Cada parte ganha nome e pode ser testada sozinha |
| Mesmo bloco copiado em vários lugares | Extrair e reutilizar | Uma correção vale para todos os pontos |
Nomes como dados, temp, aux2 | Renomear | O código passa a se explicar sem comentário |
| Função com seis ou mais parâmetros | Agrupar parâmetros num objeto | Chamadas mais curtas, menos troca de ordem |
| Cadeia de if/elif por tipo de cliente | Substituir condicional por polimorfismo ou tabela | Novo tipo entra sem mexer nos antigos |
Comece sempre pelos mais baratos, renomear e extrair função. Eles resolvem uma parte surpreendente dos problemas e quase nunca quebram nada, ainda mais com as ferramentas automáticas de renomeação dos editores, que atualizam todas as referências de uma vez.
Um exemplo de antes e depois
# antes
def p(c, v):
if c == "vip":
return v - v * 0.15
return v - v * 0.05
# depois
DESCONTO_POR_TIPO = {"vip": 0.15, "comum": 0.05}
def preco_com_desconto(tipo_cliente, valor):
return valor * (1 - DESCONTO_POR_TIPO[tipo_cliente])O resultado é o mesmo para os dois tipos de cliente. O que mudou é que um novo tipo, como “atacado”, entra numa linha da tabela e não exige outro if.
E quando a refatoração é feita por uma IA?
Assistentes de código sugerem refatorações em segundos, e elas costumam parecer corretas. O risco é o mesmo de sempre, só que em velocidade maior: uma mudança “estrutural” que altera um detalhe de comportamento, como a ordem de uma lista ou o tratamento de um valor nulo. A regra continua valendo. Peça mudanças pequenas, leia o diff linha por linha e rode os testes antes de aceitar. Se não há testes, a IA não resolve o problema da seção anterior; ela só torna mais fácil cometê-lo.
Refatorar e otimizar são a mesma coisa?
Não. Otimizar busca deixar o código mais rápido ou mais econômico, e muitas vezes o deixa mais difícil de ler. Refatorar busca deixá-lo mais fácil de entender e de mudar. As duas atividades podem andar juntas, mas misturá-las no mesmo commit torna impossível saber qual delas causou um problema. Meça antes de otimizar; refatore quando a leitura estiver custando caro.
Os passos curtos que evitam quebrar o que funciona
Independentemente do cenário, a forma de refatorar é a mesma: passos pequenos, um de cada vez, com os testes rodando entre eles. Renomeou, rodou. Extraiu, rodou. Se um teste falha, você sabe exatamente qual mudança causou a falha, porque foi a última, e desfaz em segundos.
Commits pequenos ajudam pelo mesmo motivo. Um commit chamado “refatora cálculo de frete” com 15 linhas é fácil de revisar e fácil de reverter. Um commit “melhorias gerais” com 2 mil linhas não é nem uma coisa nem outra.
Se o assunto que te trouxe até aqui é organização de projetos maiores, o artigo sobre quando a arquitetura limpa compensa na web discute o passo seguinte, que é a estrutura do sistema inteiro. E para conferir outros termos citados aqui, como teste unitário e pull request, vale abrir o glossário de programação do Buildbase.
O erro a evitar a partir de hoje: chamar de refatoração qualquer mudança grande. Se o comportamento mudou, dê outro nome ao commit, e seu time vai agradecer na próxima investigação de bug.




