(On | No) Syntactic Support for Error Handling
Go team decision and process
- The blog post announces that Go will stop considering syntactic changes for error handling, after ~7 years, three official proposals, and hundreds of community ideas that never reached consensus.
- Some view this as reasonable conservatism: maintaining stability, avoiding multiple styles that would spark style wars and PR bikeshedding, and respecting Go’s “one obvious way” ethos.
- Others see it as paralysis: the team uses “no consensus” as a reason to do nothing, even though error handling repeatedly ranks as the top complaint in surveys.
- There’s debate whether the real issue is lack of agreement on whether change is needed at all, versus inability to pick among many acceptable alternatives.
Ergonomics vs explicitness
- Many working Go developers say they’ve grown to like
if err != niland value its explicit control flow; verbosity “fades into the background” and helps reason about reliability. - Critics argue verbosity hurts readability: code becomes 30–60% error boilerplate, interleaving “happy path” with trivial pass-through checks, making logic harder to follow and review.
- A recurring theme: developers want syntactic sugar for the extremely common pattern “call, check, return/wrap error”, without changing semantics or removing explicit handling.
Proposals and why they failed
- Ideas discussed or reinvented in the thread:
or {}, Rust-style?, Result/union types, Elixir-style ok/error tuples with awith/for-comprehension, monadicdo-notation, or a genericResult[T,E]with helpers. - Objections raised in the thread mirror those in the proposals:
- Hidden or implicit control flow (especially expression-level
?). - Locking in “error last” and
(T, error)as language-enforced, not just convention. - Splitting the ecosystem into two idioms and creating style fights.
- Deep incompatibility with Go’s zero-value design and lack of sum types.
- Hidden or implicit control flow (especially expression-level
- Some commenters think the team exhausted the syntax-only design space; others say they gave up before tackling deeper semantic issues (sum types, better error typing, boundaries).
Practical issues and footguns
- Multiple commenters highlight real bugs from:
- Accidentally writing
if err == nilinstead of!= nil. - Shadowing
errwith:=and effectively ignoring earlier errors. - Dropping error return values entirely; the compiler doesn’t enforce handling.
- Accidentally writing
- Lack of built-in stack traces on errors is widely disliked; Go’s answer is manual wrapping and optional libraries, which rely on human discipline.
- Some argue that current semantics (zero values alongside errors, no sum types, generic
errorinterface) make it fundamentally harder to build robust, composable error APIs.
Tooling, LLMs, and IDEs
- Several participants suggest leaning on tooling rather than new syntax:
- Linters (errcheck, staticcheck, golangci-lint) to catch ignored errors and shadowing.
- IDE folding/highlighting of
if err != nilblocks to visually de-emphasize them. - LLMs or snippets to generate repetitive checks.
- Others note writing boilerplate isn’t the main pain; reading and reviewing large amounts of near-identical error code is.
Comparisons to other languages
- Rust’s
Resultand?are often cited as a good balance: explicit, type-checked, but concise. Some point out they rely on sum types and a different type-system philosophy. - Exceptions (Java, Python, C#, PHP) are criticized for implicit control flow and unclear “what can fail here?”; defenders argue error boundaries and automatic stack traces are powerful, and Go has equivalent complexity spread across many
if errblocks. - Elixir/Erlang ok/error tuples with
with, Haskell/Scala monads, and Zig’stry/error traces are mentioned as attractive, but seen as mismatched with Go’s current design.
Language evolution and philosophy
- Broader debate emerges: is Go’s extreme conservatism (generics took ~13 years, modules ~9, error syntax now frozen) a strength or slow path to irrelevance?
- Supporters say Go’s stability and simplicity are its core value; rapid feature accretion leads to C++/Java-style complexity.
- Critics worry that refusing to modernize ergonomics—especially around error handling and nil safety—will push new developers toward other languages, even if existing users adapt.