Exception Handling Isn’t Syntax, It’s Architecture


I was scrolling through LinkedIn the other day — as one does, with a mix of professional curiosity and masochism — when I came across a brightly polished post about “Throwing Exceptions the Right Way in C#.” You’ve probably seen it, or one just like it. Clean diagrams. Green checkmarks. Red Xs. Bite‑sized rules presented as wisdom.

Nothing in it was wrong.

And that, paradoxically, is exactly the problem.

This article was born out of that moment of unease: the feeling that we are increasingly mistaking syntactic hygiene for architectural maturity, and mistaking LinkedIn‑optimized advice for lived engineering experience. Exception handling is a perfect example of this pathology.

Let’s talk about it properly.

The LinkedIn Version of Exception Handling

The now‑familiar checklist goes something like this:

  • Use specific exception types

  • Don’t throw Exception

  • Never use exceptions for control flow

  • Always use throw; instead of throw ex;

Individually, these statements are defensible. Collectively, they create the illusion that exception handling is a local, mechanical concern — a matter of choosing the right keyword and calling it a day.

Real systems don’t fail locally.

They fail systemically.

And the moment your system grows beyond a single method, these rules stop being sufficient and start becoming actively misleading.

What throw; Really Teaches (and What It Doesn’t)

Yes, rethrowing with throw; preserves the original stack trace. Losing it is unforgivable in production debugging. On that, there is no disagreement.

But here’s the uncomfortable truth: a pristine stack trace attached to a meaningless exception is still useless.

If your logs say:

InvalidOperationException: Order state is invalid

Then congratulations — you have preserved perfectly the record of having learned nothing.

Which order? Which state? What transition? What input? What user? What correlation ID?

Stack traces tell you where something exploded.
Context tells you why.

LinkedIn posts obsess over the former because the latter requires design.

The Cardinal Sin Isn’t “throw new Exception”

You’ll often see blanket proclamations like “Never throw Exception.” They sound authoritative. They’re also wrong in any absolute sense.

The real question is not what you throw.

It’s where you throw it.

At system boundaries — between your application and:

  • A third‑party API

  • A database driver

  • A legacy system

  • A message broker

—you often have no meaningful way to preserve the original exception semantics. Wrapping a low‑level failure into a higher‑level, application‑meaningful exception is not laziness; it is ownership.

The problem isn’t generic exceptions.
The problem is generic thinking.

Exception Ownership: The Topic Everyone Avoids

Here is the part LinkedIn advice never touches, because it doesn’t fit into a carousel:

Every exception must have an owner.

  • Domain exceptions belong to the domain

  • Infrastructure exceptions belong to infrastructure

  • Application services translate, never leak

  • Presentation layers do not decide business outcomes

If an exception crosses more than one architectural boundary unchanged, something is already wrong.

Exception handling is not about catching errors.
It is about containing failure.

“Never Use Exceptions for Control Flow” (Except When You Should)

This phrase is repeated so often it has become a reflex. And like most reflexes, it lacks judgment.

Exceptions should not represent expected, frequent states — agreed.

But they are often the cleanest expression of violated invariants:

  • A payment captured twice

  • A state machine receiving an illegal transition

  • A security boundary being crossed incorrectly

Encoding these as return values frequently leads to worse code, not better — because now the burden of correctness is silently pushed onto every caller.

Exceptions exist to say:

“This path must not continue.”

Using them responsibly is not immaturity. It’s honesty.

What Mature Exception Handling Actually Looks Like

In real systems, good exception handling has very little to do with the throw keyword and everything to do with discipline:

  • Exceptions are rare, intentional, and documented

  • Messages are written for humans, not compilers

  • Context is captured once, close to the source

  • Exceptions are translated at boundaries

  • Logs are structured and correlated

  • Callers either handle an exception or deliberately let it crash

None of this fits in a “Pro Tips” box.

The Social Media Distortion Field

Why does shallow advice dominate?

Because LinkedIn rewards:

  • Certainty over nuance

  • Rules over reasoning

  • Syntax over systems

It is safer to say “Always do X” than to explain when X is insufficient.
It is easier to teach mechanics than judgment.

But judgment is what keeps production systems alive.

Closing Thought

If exception handling were merely about choosing the right exception type, debugging modern software would be trivial.

It isn’t.

Failures are narratives. They tell a story about what your system believed, what assumptions broke, and how well you anticipated reality pushing back.

Clean code doesn’t come from memorizing rules.
It comes from designing for failure — and taking responsibility when it happens.

Everything else is just syntax dressed up as wisdom.

Comments