How we centralized and structured error handling in Golang
Centralized error package proposal
- Many see the article’s “god error package” as over-centralizing domain knowledge and tightly coupling services.
- Critics argue errors are part of each service’s API; only very low-level, cross-cutting concerns (HTTP, protocols) should be global.
- Some feel it effectively reimplements an error “language” or exceptions inside Go, adding complexity and indirection.
- A few defend centralization for shared schemas/standards, but think the article’s concrete design is too heavy.
Go’s error model vs alternatives
- Strong sentiment that Go’s
(T, error)style is clumsy and encourages boilerplate, especially for composing multiple calls. - Calls for sum types /
Result-like types and a?-style operator; Rust is often cited as a better realization of “errors as values.” - Others defend Go’s simplicity and explicitness, preferring visible error handling over “magic” monadic or exception-based flows.
- Debate over exceptions: some see them as better for default bubbling; others argue they’re harder to reason about, especially without checked exceptions or effect types.
HTTP status codes vs application errors
- Strong pushback against conflating internal errors with HTTP status codes or centrally mapping all errors to HTTP in a low level.
- Many argue HTTP should reflect transport/request handling (200/400/401/403/404/500 etc.), while application semantics live in the body.
- Some advocate extreme minimalism (always 200, errors in body); others highlight loss of monitoring, tooling, and infrastructure benefits.
- Consensus trend: use a small, pragmatic subset of HTTP codes plus structured JSON error payloads.
Error context, structure, and logging
- Broad agreement that plain strings without context are inadequate in large systems.
- Techniques mentioned: wrapping with
%w, sentinel/custom error types, attaching structured metadata (key–value pairs), and stack traces via logging libraries. - Some stress adding context mainly at subsystem boundaries rather than every helper function.
Fail-fast vs graceful degradation
- One camp: treat violated invariants as bugs, assert/fail fast, and avoid running in an unknown state.
- Opposing view: crashing entire processes (especially servers) for a single bad request is unacceptable; isolate failures per request/goroutine and recover.
- General recognition that distinguishing “bug” vs “user/input/environment error” is crucial to choosing between aborting, retrying, or degrading gracefully.
Idiomatic Go vs “imported patterns”
- Multiple comments warn against “writing Java/Scala/Rust in Go” with heavyweight frameworks and non-idiomatic abstractions.
- Others counter that Go’s minimalism sometimes forces people to reinvent missing features in ad hoc, inconsistent ways.