Chapter 7 Flashcards — Error Handling
flashcards clean-code error-handling exceptions null-safety
Why is error handling with return codes problematic?
?
Return codes require every caller to check the result immediately after each call. If any caller forgets to check, the error is silently swallowed. The error-checking logic clutters the calling code, obscuring the real algorithm. Exceptions let the main path stay clean and focused.
What does it mean to “write your try-catch-finally first”?
?
When writing code that can throw exceptions, write the try-catch-finally structure before filling in the body. This defines the transaction scope: what state must be consistent even if something fails. It also makes it natural to write tests that verify exception behavior before the implementation exists.
What is the Open/Closed Principle violation caused by checked exceptions?
?
If you throw a checked exception deep in the call stack, every method between the throw site and the catch site must declare throws ExceptionType in its signature. A change to a low-level method cascades forced changes through all intermediate callers — violating the principle that classes should be open for extension but closed for modification.
What should a high-quality exception message include?
?
Three elements: (1) the operation being attempted, (2) the entity involved (ID, filename, etc.), and (3) the context (environment, user, state). Example: "Stripe charge failed for orderId=X, customerId=Y, amount=N cents. Stripe error: card_declined". A stack trace tells you where — the message tells you why.
What is the “classify exceptions by caller needs” principle?
?
When defining exception types, the question is how will callers handle this exception — not what caused it at the source. If three different underlying errors all trigger the same recovery action (log and retry), they should map to one exception type. This reduces the number of catch clauses and decouples callers from implementation details.
What is the purpose of wrapping a third-party API’s exceptions?
?
Wrapping translates third-party exceptions (e.g., ACMEPort’s DeviceResponseException) into a local exception type (e.g., PortDeviceFailure). This decouples your application from the third-party library. If you switch libraries, only the wrapper changes — all callers see the same local exception. It also collapses multiple third-party exception types into one meaningful local type.
What is the Special Case Pattern?
?
The Special Case Pattern (Fowler) creates an object that encapsulates the behavior for a special case, so calling code never sees a null or an exception. For example, PerDiemMealExpenses is returned when no expense report is found; its getTotal() returns the per-diem allowance. The caller calls getTotal() without any null check or exception handling.
When should you use the Special Case Pattern instead of throwing an exception?
?
When “absence” or “not found” is a normal business outcome, not an error. If the calling code always catches an exception and does something reasonable (like applying a default), you’ve modeled a normal case as exceptional. Make the default behavior explicit by encoding it in a Special Case Object that the DAO or factory returns.
Why is returning null from a method considered bad practice?
?
Every caller of a null-returning method must remember to null-check. One missed check causes a NullPointerException at a location far from the bug’s origin. The null propagates silently through the call stack. The alternative is to return an empty collection, a Special Case Object, or Optional<T> — all of which callers can use without null checks.
What is Collections.emptyList() and when should you use it?
?
Collections.emptyList() returns an immutable empty list in Java. Return it instead of null when a list-returning method has no results. The caller can iterate, stream, or call .size() on it without any null check. This is the simplest application of “don’t return null” for collection-typed results.
How does Optional<T> differ from returning null in Java?
?
Optional<T> makes absence explicit in the type signature: callers are forced by the type system to acknowledge that the value may not exist. They must call .get(), .orElse(), .orElseThrow(), or .ifPresent(). With plain null, absence is invisible in the signature and easy to ignore. Optional also chains cleanly with streams and lambdas.
What is std::optional<T> in C++ and when was it introduced?
?
std::optional<T> is a C++17 wrapper that either holds a value of type T or holds “no value” (std::nullopt). It replaces returning a raw pointer or a sentinel value to indicate absence. Callers check with .has_value(), use .value() to get the value, or provide a default with .value_or(default). It is analogous to Java’s Optional<T>.
How do you signal nullable return values in Python’s type system?
?
Use Optional[T] from the typing module (or T | None in Python 3.10+) as the return type annotation. This is a type hint only — Python’s runtime still uses None as the absent value. With a type checker like mypy in strict mode, callers that fail to handle None are flagged. The annotation communicates intent even without runtime enforcement.
Why is passing null to a method worse than returning null?
?
When you pass null, the receiving method must either (1) add null checks that clutter its logic purely to compensate for the caller’s mistake, (2) crash with an NPE far from the call site, or (3) silently compute wrong results. The root of the bug is in the caller, but the crash is in the callee. This makes diagnostics harder. Returning null is at least localized — passing null can cause confusing failures anywhere.
What is Objects.requireNonNull in Java and when should it be used?
?
Objects.requireNonNull(obj, "message") throws NullPointerException immediately if obj is null, with your custom message. Use it at the entry point of a method to validate parameters. This causes a fast, readable failure at the source of the bug rather than a confusing NPE deep inside the method body. It is the idiomatic Java way to enforce “don’t pass null”.
How do C++ references enforce null-safety compared to pointers?
?
C++ references cannot be null by the language specification. A reference must be bound to a valid object when created. Using const Point& p1 instead of const Point* p1 in a method signature makes null structurally impossible — no null check is needed. This is stronger than any runtime assertion: the compiler prevents the bug at the type level.
What distinguishes an unchecked exception from a checked exception in Java?
?
Checked exceptions extend Exception (but not RuntimeException) and must be declared in method signatures (throws) or caught. Unchecked exceptions extend RuntimeException and require no declaration. Clean Code recommends unchecked exceptions for application code because they don’t force every intermediate method to declare or handle exceptions they can’t meaningfully address.
Does C++ have checked exceptions?
?
No. All C++ exceptions are unchecked — no method signature declares what it can throw (unless using noexcept). There is no compiler-enforced requirement for callers to handle specific exception types. C# and Kotlin also chose not to have checked exceptions, for the same reason Clean Code describes: they increase coupling without sufficient benefit.
What is the relationship between error handling and the Single Responsibility Principle?
?
A function that mixes the main algorithm with error-handling logic has two responsibilities: doing the work and handling failure. By extracting the main algorithm into a helper method (e.g., tryToShutDown()) and keeping the try-catch in a separate method, each method has one responsibility. The try-catch method is responsible for error handling; the helper is responsible for the algorithm.
What is the “transaction scope” of a try-catch-finally block?
?
The try block defines the atomic unit of work: everything inside must succeed or be rolled back together. The finally block guarantees cleanup (releasing resources, closing connections) regardless of success or failure. Writing the try-catch-finally first forces the programmer to define this transaction scope before filling in the algorithm — clarifying what invariants must hold.
When is using return codes acceptable instead of exceptions?
?
Return codes are acceptable in: (1) C-style APIs where exceptions aren’t available, (2) extreme performance-critical inner loops where the overhead of exception handling is measurable (e.g., parsing 10M records per second), and (3) when working with a framework that uses return codes as part of its contract (e.g., system calls). These are deliberate exceptions to the rule, not defaults.
What is the difference between swallowing an exception and wrapping it?
?
Swallowing: catching an exception and doing nothing (or only logging), allowing the program to continue in an inconsistent state. This is almost always wrong. Wrapping: catching a low-level exception and rethrowing a higher-level exception that is more meaningful to the caller — using throw new AppException("...", cause) to preserve the original stack trace via the cause parameter. Wrapping is the correct pattern at API boundaries.
Why does Clean Code say “define the normal flow” in the context of error handling?
?
Because developers sometimes model normal business cases as exceptions out of habit. If the code always catches an exception to apply a default, the exception is not exceptional — it is a normal path. Modeling it as an exception adds overhead, obscures intent, and forces callers to write try-catch blocks for routine situations. The Special Case Pattern restores normalcy.
What is the from e / raise ... from e syntax in Python exception chaining?
?
raise NewException("message") from original_exception chains exceptions in Python. The original exception is stored as __cause__ on the new exception. When printed, Python shows both the original and the new exception with “The above exception was the direct cause of the following exception”. This preserves the full diagnostic context — equivalent to passing cause in Java’s new Exception("msg", cause).
What are the four wrong outcomes when null is passed to a method?
?
(1) NullPointerException at the point of first use — crash far from the bug’s origin. (2) Silent wrong results — code proceeds with a null reference used in a computation that happens to “work” but produces incorrect data. (3) Defensive null checks throughout the method — clutters callee logic to compensate for caller mistake. (4) Cascading null propagation — null returned from the method propagates further up the call stack.
How does wrapping third-party exceptions support the Open/Closed Principle?
?
When third-party exceptions are wrapped in a local type, the calling code is closed to modifications when the third-party library changes. If you switch from one payment gateway to another, only the adapter/wrapper changes — all callers of PaymentException are unaffected. Without wrapping, every catch clause in the codebase would need updating. The wrapper acts as a seam between your code and the third party.
What is the Null Object Pattern and how does it relate to Special Case?
?
The Null Object Pattern is a specific form of the Special Case Pattern where the “missing” object provides no-op implementations of the interface. Example: a NullLogger that implements Logger but does nothing. It lets callers use the object without checking for null. The Special Case Pattern is broader — the special-case object can have meaningful behavior (like PerDiemMealExpenses.getTotal() returning a real rate).
Total Cards: 27
Review Time: ~18 minutes
Priority: HIGH
Last Updated: 2026-04-14