🧹Refatoração de Código em Machine Learning: Por Que Apenas Funcionar Não é Suficiente?

🧹 Clean Code em Machine Learning: Por Que “Funcionar” Não é Suficiente?

Quando um modelo funciona, mas o código ao redor começa a virar um problema.

Olá, mundo! 👋

Quando estamos aprendendo Machine Learning, é muito fácil comemorar quando chegamos naquele momento mágico:

Modelo treinado ✅
Métrica boa ✅
Previsão funcionando ✅

Pronto. Podemos fechar o notebook e comemorar?

Nem sempre. 😅

Um projeto de Machine Learning pode apresentar excelentes resultados e, ainda assim, possuir um código difícil de entender, testar e modificar.

E esse problema aparece justamente quando o projeto começa a crescer.

Neste artigo, vamos falar sobre Clean Code, refatoração e qualidade de código em projetos de Machine Learning, mostrando por que organizar o código não é apenas uma questão de estética, mas parte importante da engenharia de ML.


🧠 Quando “funciona” começa a não ser suficiente

Imagine que você criou um notebook para prever churn.

No começo, tudo parece simples:

Carregar dados
      ↓
Limpar dados
      ↓
Treinar modelo
      ↓
Avaliar
      ↓
Prever

Você executa as células e tudo funciona.

Depois de alguns dias, surge uma nova necessidade:

“Precisamos adicionar uma nova variável.”

Você altera o código.

Depois:

“Precisamos testar outro modelo.”

Mais alterações.

Depois:

“Precisamos colocar isso em uma API.”

E então aparece aquele momento em que você olha para o notebook e pensa:

“Quem escreveu isso?”

A resposta é:

Você. 😂

Esse é um dos motivos pelos quais a qualidade do código começa a ganhar importância conforme um experimento se transforma em projeto.


🏗️ Notebook é ótimo para explorar. Produção é outra história.

O notebook é uma ferramenta excelente para exploração.

Ele permite testar rapidamente uma hipótese, visualizar dados, experimentar modelos e entender o comportamento de um problema.

O problema não está no notebook.

O problema acontece quando um código criado para experimentação passa a ser utilizado como se fosse uma aplicação pronta para produção.

Um notebook pode acabar concentrando:

  • Importação de bibliotecas;
  • Leitura dos dados;
  • Limpeza;
  • Feature engineering;
  • Treinamento;
  • Avaliação;
  • Gráficos;
  • Testes;
  • Predições;
  • Salvamento do modelo.

Tudo no mesmo lugar.

Enquanto estamos explorando, isso pode funcionar.

Mas quando precisamos reutilizar uma etapa específica, testar uma função ou colocar o modelo atrás de uma API, a situação muda.

É aí que entra a refatoração.


🔧 O que é refatoração?

Refatorar significa melhorar a estrutura interna do código sem alterar o comportamento esperado do sistema.

Pense em uma casa.

Você pode reorganizar os cômodos, melhorar a instalação elétrica e remover coisas desnecessárias sem mudar o endereço da casa.

Com código é parecido.

A ideia é melhorar a estrutura sem mudar aquilo que o sistema deveria fazer.

Em Machine Learning, uma refatoração pode significar:

  • Separar o pré-processamento;
  • Criar funções menores;
  • Remover código duplicado;
  • Melhorar nomes de variáveis;
  • Separar treinamento de inferência;
  • Organizar os módulos;
  • Facilitar a criação de testes.

O objetivo não é transformar um projeto simples em uma arquitetura gigantesca.

É fazer com que ele continue compreensível conforme cresce.


👃 Code Smells: o cheiro estranho do código

Existe uma expressão bastante utilizada em desenvolvimento de software:

Code Smell.

Não significa necessariamente que o código esteja errado.

É mais parecido com aquele cheiro estranho que aparece na cozinha.

Você ainda não sabe exatamente o que aconteceu, mas sabe que vale a pena investigar. 😂

Em Machine Learning, alguns sinais comuns são:

🧱 Funções gigantes

Uma única função carrega os dados, trata valores ausentes, cria features, treina o modelo e calcula as métricas.

Se algo der errado, descobrir onde está o problema pode virar uma investigação criminal.

🔁 Código duplicado

O mesmo pré-processamento aparece em três notebooks diferentes.

Funciona?

Sim.

É uma boa ideia?

Provavelmente não.

Se uma regra mudar, você terá que lembrar de alterar todos os lugares.

🤔 Nomes que não explicam nada

x1 = ...
x2 = ...
temp = ...
resultado2 = ...

Talvez você saiba o que significa hoje.

Daqui a três meses?

Boa sorte. 😅

Um nome como:

clientes_cancelamento

normalmente comunica muito mais do que:

df2

🌎 Variáveis globais

Uma função depende de uma variável criada muito antes, em outra parte do notebook.

Funciona enquanto você executa tudo na ordem certa.

Mas tente reutilizar aquela função em outro lugar.

Surpresa. 💥


🧹 A Regra dos Escoteiros

Existe uma ideia simples que gosto bastante:

Deixe o código um pouco melhor do que você encontrou.

Você não precisa parar um projeto inteiro para fazer uma grande refatoração.

Imagine que você precisa alterar uma função.

Antes de terminar, percebe que existe uma variável com nome ruim.

Você pode melhorar.

Depois percebe que existe uma pequena duplicação.

Pode remover.

Depois percebe que uma função está fazendo duas coisas diferentes.

Pode separar.

Pouco a pouco, o projeto vai melhorando.

É uma abordagem muito mais realista do que esperar o momento perfeito para “refatorar tudo”.


🧠 Clean Code não é escrever mais código

Esse é um ponto importante.

Código limpo não significa necessariamente código maior.

Às vezes acontece exatamente o contrário.

Veja um exemplo simplificado:

def processar_cliente(cliente):
    # valida cliente
    # transforma dados
    # carrega modelo
    # faz previsão
    # salva resultado
    # registra log

Essa função está fazendo várias coisas.

Uma organização melhor poderia separar responsabilidades:

def validar_cliente(cliente):
    ...

def preparar_dados(cliente):
    ...

def prever(modelo, dados):
    ...

def registrar_resultado(resultado):
    ...

Agora cada parte possui uma responsabilidade mais clara.

Isso facilita:

entender → testar → alterar → reutilizar

E essa sequência é extremamente importante quando estamos construindo sistemas de Machine Learning.


🤖 O problema fica ainda maior em Machine Learning

Em aplicações tradicionais, podemos testar facilmente:

entrada → função → saída

Em Machine Learning, existe uma cadeia muito maior:

Dados
 ↓
Limpeza
 ↓
Transformação
 ↓
Features
 ↓
Modelo
 ↓
Predição
 ↓
Regra de decisão
 ↓
Resultado

Se uma etapa estiver mal organizada, pode ser difícil descobrir onde surgiu um problema.

E existe um detalhe ainda mais importante:

o código pode funcionar e mesmo assim produzir um resultado estatisticamente incorreto.

Um exemplo clássico é o data leakage.

Imagine que você faça uma transformação utilizando toda a base antes de separar treino e teste.

O código executa.

O modelo treina.

A métrica aparece.

Tudo parece perfeito.

Mas existe informação do conjunto de teste influenciando o processo de treinamento.

O código funciona.

O experimento é que está comprometido.

Por isso, qualidade de código e qualidade estatística estão muito mais relacionadas do que parece.


🧪 Refatoração precisa de uma rede de segurança

Aqui entra outro conceito importante:

testes.

Imagine que você refatorou uma função responsável pelo pré-processamento.

Como saber se a alteração realmente preservou o comportamento esperado?

Você pode executar novamente todo o projeto e comparar os resultados.

Mas quanto maior o projeto, menos eficiente essa abordagem se torna.

É aí que entram testes automatizados.

Por exemplo:

Entrada válida → resultado esperado
Entrada inválida → erro esperado
Cliente válido → previsão válida
Categoria inexistente → rejeição

Quanto melhor for a cobertura das partes críticas, maior será a segurança para realizar mudanças.

E isso muda completamente a relação com a refatoração.

Em vez de:

“Será que essa alteração quebrou alguma coisa?”

Você começa a ter:

“Vou alterar e deixar os testes me avisarem se algo mudou.”

Muito melhor. 😎


🔄 Refatorar sem quebrar o modelo

Uma boa estratégia é fazer mudanças pequenas.

Imagine que você encontrou uma função de 150 linhas.

Não precisa acordar numa segunda-feira e decidir:

“Hoje vou reescrever o projeto inteiro.”

Esse é um excelente jeito de criar novos problemas. 😂

Uma abordagem mais segura seria:

1. Identificar o problema

Encontrar a função ou trecho que está difícil de manter.

2. Registrar o comportamento atual

Verificar entradas, saídas e métricas.

3. Fazer uma mudança pequena

Por exemplo, extrair uma parte da função.

4. Executar os testes

Garantir que o comportamento esperado continua funcionando.

5. Comparar os resultados

No caso de ML, isso pode incluir métricas, previsões e comportamento do pipeline.

6. Repetir

Uma melhoria de cada vez.

Assim, uma grande refatoração se transforma em várias pequenas mudanças controladas.


📊 Código limpo também ajuda na reprodutibilidade

Imagine dois projetos de Machine Learning.

Projeto A

notebook_final.ipynb
notebook_final_v2.ipynb
notebook_final_v2_corrigido.ipynb
notebook_final_v2_corrigido_agoraVai.ipynb

😂

Projeto B

src/
├── preprocessing.py
├── training.py
├── predict.py
└── evaluation.py

models/
tests/
data/

A segunda estrutura não garante que o projeto seja perfeito.

Mas deixa muito mais claro onde cada responsabilidade está.

Isso facilita a reprodução de experimentos, a manutenção e a colaboração com outras pessoas.


🚀 Do código experimental para a engenharia de ML

Esse é talvez o ponto principal.

Existe uma diferença entre:

“Consegui treinar um modelo.”

e

“Consegui construir uma solução que outras pessoas conseguem entender, testar e utilizar.”

Quando um projeto começa a sair do notebook, outras preocupações aparecem:

NecessidadeO que ajuda
Código organizadoModularização
ReutilizaçãoFunções e componentes
ConfiabilidadeTestes
ReprodutibilidadePipelines e versionamento
DeployAPI e container
ManutençãoClean Code
MonitoramentoLogs e métricas
EvoluçãoRefatoração contínua

É nesse ponto que Machine Learning começa a se aproximar cada vez mais da engenharia de software tradicional.


⚠️ Cinco erros que vale evitar

1. “Se funciona, não mexe.”

Funcionar hoje não significa ser fácil de manter amanhã.

2. Colocar tudo no notebook

O notebook é excelente para exploração, mas nem sempre é a melhor estrutura para uma aplicação.

3. Copiar e colar código

A duplicação parece rápida no começo e pode virar um problema depois.

4. Refatorar tudo de uma vez

Mudanças pequenas são mais fáceis de testar e reverter.

5. Ignorar testes

Sem testes, cada refatoração vira uma aposta.


📋 Checklist rápido

Antes de considerar seu código pronto para evoluir, vale perguntar:

  • Cada função possui uma responsabilidade clara?
  • Os nomes das variáveis são compreensíveis?
  • Existe código duplicado?
  • O pré-processamento está organizado?
  • Treinamento e inferência estão separados?
  • Existem testes para as partes críticas?
  • O pipeline pode ser reproduzido?
  • É fácil entender onde uma alteração precisa ser feita?
  • O código evita dependências desnecessárias?
  • Outra pessoa conseguiria entender o projeto sem você explicar tudo?

Se várias respostas forem “não”, talvez seja hora de uma pequena refatoração.


🎯 Por que isso importa?

Um projeto de Machine Learning não é apenas o modelo.

Existe todo um ecossistema ao redor dele:

          DADOS
            ↓
      PRÉ-PROCESSAMENTO
            ↓
         MODELO
            ↓
       AVALIAÇÃO
            ↓
        PREDIÇÃO
            ↓
           API
            ↓
         PRODUÇÃO

E cada etapa precisa ser compreensível e confiável.

Um modelo excelente dentro de um código impossível de manter pode acabar sendo muito menos útil do que um modelo um pouco mais simples dentro de uma solução bem estruturada.

No final, a pergunta não deveria ser apenas:

“Meu modelo funciona?”

Talvez a pergunta mais importante seja:

“Outra pessoa consegue entender, testar e evoluir o que eu construí?”

Porque Machine Learning Engineering não é apenas fazer o modelo funcionar.

É construir algo que continue funcionando quando o notebook fecha.


🚀 Conclusão

Refatoração não é jogar o código fora e começar novamente.

É melhorar continuamente aquilo que já existe.

Uma função menor aqui.

Um nome melhor ali.

Um teste novo.

Uma duplicação eliminada.

Uma responsabilidade separada.

Pequenas mudanças podem parecer insignificantes isoladamente, mas fazem uma grande diferença quando o projeto cresce.

No fim, Clean Code não é sobre escrever código perfeito.

É sobre tornar o código compreensível, testável e preparado para mudar.

E talvez a melhor forma de resumir tudo seja:

Um modelo que funciona é um experimento.
Um modelo que pode ser mantido, testado e evoluído é engenharia.
🚀


📌 Tech Legacy

Este conteúdo faz parte da série de aprendizados sobre Machine Learning Engineering, Clean Code e MLOps, trazendo conceitos técnicos para situações práticas de desenvolvimento.

TechLegacy: Transformando código em legado! 👨‍💻

Deixe um comentário