Refatoração de código Go: quando e como não quebrar o projeto
\nVocê já abriu seu código Go antigo e sentiu uma leve nostalgia? Variáveis chamadas a e b, funções do tamanho de uma tela, e o loop principal de processamento de requisições parece um labirinto do Minotauro. Familiar? Bem-vindo ao mundo onde a refatoração não é um luxo, mas uma necessidade.
Go é famoso por sua simplicidade e velocidade de compilação, mas isso não significa que o código se escreve perfeitamente sozinho. Mais cedo ou mais tarde, todo desenvolvedor enfrenta a pergunta: "Quando é hora de organizar?". Neste artigo, vamos explorar como fazer refatoração de código Go sem medo e sem culpa, com exemplos reais e dicas de profissionais.
\n\nQuando a refatoração de código Go é necessária: bandeiras vermelhas
\nRefatoração não é sobre "reescrever tudo do zero". É sobre melhorar a estrutura interna sem alterar o comportamento externo. Aqui estão sinais claros de que é hora de pegar as ferramentas:
\n- \n
- Código cheira mal (code smell): duplicação, funções longas, números mágicos. \n
- Dificuldade em adicionar novos recursos: cada patch quebra testes antigos. \n
- Testes lentos: se os testes unitários demoram mais de 10 segundos, a estrutura está comprometida. \n
- Muitos if/else ou switch: especialmente quando novos tipos aparecem. \n
- Aninhamento excessivo:
if err != nil { if ... }— um inferno de depuração. \n
Em Go, é especialmente importante monitorar o tratamento de erros e interfaces. Se você se pega escrevendo interface{} onde um tipo específico é necessário, isso é um sinal de alerta.
Etapas da refatoração: do plano ao commit
\nRefatoração sem estratégia é um caminho para o caos. Siga um algoritmo simples:
\n- \n
- Entenda o que exatamente está errado. Escreva uma lista de problemas: duplicação, complexidade, partes lentas. \n
- Escreva testes. Sem testes, refatoração é roleta-russa. Use
go teste cubra caminhos críticos. \n - Faça pequenos passos. Um bloco lógico de cada vez. Commit após cada alteração bem-sucedida. \n
- Execute testes e benchmarks. Certifique-se de que o desempenho não caiu. \n
- Faça code review. Um olhar fresco de um colega encontrará o que você perdeu. \n
Exemplos práticos de refatoração de código Go
\nTeoria é boa, mas vamos ver código real. Abaixo estão três situações típicas e suas soluções.
\n\n1. Eliminando duplicação com funções
\nSuponha que você tenha código que analisa configuração e verifica erros da mesma forma:
\n// Ruim: duplicação da lógica de verificação\nfunc loadConfig(path string) (*Config, error) {\n data, err := os.ReadFile(path)\n if err != nil {\n return nil, fmt.Errorf("read config: %w", err)\n }\n var cfg Config\n err = json.Unmarshal(data, &cfg)\n if err != nil {\n return nil, fmt.Errorf("parse config: %w", err)\n }\n return &cfg, nil\n}\n\nfunc loadSecrets(path string) (*Secrets, error) {\n data, err := os.ReadFile(path)\n if err != nil {\n return nil, fmt.Errorf("read secrets: %w", err)\n }\n var sec Secrets\n err = json.Unmarshal(data, &sec)\n if err != nil {\n return nil, fmt.Errorf("parse secrets: %w", err)\n }\n return &sec, nil\n}\nSolução: extrair a lógica comum para uma função utilitária.
\n// Bom: método único para ler e analisar JSON\nfunc readJSON[T any](path string) (*T, error) {\n data, err := os.ReadFile(path)\n if err != nil {\n return nil, fmt.Errorf("read file %s: %w", path, err)\n }\n var result T\n err = json.Unmarshal(data, &result)\n if err != nil {\n return nil, fmt.Errorf("unmarshal %s: %w", path, err)\n }\n return &result, nil\n}\n\n// Agora