Seu código Go: de "funciona" a "aprovado"
\n\nVocê escreveu um código em Go. Ele compila. Os testes estão verdes. Você está orgulhoso. Mas então chega o code review de um desenvolvedor sênior, e em cinco minutos seu pull request está coberto de comentários vermelhos. Familiar?
\n\nGo é uma linguagem onde a "simplicidade" é enganosa. Por trás da aparente facilidade, escondem-se dezenas de armadilhas que um olho experiente vê imediatamente. Neste artigo, vamos analisar os comentários mais comuns que os seniores deixam na revisão de código Go. Você aprenderá não apenas o que está errado, mas também como corrigir, para que seu código passe na revisão na primeira tentativa.
\n\nVamos analisar os erros típicos em uma revisão de código Golang.
\n\n1. Tratamento de erros: não apenas if err != nil
\n\nProblema: ignorar erros ou "return" vazio
\n\nO problema mais comum de iniciantes é o tratamento de erros pouco informativo. Simplesmente retornar o erro para cima é apenas 50% do trabalho. Um sênior sempre perguntará: "O que deu errado? Onde?".
\n\n// Ruim: o sênior dirá "adicione contexto"\nfunc ReadConfig(path string) (*Config, error) {\n data, err := os.ReadFile(path)\n if err != nil {\n return nil, err // ❌ Nenhum contexto\n }\n // ...\n}\n\n\nA abordagem correta é encapsular os erros usando fmt.Errorf e o placeholder %w (para verificação posterior com errors.Is ou errors.As).
// Bom: o sênior aprovará\nfunc ReadConfig(path string) (*Config, error) {\n data, err := os.ReadFile(path)\n if err != nil {\n return nil, fmt.Errorf("read config file %s: %w", path, err) // ✅ Contexto + cadeia\n }\n // ...\n}\n\n\nProblema: if err != nil repetitivo
\n\nGo é conhecido por sua verbosidade no tratamento de erros. Mas isso não significa que você deva aceitar copiar e colar. Um sênior sugerirá extrair verificações repetidas para helpers ou usar o padrão "errgroup" para goroutines.
\n\n- \n
- Não faça isso: 10 vezes seguidas
if err != nil { return ... }em uma função é normal, mas se o padrão se repete em funções diferentes, é hora de refatorar. \n - Dica do sênior: Use
deferpara fechar recursos e verificar erros de fechamento (sim,file.Close()também retorna um erro). \n
2. Concorrência: condições de corrida e canais incorretos
\n\nProblema: acesso a variável compartilhada sem sincronização
\n\nGo é uma linguagem onde as goroutines são baratas, mas as condições de corrida são caras. Um sênior notará imediatamente se você estiver escrevendo em um map ou slice a partir de várias goroutines sem um mutex.
\n\n// Ruim: condição de corrida\nvar counter int\nfor i := 0; i < 100; i++ {\n go func() {\n counter++ // ❌ Corrida!\n }()\n}\n\n\nSolução: usar sync.Mutex ou atômicos. Mas o sênior dirá: "Não seria mais fácil usar canais?".
// Bom: canais para sincronização\nch := make(chan int, 100)\nfor i := 0; i < 100; i++ {\n go func() {\n ch <- 1\n }()\n}\nvar counter int\nfor i := 0; i < 100; i++ {\n counter += <-ch // ✅ Sem corridas\n}\n\n\nProblema: vazamento de goroutines
\n\nEsqueceu de fechar o canal? A goroutine ficará presa para sempre. Um sênior na revisão de código Go certamente verificará se você tem uma goroutine "pendurada" sem select com ctx.Done().
// Ruim: a goroutine pode ficar presa para sempre\ngo func() {\n for msg := range ch {\n process(msg)\n }\n}()\n// Se ch nunca for fechado — vazamento de memória\n\n\nO padrão correto é sempre passar context.Context e ouvir o sinal de cancelamento.
// Bom: proteção contra vazamento\ngo func(ctx context.Context) {\n for {\n select {\n case msg, ok := <-ch:\n if !ok {\n return // canal fechado\n }\n process(m