Error Handling — Professional¶
At professional level, focus on this question:
How should teams adopt and operate Error Handling with measurable outcomes and limited coordination?
Use the smallest realistic scenario that exposes the decision and its failure behavior.¶
Core Concepts¶
1. A shared error-kind vocabulary across services¶
If every service invents its own Kind enum with different names for the same underlying concepts (NotFound vs Missing vs 404Error), engineers debugging a multi-service incident pay a real cognitive tax translating between them. A shared internal library defining a small, common set of error kinds — adopted org-wide — removes that translation cost.
2. Code review conventions for error handling¶
A lightweight, written checklist reviewers apply consistently:
- Is every error checked, not discarded?
- Is wrapping done with
%w, preserving inspectability? - Is the error classified (client vs. server fault) correctly?
- Does a client-facing error leak internal detail?
- Is a request/trace ID attached before logging?
Without an explicit checklist, error-handling quality varies by reviewer attention and mood — codifying it produces consistent outcomes.
3. Postmortems should trace back to the error-handling decision, not just the symptom¶
"The database connection pool exhausted" is a symptom. The professional-level postmortem asks: was there a retry loop that didn't back off? Was a transient error incorrectly classified as fatal (or vice versa)? Was a context deadline missing, letting a slow query hold a connection indefinitely? The structural fix usually lives in the error-handling/retry logic, not just in scaling the pool.
4. Error-handling debt accumulates like any other technical debt¶
A codebase where errors are inconsistently wrapped, sometimes discarded, sometimes logged three times as they propagate, is a form of technical debt that specifically taxes incident response speed — the worst possible time for that tax to be due. Treating error-handling consistency as a tracked quality metric (e.g., via a linter checking for discarded errors, or an errcheck/staticcheck gate in CI) keeps the debt from silently accumulating.
5. Teaching the client/server fault distinction early¶
Engineers new to distributed systems often don't intuitively separate "the caller did something wrong" from "we failed to serve a valid request" — both feel like "an error happened." Making this distinction explicit and central in onboarding material, and enforcing it via the shared error-kind vocabulary (concept 1), pays off in every alerting decision made afterward.
Code Examples¶
Example 1 — Enforcing error checking in CI¶
# .golangci.yml (excerpt)
linters:
enable:
- errcheck
- wrapcheck # flags errors returned without wrapping
Example 2 — A shared error-kind package used org-wide¶
// company.com/pkg/apperr
type Kind int
const (
KindNotFound Kind = iota
KindInvalidInput
KindUnauthorized
KindUnavailable
KindInternal
)
Every service imports this instead of defining its own enum — a small, deliberate act of standardization with outsized payoff during cross-service incidents.
Best Practices¶
- Publish and mandate a shared error-kind vocabulary across services, rather than letting each team invent its own.
- Enforce error-checking and wrapping conventions with linters (
errcheck,wrapcheck,staticcheck) in CI. - Include an error-handling checklist in code review guidelines, applied consistently.
- Require postmortems for error-handling-rooted incidents to identify the structural fix, not just patch the symptom.
Edge Cases & Pitfalls¶
- Rolling out
errcheckon a large existing codebase can surface hundreds of pre-existing violations — plan a staged rollout (new code only, then a scheduled cleanup) rather than blocking all work at once. - A shared error-kind package that's too coarse (only 3 kinds for a genuinely diverse set of failure modes) forces awkward overloading of a single kind for unrelated failures — review and evolve it as real usage reveals gaps.
Common Mistakes¶
| Mistake | Fix |
|---|---|
| Every team maintaining its own error-kind enum | Adopt a shared, org-wide vocabulary |
| No linter enforcement for discarded/unwrapped errors | Add errcheck/wrapcheck to CI, staged if the codebase is large |
| Postmortems that stop at "we scaled the connection pool" | Trace back to the error-handling/retry decision that caused exhaustion |
Apply it¶
- Define the user or business outcome that Error Handling should improve.
- Assign one owner for code, contracts, operations, and incidents.
- Split delivery into reversible increments that produce evidence early.
- Publish responsibilities, escalation paths, and compatibility windows.
- Stop or expand only when the agreed measures support that decision.
Verify your work¶
- Each increment has an owner, rollback path, and observable exit condition.
- Adoption, reliability, delivery time, and coordination cost are measured.
- Incident and migration exercises prove that responsibility is executable.
- The old path is removed only after telemetry proves it is unused.
Review questions¶
- Which measurable outcome justifies investing in Error Handling?
- Which team owns the full lifecycle and incident response?
- What reversible increment produces the earliest useful evidence?
- Which exit condition proves that migration or adoption is complete?
In this topic