result-vs-exceptions
Result types vs. exceptions
A code-review lens for the question: should this failure be a Result<T, E> or a throw?
The convention in this repo: recoverable, expected failures return a Result. Unrecoverable bugs throw.
Why this matters
Exceptions are invisible in the type signature. A function that throws looks pure from the outside — the caller has no compiler-enforced reminder that failure is possible, no list of failure kinds, no exhaustiveness check. That's fine for "the runtime is broken" failures (out-of-memory, broken invariants, programmer mistakes), because no caller can sensibly recover and the crash is the right behavior.
It's the wrong default for failures that are part of normal control flow. Invalid user input, validation issues, parse errors, "not found" lookups, expected network outcomes (4xx, rate limits) — these are not exceptional. They are predictable branches of the API. Hiding them inside try/catch puts business logic in the catch block, lets callers forget the failure path entirely, and conflates "this might fail" with "this should never happen."
Result<T, E> flips both problems: failure is part of the return type, the caller is forced by the type system to discriminate, and the set of failure kinds is enumerable.
Decision
When reviewing a new throw or a new error class, ask: