What Is Good Error Handling, Really?

Walter SotoWalter Soto
3 min read

Have you ever shipped a feature, crashed production, and then stared blankly at your PC, wondering what happened? Yeah, welcome to error handling, or as a buddy of mine calls it: the art of pretending chaos is under control.

Let's get this out of the way: error handling isn’t about avoiding failure. It’s about making failure slightly less catastrophic like choosing to land your plane in a cornfield instead of a mountain.

So let’s break this thing down:


🌩 Graceful Degradation, Fail-Fast, and Resilient Design

You’ve probably heard these terms thrown around in tech meetings, but what do they mean?

Graceful Degradation

This is when your app doesn’t throw a tantrum just because one part failed. Think: Netflix is down? Okay, fine. But at least the homepage still loads and I can stare at thumbnails.

Fail-Fast

The opposite of sweeping problems under the rug. If something's wrong, make it known fast. Your API should not “kinda” work, it should either do the job or crash like a toddler denied a cookie.

Resilient Design

This is the system equivalent of having an emotionally stable friend. Something goes wrong? They have a plan. Maybe it’s retries, maybe it’s fallback logic, maybe it’s screaming silently behind the scenes. Either way, users don’t feel it.


⚖️ Expected vs Unexpected Errors

This is the difference between I knew this might happen and what fresh hell is this?

  • Expected errors: A user gives you garbage input. A network times out. A file isn’t found. These are normal screwups. You plan for these. You return polite messages. You log warnings. You don’t freak out.

  • Unexpected errors: Null pointer exceptions. Race conditions. The database is on fire. These are not things you anticipate. They deserve your panic, but also your attention. You should catch them, log them, and contain them.

Good error handling means acknowledging that both of these will happen and treating them differently. A 400 is a “try again.” A 500 is a “call for help.”


🫦 try/catch Overuse vs Strategic Error Boundaries

Let’s talk about the try/catch addiction.

Yes, exceptions should be caught. No, that does not mean you wrap every other line in try {} like you're bubble-wrapping your code for shipping. Catching every error and doing nothing is the same as yelling "Oops!" during a car crash and then driving off.

That’s why modern frameworks give you error boundaries—a way to catch groups of issues in a meaningful place. In frontend land (hi React), this could be wrapping a component tree. In backend services, it’s middleware that logs and responds without leaking stack traces into the void.

The goal isn’t to silence every error. It’s to localize the chaos. Keep the bug from spreading like mold in a damp basement.


So What Is Good Error Handling?

It's not just about avoiding the red screen of death. It’s about:

  • Knowing what can go wrong

  • Designing fallback behavior that keeps things usable

  • Failing loudly when needed, quietly when appropriate

  • Not catching exceptions just to whisper "shhh" into the log file

  • Logging like your future self will need to do forensic debugging at 3 am

Think of error handling like good parenting: you're not preventing mistakes, you're just making sure nobody dies and the house doesn’t burn down.


TL;DR

PrincipleBad ExampleGood Example
Graceful degradationBlank screen if API fails“We couldn’t load this” message + fallback
Fail-fastSilent null error three functions deepValidate and crash early
Expected vs Unexpected500 for invalid input400 for bad input, 500 for bugs
try/catch usageCatch everything, log nothingCatch once, log meaningfully, recover, or exit

Final thought:
Code like nothing works and design like someone will click the one button you forgot to test. Because they will.

0
Subscribe to my newsletter

Read articles from Walter Soto directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Walter Soto
Walter Soto