Error handling is where codebases reveal their true nature. In well-maintained codebases, errors are handled consistently: clear patterns, informative messages, proper logging, appropriate recovery. In typical codebases, error handling is a patchwork of different approaches accumulated over years of different developers making different choices.
You'll find empty catch blocks that swallow errors silently. You'll find catch blocks that log cryptic messages with no context. You'll find exceptions that bubble up as generic "Something went wrong" to users. You'll find retry logic in some places but not others. The inconsistency makes debugging hard, user experience poor, and incident response chaotic.
AI tools like Devonair can analyze your entire codebase, identify error handling patterns (good and bad), and standardize them automatically.
Why Error Handling Becomes Inconsistent
Understanding the problem helps solve it.
Different Developer Philosophies
Some developers prefer exceptions. Others prefer error return values. Some log everything. Others log minimally. Without enforced standards, personal preferences create inconsistency.
Historical Accumulation
Early code uses the error handling style of its time. Later code uses newer patterns. Nobody updates the old patterns. The codebase becomes an archeological record of error handling trends.
Deadline Pressure
Proper error handling takes time. When shipping is urgent, developers write quick-and-dirty catch blocks with plans to "fix it later." Later never comes.
Copy-Paste Propagation
Developers copy existing code, including its error handling patterns. If the source has bad patterns, copies have bad patterns. The problem multiplies.
External Library Inconsistency
Different libraries throw different types of errors with different shapes. Wrapping them consistently requires effort that often doesn't happen.
Error Handling Anti-Patterns
First, find the problems.
Silent Failures
@devonair identify empty catch blocks that swallow errors
Errors that disappear silently are debugging nightmares.
Generic Messages
@devonair identify error handlers that provide no useful information to users
"Something went wrong" helps nobody.
Missing Context
@devonair identify error logs without sufficient context for debugging
Stack traces without inputs and state are incomplete.
Inconsistent Types
@devonair identify places throwing string errors instead of Error objects
throw "error" loses stack traces and type information.
Swallowed Rejections
@devonair identify unhandled promise rejections
Missing .catch() on promises causes silent failures.
Overly Broad Catches
@devonair identify catch blocks that catch too many error types
Catching all errors hides bugs in non-error paths.
Standardization Patterns
Error Class Hierarchy
@devonair implement standard error class hierarchy for the application
Custom error classes enable specific handling:
ValidationErrorfor input problemsAuthenticationErrorfor auth failuresNotFoundErrorfor missing resourcesServiceErrorfor external service failures
Error Factory Functions
@devonair create error factory functions for consistent error creation
Factories enforce structure and context.
Standard Error Shape
@devonair standardize error response shape for all API endpoints
Consistent error responses enable consistent client handling:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Email address is invalid",
"details": { "field": "email" }
}
}
Error Wrapping
@devonair implement error wrapping pattern to preserve original error context
Wrap errors while preserving the chain.
Standardizing by Code Area
API Error Responses
@devonair standardize all API error responses to consistent format
@devonair ensure all API errors include request ID for tracing
Service Layer Errors
@devonair standardize error handling in /src/services
@devonair ensure service errors include operation context
Data Access Errors
@devonair standardize database error handling with proper transaction rollback
@devonair convert database errors to application errors
Validation Errors
@devonair standardize validation error format across the application
@devonair ensure validation errors identify specific failing fields
External Service Errors
@devonair standardize handling of external API errors with retry logic
@devonair convert third-party errors to internal error types
Error Context Enrichment
Good errors have context.
Request Context
@devonair add request ID to all error logs
@devonair include user ID in error context where available
Operation Context
@devonair add operation name to all error handlers
@devonair include input parameters in error logs (sanitized)
State Context
@devonair capture relevant application state in error context
What state led to this error?
Timing Context
@devonair add timestamps and duration to error logs
When did it happen? How long did it take?
Error Logging Standardization
Log Level Consistency
@devonair standardize error log levels: error for failures, warn for recoverable issues
Log Format Consistency
@devonair standardize error log format with structured fields
Log Completeness
@devonair ensure all error logs include stack trace and context
Log Deduplication
@devonair prevent duplicate error logging in nested catch blocks
Log once at the appropriate level.
Error Recovery Patterns
Retry Logic
@devonair implement consistent retry logic for transient failures
@devonair add exponential backoff to external service calls
Fallback Values
@devonair standardize fallback handling for non-critical failures
Some errors can use defaults.
Circuit Breakers
@devonair implement circuit breaker pattern for external dependencies
Stop hammering failing services.
Graceful Degradation
@devonair implement graceful degradation for feature failures
The whole app shouldn't break because one feature fails.
Async Error Handling
Promise Handling
@devonair ensure all promises have error handling
@devonair convert .catch(e => console.log(e)) to proper error handling
Async/Await Errors
@devonair add try/catch to async functions missing error handling
@devonair standardize async error propagation
Event Emitter Errors
@devonair add error handlers to all event emitters
Stream Errors
@devonair add error handling to all stream operations
User-Facing Errors
Error Messages
@devonair ensure user-facing error messages are helpful and actionable
Tell users what happened and what they can do.
Error Codes
@devonair add error codes for client-side handling
Codes enable programmatic handling.
Error Recovery UI
@devonair ensure error states offer recovery actions
Retry buttons, help links, support contacts.
Error Boundary Components
@devonair implement React error boundaries for component failures
Contain failures to component trees.
Error Monitoring Integration
Structured Error Reporting
@devonair ensure errors sent to monitoring service have standard structure
Error Grouping
@devonair add error fingerprinting for proper grouping in monitoring
Similar errors should group together.
Error Severity
@devonair tag errors with appropriate severity levels
Critical errors alert; minor errors aggregate.
Error Metrics
@devonair track error rates by type and endpoint
Measure error health over time.
Testing Error Handling
Error Path Testing
@devonair ensure error handling paths have test coverage
Test the failure cases, not just success.
Error Response Testing
@devonair add tests verifying error response format
Recovery Testing
@devonair test retry and fallback logic
Error Boundary Testing
@devonair test error boundary behavior with component failures
Enforcement and Prevention
PR Checks
@devonair on PR: verify error handling follows standards
@devonair on PR: flag empty catch blocks for review
Linting Rules
@devonair configure ESLint for error handling best practices
Automated enforcement of patterns.
Code Review Guidelines
@devonair document error handling standards for reviewers
Clear standards enable consistent reviews.
Migration Strategy
Audit Current State
@devonair audit all error handling patterns in /src
Understand what you have.
Prioritize High Impact
@devonair identify error handling in critical paths: payments, auth, core features
Fix important code first.
Incremental Standardization
@devonair standardize error handling in /src/api this week
Progressive improvement.
New Code Standards
@devonair on PR: enforce standards on new code only
Prevent new debt while addressing old.
Getting Started
Start with visibility:
@devonair report on error handling patterns across the codebase
Know your current state.
Fix the worst problems:
@devonair fix empty catch blocks in /src
@devonair add error logging to silent failure points
Implement standards:
@devonair implement standard error classes and factories
Enforce going forward:
@devonair on PR: verify new error handling matches standards
Errors that are handled consistently are errors that get fixed. When every error follows the same pattern, debugging becomes systematic instead of archaeological.
FAQ
Should I use exceptions or error return values?
Use your language's conventions. JavaScript/TypeScript typically uses try/catch for synchronous code and rejected promises for async. Go uses error returns. Consistency within your codebase matters more than which approach you choose.
How much context should error logs have?
Enough to reproduce the problem without the original developer. Include: what operation, what inputs (sanitized), what state, what failed. Exclude: sensitive data, excessive verbosity.
Should I expose stack traces to users?
Never in production. Stack traces reveal internal implementation and can expose security vulnerabilities. Log full traces server-side; show users helpful messages with request IDs for support.
How do I handle errors from external services?
Wrap them in your own error types. The rest of your code shouldn't know or care about the third party's error format. Add retry logic for transient failures. Circuit break for prolonged failures.