Você já passou horas refatorando um código que funcionava perfeitamente, só pra deixar ele “mais bonito”? Ou pior: já evitou mexer num código legado horrível com medo de quebrar tudo? A real é que nem toda refatoração vale a pena e nem todo código feio precisa ser reescrito.
No dia a dia, tempo é dinheiro. E ficar refatorando código por ego ou por “boas práticas” sem critério é jogar grana fora. Ao mesmo tempo, ignorar código problemático pode custar muito mais caro no futuro.
Vamos direto ao ponto: como saber quando refatorar e quando deixar quieto?
Refatoração tem que ter retorno
Se você vai gastar 4 horas refatorando um código que ninguém mais vai mexer, você tá perdendo tempo. Simples assim.
Esse código é modificado frequentemente? Tá atrapalhando novas features? Tem bugs recorrentes? Novos devs ficam presos tentando entender? Se a resposta for “não”, deixa como está. Código funcionando não se mexe sem motivo.
Quando você PRECISA refatorar
Tem código feio que funciona. E tem código que é uma bomba-relógio. Aprenda a diferença:
1. Você perde muito tempo tentando entender o que o código faz
Se toda vez que você ou alguém do time precisa mexer ali, demora 15 minutos só pra entender o fluxo, tá na hora. Lembra do que falei sobre nomes honestos e Early Return no artigo de Clean Code? Aqui é a mesma coisa: se você precisa de debugger pra entender uma função simples, algo tá muito errado. Refatora agora antes que vire rotina.
2. O mesmo bug aparece várias vezes na mesma área
Código confuso gera bugs. Se você já corrigiu 3 bugs parecidos no mesmo trecho em 6 meses, o problema é estrutural. Refatora antes que apareça o quarto.
3. Você tem medo de mexer no código
Sério. Se você fica com receio de alterar uma função porque “pode quebrar alguma coisa em outro lugar”, isso é um sintoma claro de acoplamento forte demais. Refatore pra isolar responsabilidades.
4. O código duplicado tá te atrapalhando
Não é sobre DRY religioso. É sobre manutenção. Se você corrige um bug e precisa copiar o fix pra outros 5 lugares, você precisa extrair isso. Simples.
// Ruim: lógica duplicada em 3 endpoints diferentes
app.post('/user', (req, res) => {
if (!req.body.email || !req.body.email.includes('@')) {
return res.status(400).json({ error: 'Email inválido' });
}
// resto do código...
});
app.put('/user/:id', (req, res) => {
if (!req.body.email || !req.body.email.includes('@')) {
return res.status(400).json({ error: 'Email inválido' });
}
// resto do código...
});
// Bom: validação extraída
function validateEmail(email: string): boolean {
return email && email.includes('@');
}
app.post('/user', (req, res) => {
if (!validateEmail(req.body.email)) {
return res.status(400).json({ error: 'Email inválido' });
}
// resto do código...
});Código legado: mexe ou não mexe?
A regra de ouro: se tá funcionando em produção há anos e ninguém mexe, NÃO REFATORE.
Mas se você precisa adicionar features nesse código legado, aí muda o jogo:
A estratégia do Strangler Pattern
Não reescreve tudo de uma vez. Vai isolando partes aos poucos:
- Identifica a parte que você precisa mexer
- Extrai pra uma função/classe nova e limpa
- Testa bem
- Aos poucos, vai migrando o resto
// Código legado gigante e confuso
function processOrder(data: IData) {
// 300 linhas de código misturando validação,
// cálculo, salvamento no DB, envio de e-mail...
}
// Refatoração incremental - começa extraindo uma parte
function validateOrderData(data: IData): boolean {
// Lógica de validação extraída e testável
return data.items?.length > 0 && data.total > 0;
}
function processOrder(data: IData) {
if (!validateOrderData(data)) {
throw new Error('Dados inválidos');
}
// Resto do código legado continua igual por enquanto
// ...
}Aos poucos você vai quebrando aquele monstrinho em pedaços gerenciáveis.
Refatoração que é perda de tempo
Renomear variáveis só porque “não segue o padrão”
Se o código é claro, funciona e ninguém reclama, deixa. Não gasta 2 horas renomeando userInfo pra userData porque você leu que “info” é genérico demais.
Aplicar design patterns porque “é a forma correta”
Factory, Strategy, Observer… tudo lindo na teoria. Mas se você tá criando 5 classes pra fazer algo que uma função resolve, você tá complicando desnecessariamente.
Reescrever em outra linguagem/framework “porque é melhor”
A não ser que tenha um motivo técnico real (performance, manutenção impossível), não cai nessa. “Vou reescrever tudo em Rust porque é mais rápido” geralmente termina em projeto abandonado.
Refatorar pra “aprender” em produção
Quer aprender um pattern novo? Faz um side project. Código de produção não é laboratório.
Como convencer o time a refatorar
Fala em tempo e dinheiro, não em “código limpo”:
“Gastamos 8 horas esse mês debugando essa área. Se refatorarmos agora (4 horas), economizamos esse tempo todo mês.”
Ou:
“Cada nova feature nesse módulo demora 2x mais que deveria porque o código é confuso. Refatorando, velocidade de entrega aumenta.”
Gestor entende ROI. Mostra o número.
Pragmatismo > purismo
Código perfeito não existe. Código que funciona, traz valor e é mantível, existe.
Refatore quando faz sentido, não quando é bonito. No final, o melhor código é aquele que entrega valor pro usuário final. Se o usuário tá feliz e o código não tá atrapalhando o time, tá valendo. Nem todo código precisa ser uma obra de arte.