Tratamento de Erros em Go: Padrões, Wrapping e Boas Práticas para 2026

Guia completo sobre tratamento de erros em Go: desde a interface error até padrões avançados como wrapping, sentinel errors, errors.Is e errors.As. Domine os conceitos essenciais para entrevistas técnicas e projetos profissionais.

Diagrama ilustrando padrões de tratamento de erros em Go com wrapping e hierarquia de tipos

O tratamento de erros em Go representa uma das características mais distintivas da linguagem. Diferentemente de outras linguagens que utilizam exceções, Go adota uma abordagem explícita onde os erros são valores retornados pelas funções. Essa filosofia, embora inicialmente pareça verbosa, proporciona um controle granular sobre o fluxo de execução e facilita a criação de código robusto e previsível.

Em 2026, com a consolidação do Go 1.24 e a evolução contínua do ecossistema, compreender profundamente os padrões de tratamento de erros tornou-se requisito fundamental para desenvolvedores que desejam construir aplicações escaláveis. Este artigo explora desde os fundamentos até técnicas avançadas, preparando o leitor tanto para desafios do dia a dia quanto para entrevistas técnicas.

Dica para Entrevistas

O tratamento de erros em Go constitui um dos temas mais frequentes em entrevistas técnicas. Recrutadores avaliam não apenas o conhecimento da sintaxe, mas principalmente a capacidade de aplicar padrões adequados em diferentes contextos. Dominar errors.Is, errors.As e wrapping de erros demonstra maturidade técnica.

A Interface error: Fundamento do Sistema

No coração do sistema de erros de Go encontra-se uma interface extremamente simples. Essa simplicidade proposital permite que qualquer tipo que implemente o método Error() seja considerado um erro:

builtin.gogo
type error interface {
    Error() string
}

Essa definição minimalista oferece flexibilidade extraordinária. Desenvolvedores podem criar tipos de erro personalizados que carregam informações contextuais específicas do domínio da aplicação, mantendo compatibilidade total com o ecossistema existente.

Tipos de Erro Personalizados

Quando erros simples não fornecem contexto suficiente, a criação de tipos personalizados torna-se a solução adequada. Tipos estruturados permitem incluir metadados relevantes para debugging e tratamento específico:

apperror.gogo
type NotFoundError struct {
    Resource string
    ID       string
}

func (e *NotFoundError) Error() string {
    return fmt.Sprintf("%s with ID %s not found", e.Resource, e.ID)
}

Esse padrão possibilita extrair informações do erro posteriormente, permitindo respostas HTTP adequadas ou logging estruturado. A estrutura carrega dados que seriam perdidos em uma simples string de erro.

Sentinel Errors: Valores de Erro Predefinidos

Sentinel errors representam valores de erro conhecidos e comparáveis. Definidos no nível do pacote, permitem verificações precisas em diferentes partes da aplicação:

errors.gogo
var (
    ErrNotFound     = errors.New("record not found")
    ErrUnauthorized = errors.New("unauthorized access")
    ErrConflict     = errors.New("resource conflict")
)

A convenção de nomear sentinel errors com o prefixo Err facilita sua identificação no código. Esses valores devem ser tratados como constantes, nunca modificados após a inicialização.

Error Wrapping: Preservando o Contexto

O wrapping de erros, introduzido no Go 1.13, revolucionou o tratamento de erros na linguagem. Utilizando o verbo %w no fmt.Errorf, desenvolvedores podem adicionar contexto mantendo a cadeia de erros intacta:

repository.gogo
func (r *UserRepo) FindByID(ctx context.Context, id string) (*User, error) {
    user, err := r.db.QueryContext(ctx, "SELECT * FROM users WHERE id = $1", id)
    if err != nil {
        return nil, fmt.Errorf("UserRepo.FindByID(%s): %w", id, err)
    }
    return user, nil
}

Essa técnica preserva o erro original enquanto adiciona informações sobre onde e em que contexto o erro ocorreu. O stack de chamadas fica implícito na mensagem, facilitando o debugging sem necessidade de stack traces completos.

Pronto para mandar bem nas entrevistas de Go?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Verificando Erros com errors.Is

A função errors.Is permite verificar se um erro, em qualquer ponto da cadeia de wrapping, corresponde a um valor específico. Essa função percorre toda a cadeia de erros encapsulados:

handler.gogo
func handleGetUser(w http.ResponseWriter, r *http.Request) {
    user, err := userService.GetByID(r.Context(), chi.URLParam(r, "id"))
    if errors.Is(err, ErrNotFound) {
        http.Error(w, "User not found", http.StatusNotFound)
        return
    }
    if err != nil {
        http.Error(w, "Internal error", http.StatusInternalServerError)
        return
    }
    json.NewEncoder(w).Encode(user)
}

Mesmo que ErrNotFound tenha sido wrapped múltiplas vezes nas camadas inferiores, errors.Is consegue identificá-lo. Essa capacidade elimina a necessidade de unwrap manual e comparações frágeis de strings.

Extraindo Tipos com errors.As

Quando o objetivo é extrair um tipo específico de erro da cadeia, errors.As oferece a solução. Essa função tenta fazer type assertion em cada erro da cadeia até encontrar uma correspondência:

middleware.gogo
func errorMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if r := recover(); r != nil {
                http.Error(w, "Internal error", http.StatusInternalServerError)
            }
        }()
        next.ServeHTTP(w, r)
    })
}

func mapErrorToHTTP(err error) int {
    var notFound *NotFoundError
    if errors.As(err, &notFound) {
        return http.StatusNotFound
    }
    var validationErr *ValidationError
    if errors.As(err, &validationErr) {
        return http.StatusBadRequest
    }
    return http.StatusInternalServerError
}

A combinação de errors.Is e errors.As proporciona um sistema robusto para tratamento de erros em aplicações complexas, permitindo decisões baseadas tanto em valores quanto em tipos.

Erros de Domínio: Estrutura para Aplicações Complexas

Em aplicações de grande porte, erros de domínio estruturados oferecem consistência e facilidade de manutenção. Um tipo de erro de domínio bem projetado inclui código, mensagem e suporte a wrapping:

domain/errors.gogo
type DomainError struct {
    Code    string
    Message string
    Err     error
}

func (e *DomainError) Error() string {
    if e.Err != nil {
        return fmt.Sprintf("%s: %s: %v", e.Code, e.Message, e.Err)
    }
    return fmt.Sprintf("%s: %s", e.Code, e.Message)
}

func (e *DomainError) Unwrap() error {
    return e.Err
}

O método Unwrap permite que errors.Is e errors.As percorram a cadeia de erros, mantendo a compatibilidade com o sistema padrão de Go.

Camada de Serviço: Aplicando os Padrões

Na camada de serviço, os padrões de erro convergem. O código a seguir demonstra wrapping contextual combinado com erros de domínio:

service/user.gogo
func (s *UserService) Deactivate(ctx context.Context, userID string) error {
    user, err := s.repo.FindByID(ctx, userID)
    if err != nil {
        return fmt.Errorf("deactivating user %s: %w", userID, err)
    }
    if user.Status == StatusInactive {
        return &DomainError{
            Code:    "ALREADY_INACTIVE",
            Message: fmt.Sprintf("user %s is already inactive", userID),
        }
    }
    return s.repo.UpdateStatus(ctx, userID, StatusInactive)
}

Essa abordagem proporciona mensagens de erro informativas para debugging enquanto mantém a capacidade de verificar tipos específicos nas camadas superiores.

Perguntas Frequentes em Entrevistas Técnicas

O tratamento de erros em Go aparece consistentemente em entrevistas para posições de backend. As perguntas mais comuns incluem:

Qual a diferença entre errors.Is e errors.As? errors.Is compara valores de erro, útil para sentinel errors. errors.As extrai tipos específicos de erro, permitindo acesso a campos e métodos do tipo.

Quando usar tipos de erro personalizados versus sentinel errors? Sentinel errors funcionam bem para condições simples e bem definidas. Tipos personalizados oferecem vantagens quando informações adicionais precisam ser carregadas junto ao erro.

Como implementar o método Unwrap? O método Unwrap() error deve retornar o erro encapsulado, permitindo que as funções do pacote errors percorram a cadeia.

Anti-padrões: O Que Evitar

Alguns padrões de tratamento de erros prejudicam a manutenibilidade e debugging do código. O primeiro anti-padrão é ignorar erros silenciosamente:

go
// anti-pattern: silent error swallowing
result, _ := riskyOperation()

Ignorar erros com o blank identifier dificulta a identificação de problemas em produção. Mesmo quando o erro parece irrelevante, ao menos um log deve ser registrado.

Outro anti-padrão frequente é o tratamento duplo:

go
// anti-pattern: double handling
if err != nil {
    log.Printf("operation failed: %v", err)
    return err
}

Quando o erro é logado e retornado simultaneamente, a mesma falha aparece múltiplas vezes nos logs à medida que sobe pela stack de chamadas. A recomendação é logar no ponto final de tratamento ou adicionar contexto via wrapping.

Processamento Concorrente com errgroup

Para operações concorrentes, o pacote errgroup do módulo golang.org/x/sync oferece gerenciamento elegante de erros. O grupo cancela automaticamente as goroutines restantes quando uma falha ocorre:

batch.gogo
func (s *OrderService) ProcessBatch(ctx context.Context, orderIDs []string) error {
    g, ctx := errgroup.WithContext(ctx)
    g.SetLimit(10)

    for _, id := range orderIDs {
        g.Go(func() error {
            if err := s.processOrder(ctx, id); err != nil {
                return fmt.Errorf("processing order %s: %w", id, err)
            }
            return nil
        })
    }

    return g.Wait()
}

O método SetLimit controla o paralelismo máximo, evitando sobrecarga de recursos. O contexto derivado propaga o cancelamento automaticamente para todas as goroutines do grupo.

Novidades do Go 1.24

O Go 1.24 trouxe melhorias de performance no runtime e refinamentos na biblioteca padrão. Embora o sistema de erros permaneça estável desde o Go 1.13, a comunidade continua desenvolvendo bibliotecas complementares que estendem as capacidades nativas, como suporte aprimorado a stack traces e integração com sistemas de observabilidade.

Conclusao

O tratamento de erros em Go reflete a filosofia da linguagem: explícito, simples e eficaz. Os conceitos fundamentais para dominar esse sistema incluem:

  • A interface error como base para qualquer tipo de erro personalizado
  • Sentinel errors para condições bem definidas e comparáveis
  • Tipos estruturados quando contexto adicional se faz necessário
  • Wrapping com %w para preservar a cadeia de erros
  • errors.Is para comparação de valores através da cadeia
  • errors.As para extração de tipos específicos
  • errgroup para gerenciamento de erros em operações concorrentes

Evitar anti-padrões como ignorar erros ou tratá-los duplamente mantém o código limpo e os logs úteis. Em entrevistas técnicas, demonstrar compreensão desses padrões e saber quando aplicar cada um diferencia candidatos preparados.

O investimento em compreender profundamente o tratamento de erros em Go retorna em forma de código mais robusto, debugging mais eficiente e maior confiança ao enfrentar desafios técnicos complexos.

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Tags

#go
#error-handling
#best-practices
#interview
#golang

Compartilhar

Artigos relacionados