Erro 1: Ignorar o erro retornado
go
// RUIM
dados, _ := os.ReadFile("config.json")
process(dados) // pode estar vazio se falhou!
// BOM
dados, err := os.ReadFile("config.json")
if err != nil {
return fmt.Errorf("config: %w", err)
}
process(dados)Erro 2: Capturar variável de loop em goroutine
go
// RUIM (em Go < 1.22)
for _, item := range items {
go func() {
fmt.Println(item) // todas imprimem o mesmo!
}()
}
// BOM
for _, item := range items {
item := item // shadowing — cria nova variável a cada iteração
go func() {
fmt.Println(item)
}()
}
// AINDA MELHOR
for _, item := range items {
go func(it string) {
fmt.Println(it)
}(item)
}Desde Go 1.22, esse problema foi corrigido na linguagem. Mas se você lê código antigo, vai encontrar isso.
Erro 3: Esquecer de fechar channels
go
// RUIM — recebedor fica esperando para sempre
ch := make(chan int)
for i := 0; i < 5; i++ {
ch <- i
}
for v := range ch { // deadlock!
fmt.Println(v)
}
// BOM — fechar sinaliza fim para o range
ch := make(chan int, 5)
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
for v := range ch { // sai do loop quando ch fecha
fmt.Println(v)
}Regra: quem envia é quem fecha. Nunca feche um channel que você só recebe.
Erro 4: Usar nil map para escrita
go
// RUIM
var m map[string]int
m["a"] = 1 // panic: assignment to entry in nil map
// BOM
m := make(map[string]int)
m["a"] = 1Erro 5: Slice "compartilhando" memória inesperadamente
go
// PERIGOSO
a := []int{1, 2, 3, 4, 5}
b := a[1:3] // b aponta pra dentro de a!
b[0] = 999 // modifica a também!
fmt.Println(a) // [1 999 3 4 5]
// BOM — copie se precisar de independência
b := make([]int, 2)
copy(b, a[1:3])Erro 6: Goroutines vazando
go
// RUIM — se ninguém receber, a goroutine fica para sempre
func vazar() {
ch := make(chan int)
go func() {
ch <- 42 // bloqueia eternamente se ninguém ler
}()
// função retorna sem ler do channel
}
// BOM — use context para cancelar
func ok(ctx context.Context) {
ch := make(chan int, 1) // ou use buffer
go func() {
select {
case ch <- 42:
case <-ctx.Done():
return
}
}()
}Erro 7: panic em código de biblioteca
panic é o throw do Go, mas só deve ser usado para erros impossíveis de recuperar (estado inválido do programa, bug grave). Em código normal, retorne erros.
go
// RUIM
func DividirNumeros(a, b int) int {
if b == 0 {
panic("divisão por zero!")
}
return a / b
}
// BOM
func DividirNumeros(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("divisão por zero")
}
return a / b, nil
}Erro 8: Usar fmt.Println em produção
Use o pacote log (ou log/slog no Go 1.21+) que adiciona timestamps, níveis e formatação adequada:
go
import "log/slog"
slog.Info("usuário criado", "id", u.ID, "email", u.Email)
slog.Error("falha ao salvar", "erro", err)