Es Go Not Use Try/catch
Fast recall
Quick revision
Core answer
Practical understanding of If Err Not Nil Pattern.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
Go's designers looked at exception systems and saw a cost they didn't want: invisible control flow. Any call in an exception language might throw — the failure path isn't in the code, it's in the documentation. Go made the opposite trade: failure is a value in the signature, and the caller must acknowledge it before using the result.
What that buys:
Auditable failure — reading a function shows its failure handling; there is no elsewhere to check.
Forced acknowledgment — unused-variable rules mean you can't silently skip the error; discarding it requires writing _, which is a visible choice.
Local reasoning — the failure and its handling are adjacent, in the same function, at the same call. No unwinding through five frames to find the handler.
What it costs: the ceremony is real. Three lines per fallible call, in every layer, forever. The Go community's own essays acknowledge this — the verbosity is the price of the visibility, and the bet is that visibility wins at maintenance time.
The design keeps panic/recover for the other kind of failure — the program is wrong, not the input — which is why the two mechanisms don't overlap: try/catch conflates expected failure with bugs; Go splits them at the language level.
// the entire mechanism:
v, err := doWork()
if err != nil {
return fmt.Errorf("context: %w", err) // visible, wrap, propagate
}
// v valid here — no catch, no throw, the check IS the handlingEs Go Not Use Try/catch
Direct Answer
Visible failure paths: signatures document what can fail, callers deal at the call site, and the compiler enforces acknowledgment. Panic is reserved for program bugs.
Deep Dive & Architecture
The historical answer is specific: Go's designers (Pike, Thompson, Griesemer — all with deep Unix/C lineage) watched 20 years of C++/Java exception practice and concluded the cost wasn't the syntax, it was the semantics: exceptions make failure paths invisible and composition unpredictable.
The invisible-path problem: any call might throw anything. API surfaces can't say what fails; docs must. Callers can't locally reason; they must know the exception graph. Java's 'catch or declare' was the strongest enforcement ever tried — and it still left RuntimeException as the un-declared back door.
The unpredictable-composition problem: handlers interact with scope and cleanup in ways that make refactoring hazardous — moving a call across a try boundary changes which handler sees it, and destructors/finally interleave with unwinding in orders that surprise even experts.
Go's replacement is deliberately boring: errors ride return values, so the type system carries the failure surface, callers read it from the signature, and cleanup rides defer (which composes linearly — LIFO at scope exit, no unwinding interleaving). The result: every function is a contract you can read in one line. The cost — verbosity — was priced in consciously.
For interviews: acknowledge the trade honestly. 'Go chose visible ceremony over invisible control flow' is fair to both sides. The strongest counterpoint is Java's checked exceptions' unpopularity; the strongest rebuttal is Go's own error-wrapping evolution (errors.Is/As and %w were added BECAUSE bare returned errors were too weak for composition) — the design iterates toward usability while keeping the visibility.
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is If Err Not Nil Pattern, and when would you use it?
- When would you use If Err Not Nil Pattern in a real project?
- How would you compare If Err Not Nil Pattern with an alternative?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.