Opening Scenario

Your payments service currently has this:

# Python
def charge(card, amount):
    if amount <= 0:
        raise Exception("bad amount")
    if card.expired:
        raise Exception("card expired")
    try:
        gateway.submit(card, amount)
    except Exception as e:
        raise Exception(f"gateway error: {e}")

Upstream, the handler does:

try:
    charge(card, amount)
except Exception as e:
    return 500, str(e)

This works until someone asks: "Why do card-expired errors return 500? That's a client problem, not a server problem." The handler can't tell the three cases apart because they're all Exception. You add string matching (if "expired" in str(e):) and immediately regret it.

The fix isn't more try/except — it's better exception types. What does a good exception hierarchy look like, and where do people go wrong designing one?


Question Progression

Q1: Why are string-based error checks a bug magnet?

Question: Matching on error messages (if "expired" in str(e)) seems to work. Why do seasoned engineers forbid it?

In the full tutorial

  • When to Reach for This

Glossary history

Click dotted jargon to save explanations here.