Course contentsShow
← Rust
Lesson 935 of 1,9107. Error Handling PatternsFree lesson

Error-Driven Development (EDD) Techniques

Use compiler-enforced error propagation to drive the design of system components.

In this lesson, you will learn how to use the Rust compiler as a design partner, letting exhaustive error checking dictate the architectural boundaries of your system components.

The Philosophy of EDD

Instead of writing your logic first and "patching in" error handling later, Error-Driven Development (EDD) involves defining your possible failure states before implementing your business logic. By defining your enum of errors early, you force the compiler to act as a strict inspector. If a new state or transition is added to your system, the compiler’s exhaustiveness checking ensures you cannot move forward until the error handling for that new branch is explicitly addressed.

Designing via Failure Boundaries

Consider a scenario where you are building a payment gateway. Instead of returning a generic Result, you define a specialized error type that reflects the physical boundaries of your system:

enum PaymentError {
    NetworkTimeout,
    InsufficientFunds,
    InvalidCurrency,
}

fn process_transaction(amount: u64) -> Result<(), PaymentError> {
    // The compiler now forces you to handle every variant of PaymentError
    // in your orchestrator, preventing "forgotten" failure modes.
    todo!()
}

By defining PaymentError first, you have effectively "fenced" the behavior of process_transaction. If you later need to add a FraudDetected variant, the compiler will immediately highlight every location in your codebase where this error isn't handled. This turns your error-handling logic into a design contract.

When you use EDD, you aren't just catching bugs; you are building an API that is "self-documenting" via its failure modes. If a function signature requires you to handle a NetworkTimeout, the system is telling you exactly what environmental assumptions it makes. You listen to the compiler to understand how complex your component actually needs to be. If the list of error variants grows too large, the compiler effectively warns you that your component has too many responsibilities, prompting you to decompose it into smaller, more manageable parts.

Key Takeaway: Use the Rust compiler’s exhaustiveness checking on custom error enums to enforce architectural boundaries and ensure that no system failure mode goes unhandled during development.