Erros Comuns em Go (Golang) que Você Deve Evitar e Como Corrigi-los

Erros Comuns em Go (Golang) que Você Deve Evitar e Como Corrigi-los






Erros comuns em Go: o que você deve evitar


Go / Boas práticas

Erros comuns em Go: o que você deve evitar

Eu, como programador sênior e autor do Yurideveloper, compartilho neste post
um guia direto ao ponto sobre armadilhas frequentes em Go e como evitá-las com
padrões simples e eficaz.

1) Nil, zero value e inicialização indevida

Go trata mapas, slices e ponteiros de forma diferente entre zero value e valor inicial. Muitos erros surgem quando esperamos que um mapa ou slice já esteja pronto para uso.

  • Declarar var m map[string]int sem fazer make: o mapa permanece nil e leituras/escritas falham com runtime panic.
  • Conferir apenas a presença de chave sem considerar o estado do mapa/slice pode levar a resultados inesperados.
  • Não inicializar colunas de dados antes de enviar para canais, bancos ou APIs locais pode trazer concorrência imprevisível.
Ilustração de Go

Práticas recomendadas: sempre inicialize estruturas com make/new ou use literals quando possível. Emite checagens antes de operações com mapas e slices para evitar panics.

2) Ponteiros, interfaces e nil

Erros comuns aparecem quando confundimos nil de uma interface com nil de um ponteiro ou quando manipulamos ponteiros sem checagem adequada.

  • Interface nil não é o mesmo que valor concreto; verificar apenas se err != nil não cobre casos com interfaces vazias.
  • Dereferenciar ponteiros sem inicialização pode levar a panic. Use checagens claras ou construtores/validações antes de usar o ponteiro.
  • Quando retornar interfaces, confirme se o tipo implementa o método esperado ou use type assertions com checagem segura.

Dicas rápidas: prefira valores concretos quando possível e mantenha a hierarquia de erros clara para facilitar o debug.

3) Concorrência mal gerida

Go facilita a concorrência, mas demandas de sincronização, cancelamento e compartilhamento de dados exigem disciplina para evitar race conditions e deadlocks.

  • Goroutines sem sincronização podem atualizar estados compartilhados de forma não determinística.
  • Não fechar canais corretamente pode deixar receivers esperando indefinidamente ou provocar failed sends.
  • Contextos e deadlines ajudam a manter a aplicação responsiva sob carga ou operações bloqueantes.

Boas práticas: utilize mutexes quando necessário, prefira canais para comunicação entre goroutines e propagate cancelamentos com context.Context.

4) Tratamento de erros: não subestime o contexto

O tratamento de erros em Go frequentemente é subutilizado ou mal executado. Embutir contexto ao erro facilita o diagnóstico sem perder a run atual.

  • Ignorar erros, especialmente em chamadas de IO, bancos ou rede, leva a falhas silenciosas.
  • Não embrulhar erros com contexto dificulta entender a causa raiz quando o fluxo retorna a níveis mais altos.
  • Comparações diretas com erros específicos sem usar errors.Is/ errors.As reduz a flexibilidade frente a wrapping de erros.
// Exemplo: embrulhar com contexto e usar wrap
package main

import (
  "errors"
  "fmt"
)

func loadConfig() (string, error) {
  return "", errors.New("arquivo não encontrado")
}

func main() {
  if cfg, err := loadConfig(); err != nil {
    // embrulha com contexto
    err = fmt.Errorf("loadConfig falhou: %w", err)
    fmt.Println(err) // output: loadConfig falhou: arquivo não encontrado
    return
  } else {
    fmt.Println("config loaded:", cfg)
  }
}

Sugestão: adote um padrão de retorno de erro claro com contexto, e utilize errors.Is / errors.As quando precisar de checagens específicas.