Errors and How to Handle them.
Hello, I’m Nripesh
I am an aspiring programmer, continuously learning and building projects one step at a time.
This blog will serve as a record of my journey, where I document the coding challenges I encounter each week and the solutions I work towards. My intention is not only to track my own growth but also to provide clarity for others who may face similar difficulties.
What you can expect here:
Weekly summaries of the issues I struggled with
Solutions, explanations, and key insights I gained from them
References and links to helpful resources, documentation, or my GitHub repositories
Occasional notes on personal projects I am developing
I am still in the early stages of my learning path and have much to improve, but I approach each challenge with confidence and persistence. This space is both a personal archive and an attempt to contribute—however modestly—to the wider learning community.
If you are also navigating the world of programming, I welcome you to connect and share perspectives.
A backend engineer is bound to face some errors sooner or later so it is necessary to create a fault tolerant backend system and handle the Errors.
Types of Errors:
1. Logical Errors
Cause: Flaws in reasoning, misunderstood requirements, or incorrect algorithm design.
Misinterpreting requirements
Incorrect algorithm / condition logic
Ignoring edge cases
Example:
if (age > 18) allowAccess();
Bug: misses age == 18 → should be >= 18
2. Database Errors
Cause: Issues interacting with the database layer.
Connection errors: DB not reachable
Constraint violations: unique/foreign key failure
Query errors: malformed SQL or wrong assumptions
3. External Service Errors
Cause: Dependency on third-party services (you’re effectively a client).
Network failures
Connection timeouts
DNS resolution failures
Network partitions
Rate limiting (e.g., Clerk, Auth0)
Example:
await axios.get("https://api.auth0.com/user");
Fails with 429 Too Many Requests
Mitigation:
Retry with exponential backoff (trying after 1min then 2 then 4 then 8 ...)
Fallback mechanisms
4. Input Validation Errors
Cause: Invalid or malformed user input (most controllable category).
Format validation (email, phone)
Range validation (age < 0)
Missing required fields
Example:
{ "email": "not-an-email" }
Rejected due to invalid format
Note: Easiest to prevent via schema validation (e.g., Joi, Zod)
5. Configuration Errors
Cause: Incorrect environment or system configuration.
Missing environment variables
Wrong API keys / DB URLs
Misconfigured ports/services
Example:
DATABASE_URL=undefined
App crashes on startup
Best Practice:
Fail fast at startup, not during runtime
Validate config using schemas before boot
Proactive Error Handling (Before Errors Happen)
The most effective error handling isn’t reactive — it’s preventive. Strong backend systems are designed to detect, isolate, and surface issues before they impact users.
1. Health Checks (Service-Level Monitoring)
Expose endpoints like:
/health/status
These are continuously pinged by load balancers or monitoring tools to verify:
Service is running
Dependencies are reachable
System is responsive
2. Database Health Checks
Monitor database behaviour, not just availability:
Query latency (slow queries act like an early warning)
Connection pool saturation (long job queues).
Failed/blocked queries
Purpose: Catch degradation before full outages.
3. External Service Health Checks
Since third-party services are outside your control, actively validate them:
Dummy API calls to auth providers
Sending test emails via email services
Simulating lightweight transactions
Purpose: Ensure integrations (Auth, Payments, Notifications) are operational.
4. Configuration Validation (Fail Fast)
Misconfiguration should crash the app at startup, not during runtime.
Validate environment variables
Check API keys, DB URLs, ports
Schema-based config validation
Purpose: If the system can’t run correctly, don’t let it start.
5. Core Functionality Checks
Before deployment or during startup:
Ensure critical flows are functional
Verify dependencies are wired correctly
Run smoke tests on key features
6. Logging & Monitoring (Early Detection)
Proactive systems don’t just prevent errors — they detect and diagnose instantly:
Centralised logging (trace failures quickly)
Real-time monitoring & alerts
Track performance metrics (latency, throughput, not just errors)
Note: Performance degradation is often the first signal of an upcoming failure.
(We’ll cover logging, monitoring, and performance tracking in more depth afterwards.)
Handling Errors Gracefully (When They Do Happen)
Even with proactive systems, failures are inevitable. The goal is to minimise user impact, recover where possible, and fail intelligently where not.
1. Immediate Responses (Recoverable vs Non-Recoverable)
Recoverable Errors: System can retry or provide fallback.
- Example: Email service down → Queue the email + retry later → Respond: “Action successful, email will be sent shortly”
Non-Recoverable Errors: Cannot complete the operation → contain damage.
- Example: Payment processing failure → Abort transaction, rollback state → Respond: “Payment failed, please try again”
Key Idea:
Recover silently when possible
Fail transparently when necessary
2. Graceful Degradation
Instead of full failure, reduce functionality but keep system usable.
Recommendation service down → show generic content
Analytics failure → skip tracking, don’t block request
Goal: Maintain core user experience even under partial failure.
3. Error Recovery Strategies
Structured approaches to handle transient failures:
Retries with exponential backoff
Fallback mechanisms (cached data, secondary service)
Queue-based recovery (async jobs for later retry)
Example:
retry(fn, { retries: 3, backoff: exponential });
4. Error Propagation Control (Bubbling Up)
Errors should bubble up cleanly, not leak or get swallowed.
Lower layers → throw structured errors
Upper layers → decide response/action
Example:
// service layer
if (!user) throw new NotFoundError("User not found");
// controller layer
catch (err) {
next(err); // pass to global handler
}
Principle:
- Don’t handle errors where you can’t act meaningfully
5. Global Error Handling (Final Safety Net)
A centralised middleware that catches all unhandled errors.
It ensures to:
Standardise error responses
Hide internal details (no stack leaks)
Log errors for debugging
Example :
app.use((err, req, res, next) => {
console.error(err);
res.status(err.status || 500).json({
message: err.message || "Internal Server Error",
});
});
This layer ensures that: no request crashes silently, consistent API responses are sent and to centralisation of Logging and increasing the observability
Conclusion
Building resilient backend systems isn’t about eliminating errors—it’s about anticipating, detecting, and handling them with control and clarity. From proactive health checks to graceful degradation and centralised error handling, robustness comes from designing for failure at every layer.
In the next section, we’ll take this further by diving into Production-Grade Configuration Management—because many failures don’t start in code, they start in config.