Gestione degli Errori in Go nel 2026: Pattern, Wrapping e Domande per Colloqui Tecnici
Guida completa alla gestione degli errori in Go: pattern moderni, error wrapping, errors.Is/As e domande frequenti nei colloqui tecnici per sviluppatori.

La gestione degli errori in Go distingue il linguaggio dalla maggior parte delle alternative mainstream. Invece di eccezioni e blocchi try/catch, Go tratta gli errori come valori—restituiti esplicitamente dalle funzioni e verificati in ogni punto di chiamata. Questa scelta progettuale deliberata porta la gestione degli errori in primo piano, rendendo i percorsi di fallimento visibili e testabili.
Go non dispone di un meccanismo di eccezioni. Ogni funzione che può fallire restituisce un errore come ultimo valore di ritorno. Questo pattern rende visibile a livello di codice sorgente il costo di ignorare gli errori—e rende i flussi di errore banalmente testabili.
L'Interfaccia Error e la Sua Importanza
L'intero sistema di errori di Go si basa su una singola interfaccia definita nella libreria standard:
type error interface {
Error() string
}Qualsiasi tipo che implementa Error() string soddisfa questa interfaccia. Questa semplicità favorisce la componibilità: gli errori possono essere struct, wrapper o qualsiasi altra cosa, purché producano una rappresentazione stringa.
Un tipo di errore personalizzato basilare dimostra il pattern:
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)
}Questo NotFoundError trasporta dati strutturati. I chiamanti possono estrarre il nome della risorsa o l'ID in modo programmatico invece di parsare una stringa—un vantaggio critico quando si costruiscono API HTTP o strumenti CLI che mappano gli errori a risposte specifiche.
Errori Sentinel per Condizioni Ben Definite
Gli errori sentinel sono variabili a livello di package che rappresentano condizioni di fallimento specifiche e ben note. La libreria standard utilizza questo pattern estensivamente: io.EOF, sql.ErrNoRows, os.ErrNotExist.
var (
ErrNotFound = errors.New("record not found")
ErrUnauthorized = errors.New("unauthorized access")
ErrConflict = errors.New("resource conflict")
)Gli errori sentinel funzionano meglio quando il chiamante deve sapere solo cosa è fallito, non perché in dettaglio. Segnalano una condizione senza trasportare contesto aggiuntivo. La convenzione Go prevede di prefissarli con Err e mantenere il messaggio in minuscolo senza punteggiatura, seguendo le linee guida dei Go Code Review Comments.
Esportare errori sentinel crea un contratto API pubblico. I package downstream li confronteranno con errors.Is, quindi rinominare o rimuovere un errore sentinel costituisce una breaking change. Vanno usati con parsimonia—solo per condizioni su cui i chiamanti devono effettivamente fare branching.
Error Wrapping con fmt.Errorf e il Verbo %w
L'error wrapping, introdotto in Go 1.13, aggiunge contesto a un errore preservando l'originale in una catena. Il verbo %w in fmt.Errorf crea questa catena:
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
}L'errore wrappato mantiene l'intera catena. Un chiamante tre livelli più in alto può ancora fare matching con il sentinel originale o estrarre il tipo originale. Ogni livello aggiunge contesto su dove l'errore si è verificato senza oscurare cosa è successo.
Una distinzione critica: %w wrappa (preservando la catena), mentre %v formatta l'errore come stringa e interrompe la catena. Si utilizza %v deliberatamente quando l'errore originale non deve trapelare attraverso il confine di astrazione—per esempio, quando un repository wrappa un errore del driver database che il codice del service layer non dovrebbe mai ispezionare.
Ispezione degli Errori con errors.Is ed errors.As
Il package errors fornisce due funzioni per ispezionare catene di errori wrappati, sostituendo il confronto diretto e la type assertion.
errors.Is percorre la catena cercando un valore di errore specifico:
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)
}errors.As estrae un tipo di errore specifico dalla catena:
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, ¬Found) {
return http.StatusNotFound
}
var validationErr *ValidationError
if errors.As(err, &validationErr) {
return http.StatusBadRequest
}
return http.StatusInternalServerError
}Il vantaggio chiave rispetto alle type assertion: entrambe le funzioni attraversano l'intera catena wrappata. Un NotFoundError wrappato tre volte con fmt.Errorf e %w corrisponde comunque.
Gestione Strutturata degli Errori in Applicazioni a Livelli
Le applicazioni Go in produzione tipicamente organizzano gli errori su tre livelli: gli errori di dominio definiscono condizioni a livello business, gli errori di servizio aggiungono contesto operativo, e gli errori di handler o trasporto li mappano a risposte esterne.
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
}Il metodo Unwrap è ciò che permette a errors.Is ed errors.As di funzionare attraverso la catena. Qualsiasi tipo di errore personalizzato che wrappa un altro errore dovrebbe implementarlo.
Il service layer wrappa gli errori di dominio con contesto operativo:
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)
}Pronto a superare i tuoi colloqui su Go?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Domande Frequenti nei Colloqui sulla Gestione degli Errori in Go
I colloqui tecnici per posizioni Go testano frequentemente la conoscenza della gestione degli errori. Queste domande appaiono regolarmente nelle fasi di screening e sono trattate in profondità in domande sulla gestione degli errori in Go.
Perché Go utilizza valori di errore invece delle eccezioni?
Le eccezioni creano un flusso di controllo invisibile. Una funzione che lancia trasferisce il controllo a un sito catch sconosciuto, potenzialmente molti stack frame più in là. I valori di errore di Go rendono ogni percorso di fallimento esplicito nella signature della funzione. Il chiamante decide immediatamente come gestire l'errore—riprovare, wrappare, loggare o propagare. Questo elimina il problema del "throw a sorpresa" e rende i flussi di errore leggibili senza supporto di tooling.
Qual è la differenza tra %w e %v in fmt.Errorf?
%w wrappa l'errore, preservando la catena per errors.Is ed errors.As. %v formatta l'errore come stringa, producendo un nuovo errore senza alcun collegamento alla catena dell'originale. Si usa %w quando i chiamanti dovrebbero poter ispezionare la causa; si usa %v quando la causa è un dettaglio implementativo che non deve trapelare attraverso un confine di astrazione.
Quando una funzione dovrebbe restituire un errore invece di fare panic?
Panic è riservato a situazioni veramente irrecuperabili: errori di programmazione come indice fuori limite, dereferenziazione di puntatore nil, o invarianti violate che indicano un bug. I fallimenti recuperabili—timeout di rete, record mancanti, input non valido—restituiscono errori. Una regola utile: se la condizione può verificarsi durante il normale funzionamento, restituisce un errore. Se significa che il programma ha un bug, fa panic.
Come influisce il metodo Unwrap sull'ispezione della catena di errori?
Quando un tipo di errore personalizzato implementa Unwrap() error, le funzioni errors.Is ed errors.As seguono quel metodo per attraversare la catena. Senza Unwrap, la catena si ferma a quell'errore. Da Go 1.20, gli errori possono anche implementare Unwrap() []error per restituire più errori wrappati, abilitando catene di errori ad albero per casi come l'aggregazione di errori di validazione.
Pattern di Gestione degli Errori da Evitare
Diversi anti-pattern appaiono frequentemente nelle codebase Go e nelle sessioni di code review:
Ignorare gli errori silenziosamente. L'identificatore blank rende questo sintatticamente facile ma semanticamente pericoloso:
// anti-pattern: silent error swallowing
result, _ := riskyOperation()Ogni errore ignorato dovrebbe essere una scelta deliberata e documentata—mai una scorciatoia.
Loggare e restituire. Questo pattern produce voci di log duplicate e confonde il chiamante che gestisce anch'esso l'errore:
// anti-pattern: double handling
if err != nil {
log.Printf("operation failed: %v", err)
return err
}La soluzione: o si gestisce l'errore (loggarlo, restituire un default, riprovare) oppure lo si propaga. Non entrambe le cose.
String matching per la classificazione degli errori. Controllare l'output di err.Error() con strings.Contains è fragile. I messaggi di errore non sono contratti API—possono cambiare tra versioni delle librerie. Si usa errors.Is per i valori sentinel ed errors.As per i tipi.
Wrappare errori che attraversano confini di goroutine senza sincronizzazione. Quando più goroutine producono errori concorrentemente, si utilizza errgroup.Group dal package golang.org/x/sync o un meccanismo di sincronizzazione simile.
Gestione degli Errori con errgroup per Operazioni Concorrenti
Il package errgroup da golang.org/x/sync fornisce un pattern pulito per raccogliere errori da goroutine concorrenti:
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()
}errgroup.WithContext cancella il contesto derivato quando una goroutine restituisce un errore, segnalando alle altre di fermarsi. Questo evita lavoro sprecato e fornisce un singolo errore wrappato al chiamante.
Go 1.24 (febbraio 2025) non ha modificato il package errors. L'API di gestione degli errori è stabile da Go 1.13, con l'aggiunta in Go 1.20 dell'unwrapping multi-errore tramite Unwrap() []error. Le proposte attuali per una sintassi migliorata della gestione degli errori rimangono in discussione nel Go issue tracker.
Conclusione
- Trattare gli errori come valori: restituirli esplicitamente e gestirli in ogni punto di chiamata invece di affidarsi a meccanismi di eccezione nascosti
- Usare errori sentinel (
var ErrX = errors.New(...)) solo per condizioni su cui i chiamanti devono fare branching, e trattarli come API pubblica - Wrappare gli errori con
fmt.Errorfe%wper aggiungere contesto preservando la catena; usare%vper interrompere intenzionalmente la catena ai confini di astrazione - Preferire
errors.Isa==ederrors.Asalle type assertion—entrambe attraversano l'intera catena wrappata - Implementare
Unwrap() errorsui tipi di errore personalizzati affinché l'ispezione della catena funzioni correttamente - Gestire o propagare—mai entrambi. Loggare un errore e restituirlo produce rumore duplicato
- Usare
errgroupper la raccolta concorrente di errori invece della sincronizzazione manuale delle goroutine - Mantenere i messaggi di errore in minuscolo senza punteggiatura, seguendo le convenzioni Go
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Tag
Condividi
Articoli correlati

Go Design Patterns: Pattern essenziali e domande da colloquio per sviluppatori Go
I sei design pattern Go piu importanti con codice pronto per la produzione: Functional Options, Strategy, Factory, Observer, Middleware e Struct Embedding. Con domande frequenti nei colloqui tecnici.

Go e gRPC nel 2026: Microservizi ad Alte Prestazioni e Domande da Colloquio
Tutorial completo su Go e gRPC con Protocol Buffers, streaming RPC, interceptor, pattern di produzione e domande frequenti nei colloqui per backend engineer nel 2026.

Go 1.26 Colloquio Tecnico: Green Tea GC, go fix e Ottimizzazioni dello Stack
Domande e risposte tecniche su Go 1.26 per colloqui: Green Tea garbage collector con riduzione overhead del 10-40%, nuovo strumento go fix con modernizers, allocazione slice sullo stack, leak detection delle goroutine e sicurezza post-quantistica.