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