Article 9: Error Handling and Logging

Introduction

Errors are inevitable in softwareβ€”what matters is how we handle them. Through debugging production systems at 3 AM, I've learned that proper error handling and logging are what make the difference between a quick fix and hours of frustration.

This article covers exception handling best practices, structured logging, and debugging strategies that help you build resilient applications.

Exception Handling Fundamentals

The Basics

# Basic try-except
try:
    result = risky_operation()
except SomeException as e:
    handle_error(e)

Exception Hierarchy

spinner

Catching Specific Exceptions

# Bad: Catching everything hides bugs
try:
    process_data(data)
except:  # Never do this!
    pass

# Bad: Too broad
try:
    process_data(data)
except Exception:  # Still too broad
    log.error("Something went wrong")

# Good: Catch specific exceptions
try:
    user = fetch_user(user_id)
    process_order(user, order_data)
except UserNotFoundError:
    return {"error": "User not found"}, 404
except InvalidOrderError as e:
    return {"error": str(e)}, 400
except DatabaseConnectionError:
    log.error("Database connection failed")
    return {"error": "Service temporarily unavailable"}, 503

Multiple Exception Types

Custom Exceptions

Creating Custom Exceptions

Using Custom Exceptions

Exception Context

Error Handling Patterns

The Result Pattern

Fail Fast

Graceful Degradation

Retry Pattern

Logging

Basic Logging Setup

Structured Logging

Logging Best Practices

Logging Configuration

Context Managers for Logging

Debugging Strategies

Using the Debugger

Debugger Commands

Debug Logging

Assertions for Debugging

Practical Exercise

Exercise 1: Implement Error Handling

Exercise 2: Add Logging

Add comprehensive logging to this service:

Error Handling Checklist

Key Takeaways

  1. Be specific with exceptions - Catch only what you can handle

  2. Create domain exceptions - Custom exceptions make code clearer

  3. Log with context - Include relevant data for debugging

  4. Never log secrets - Passwords, tokens, and PII stay out of logs

  5. Use structured logging - JSON logs are easier to search and analyze

What's Next?

With robust error handling in place, let's automate quality checks. In Article 10: CI/CD Fundamentals, we'll set up GitHub Actions for automated testing, linting, and deployment pipelines.


This article is part of the Software Engineering 101 series.

Last updated