Skip to content

Elegance in Dealing with the Unexpected

Why clean error handling is an essential building block of Clean Code

White petals on a white surface

Photo from the blowup on Unsplash

The Hidden Complexity

One cannot seriously talk about Clean Code without considering the clean handling of error situations. Error handling represents a separate responsibility that should be separated from business code. The goal remains, as with all Clean Code principles, clear separation: Domain code should be recognizable at first glance, without being overlaid by try-catch blocks or if conditions for checking error codes.

The Foundation of Good Error Handling

Try-catch-finally was a first important step toward separation of concerns: The try block contains the business "happy path," while the catch block handles the exception. When defining our own exceptions, we should observe these principles:

  • Prefer unchecked exceptions: They avoid boilerplate code and lead to clearer interfaces

  • Technical vs. business exceptions: Clearly distinguish between technical errors and business exceptions

  • Provide context information: Exceptions should contain meaningful information about the error context

  • Exception design from client perspective: Design exceptions as needed by users of your API

Among common programming languages, Java is practically the only one that knows checked exceptions – a concept now considered problematic and doesn't need further elaboration here.

Avoiding Null Values

Two more essential aspects of Clean Code error handling:

  • Don't return null values: They force the caller to perform additional checks

  • Don't accept null values as method arguments: They make the behavior of methods unpredictable

These rules may seem trivial, but with increasing experience, one recognizes their importance – in line with Jeff Atwood's well-known maxims "Code is a liability, not an asset" and "the best code is no code at all." Why are null values problematic as method arguments? They require the reader of the code to understand and consider the method's behavior in this special case.

Object-Oriented Solution Approaches

In an object-oriented programming language, many error handling scenarios can be elegantly solved or even avoided. The Special Case Pattern and the Null Object Pattern offer clean alternatives here.

Structured Error Handling

The Compose Method Pattern is excellent for structured error handling. My typical approach looks like this. Incidentally, this method also works excellently in languages without an exception concept such as Go.

function tryDoSomething() {
  try {
    doBusinessLogic();
} catch (MissingSomethingError error) {
    handleMissingSomethingError(error);
  }
}

The Key to Success

Much more could be said on this topic, but for now I'd like to recommend: Write robust, readable, and above all clean code by treating error handling as a separate responsibility independent of your core business logic. This separation makes your code not only more understandable but also more maintainable and testable.

Author

Sebastian Bergandy
Sebastian Bergandy