Introdução: por que code review em Go não é apenas uma formalidade
\nCode review não é um procedimento burocrático, mas um elemento-chave da cultura de desenvolvimento. Em Go, com sua sintaxe concisa e regras rigorosas, a revisão frequentemente se torna o primeiro filtro que elimina problemas em estágio inicial. Muitas equipes que migram para Golang descobrem que velhos hábitos (por exemplo, do Python ou PHP) não funcionam: Go perdoa menos erros relacionados a tipos, mas exige atenção aos detalhes.
\nNa prática, o code review em Go resolve três tarefas: treinamento da equipe (iniciantes se adaptam mais rápido ao estilo de código), aumento da qualidade (busca de bugs e antipadrões) e consistência do código (para que o projeto não se torne uma "colcha de retalhos"). Vamos analisar exatamente o que observar e como formular observações.
\n\n1. O que verificar primeiro: da sintaxe aos erros
\nQualquer revisão começa com o básico. Em Go, existem vários "pontos críticos" que devem ser verificados automática ou manualmente.
\n\n1.1. Tratamento de erros (error handling)
\nGo não usa exceções — os erros são retornados como valores. Um problema comum: o desenvolvedor ignora o erro retornado usando _. Isso só é aceitável em casos raros (por exemplo, fmt.Println).
// Ruim: erro ignorado\nresp, _ := http.Get("https://api.example.com/data")\n\n// Bom: erro tratado\nresp, err := http.Get("https://api.example.com/data")\nif err != nil {\n log.Printf("não foi possível obter dados: %v", err)\n return err\n}\ndefer resp.Body.Close()\nO que verificar:
\n- \n
- Todos os erros foram tratados (não apenas com
_)? \n deferestá sendo usado para fechar recursos (arquivos, conexões)? \n - Erros em goroutines não estão sendo "engolidos"? \n
1.2. Nomenclatura e estilo (Go idioms)
\nGo exige concisão: camelCase para privados, PascalCase para exportados. Evite "notação húngara" e nomes longos.
// Ruim\nfunc GetUserDataFromDatabase(userID int) (*UserData, error)\n\n// Bom\nfunc GetUser(id int) (*User, error)\nBandeiras vermelhas:
\n- \n
- Nomes de variáveis como
temp,data,val(muito genéricos). \n - Funções com mais de 30-40 linhas (precisam ser divididas). \n
- Falta de comentários em funções exportadas (exigência do
golint). \n
1.3. Goroutines e sincronização
\nEsta é a parte mais complexa. Erros em código concorrente são uma fonte frequente de bugs em produção.
\n// Ruim: condição de corrida (data race)\nvar counter int\nfor i := 0; i < 100; i++ {\n go func() {\n counter++ // inseguro\n }()\n}\n\n// Bom: usando mutex\nvar mu sync.Mutex\nvar counter int\nfor i := 0; i < 100; i++ {\n go func() {\n mu.Lock()\n counter++\n mu.Unlock()\n }()\n}\nO que verificar:
\n- \n
- Há
sync.Mutexouatomicpara dados compartilhados? \n - O canal foi fechado (
close(ch))? \n - Goroutines não estão "vazando" (sem
selectcom timeout)? \n
2. Verificações arquiteturais: de interfaces a pacotes
\nApós a sintaxe básica, observamos a estrutura do código. Go incentiva composição, não herança.
\n\n2.1. Interfaces: pequenas e do lado do consumidor
\nEm Go, interfaces são implementadas implicitamente. Uma boa interface contém 1-3 métodos. Verifique se o autor não está tentando criar uma interface "abrangente".
\n// Ruim: interface enorme\ntype Storage interface {\n Save(data []byte) error\n Load(id string) ([]byte, error)\n Delete(id string) error\n List() ([]string, error)\n Connect() error\