Editor's note, 2018-10-08: The video recording of this presentation is now available on the Explore DDD YouTube channel.
Michael Feathers finds errors fascinating, but acknowledges that most developers don't spend a lot of time focusing on them. He also thinks most error handling is kind of giving up. Although best known for his book, Working Effectively With Legacy Code, Feathers used his keynote presentation at Explore DDD 2018 to discuss how eliminating errors can be a design driver for software systems.
Appropriate for a conference on Domain-Driven Design, Feathers began with five definitions for "domain." The commonalities he found among these definitions were that domains imply ranges, and many domains are arbitrary and something we invent. Because they are an arbitrary construct, we also have the ability to re-shape and extend domains. While that may seem intuitive for using DDD to model and adapt to evolving business processes, Feathers proposes making similar domain changes to better cope with, or even eliminate, errors.
He noted that caution should be used, as extended domains can cause dissonance. As examples, Feathers mentioned encountering February 30th as a selectable date, or programming languages that allow negative indexing into arrays. For the date scenario, it's easy to understand how a simple domain model could allow that to occur, despite being invalid. However, the opposite is true for negative array indexes, which look like they should throw an error. When such a technique is available, and you accept it as valid syntax, then you realize there are situations where it can be useful.
Some of the inspiration for Feathers' exploration into errors came from Joe Duffy's blog discussing the Midori operating system research project at Microsoft, specifically the error model. Duffy says, "The basic question an Error Model seeks to answer is: how do 'errors' get communicated to programmers and users of the system?" This seemingly simple question naturally leads to the challenge of "defining what an error actually is." Feathers continues the line of reasoning, eventually wondering, "Why do we have errors?" Put another way, what if "error" is just a word we use for mismatched concepts in our domain?
Moving from the conceptual to the practical handling of errors, there are three main categories of behavior when encountering an error. The first option is to simply return null. This can remove any of the explanation of what went wrong, requires additional null reference checks, and obfuscates the fact that you are still dealing with the error, if inelegantly. Throwing an exception can be better than returning null, as it can provide information about the issue, but it requires the caller to handle the exception. The third option is to make the error part of your domain. Feathers believes "errors are part of your domain because they can happen as part of your work," clearly advocating for this option.
Extending the domain means asking, "What do you really want to have happen?" Instead of just telling someone about the error, introduce new concepts that provide actionable information. One example is to use the null object pattern, and return an ItemNotFound object, but the specific implementation will depend on the situation.
A final point of discussion Feathers provided was the design philosophy of Erlang, summarized in British meme style as "Keep calm and let it crash." In the real world, computation can fail, because computation is tied to the real world. Erlang extends the domain of your application by making the world part of it. If the domain of an entire language can be extended in such a way, surely any smaller system can benefit from an extended domain that encompasses its errors.