In many companies, the topic of error handling is either not taught at all or not sufficiently. I have never been given a clear definition of what "errors" are and how I should deal with them at my workplace. This is obviously the case for many people, as this lack of knowledge leads, among other things, to the widespread bad habit of making mistakes. defensive programming is called. In addition, an exception is usually - and incorrectly - regarded as an error. In two articles, I would like to provide clarification and convey a better understanding of exceptions and how to deal with them. In the context of this focus, I will also take a closer look at the topic of error handling and start with a brief overview of the three categories of errors.
You can find a detailed article on the categories of errors here.
Operator error
A user error occurs when incorrect or missing entries are made in the user interface (UI) of an application. It does not matter what type of application it is, how the user interface is designed or who the users are - be they people or other applications. The user interface can take various forms, such as a command line interface (CLI), a console, a graphical user interface (GUI), endpoints of a web service or the public interface of a software package. The user interface reflects the requirements of the user role for the application and serves as a portal for the various use cases. Data is entered into the application by the user through the user interface. It is important to check this data as it can be incorrect. User errors are predictable, so we can develop tailor-made solutions for them.
Errors in the environment
Applications are usually connected to external data sources. For example, the file system, services, databases or hardware such as measuring devices and machines. The application communicates with these resources via APIs (Application Programming Interfaces) and expects results. An API acts as an interface that enables the application to interact with other software modules or external services. The results delivered can either be the desired results or undesired results. Access to a file can fail, as can the connection to a database or a service. An addressed hardware could be switched off. In such cases, the API returns error codes or exceptions as feedback. These represent states in which the desired result cannot be delivered. Exceptions are defined via the API. This means they are predictable and we can develop tailor-made solutions for them.
Developer error
Developer errors are errors that we as developers introduce into our code ourselves by overlooking or forgetting something in our logic. If we work in a managed runtime (e.g. CLR or JVM), this often leads to the triggering of an exception. In contrast to operator errors or errors in the environment, these errors are not foreseeable for us. Logically, in contrast to the other categories, no precise, explicit solution can be created here. One solution for such cases can be a global exception handler that takes care of all exceptions occurring in the program that are otherwise not handled.
Confidence limits
The categories Operator error and Errors in the environment describe where an application works with external data. UI and resource-side APIs represent entry and exit points to internal logic. Data must be validated here. Complete validation at the boundaries guarantees that only valid data exists within the logic. Unless the developers, i.e. us, have overlooked something and made a developer error. The validations ensure that the operator errors and errors in the environment are processed at the boundaries of our logic. The exception often plays a major role in the resource-side result check.
What are exceptions?
All developers probably have experience with exceptions. Many interpret them as errors, as they are often triggered by developer errors. The literature and this article also deal with the topic of errors when exceptions are mentioned. Logical, because exceptions are also the result of errors. Ultimately, however, they are just that - completely normal results. Exceptions are classes in which information is defined. They are returned to us by runtime and framework methods or packages. So exceptions are a common result, especially when using APIs. As the name suggests, they represent an exception. An example:
public class FileProvider
{
public IEnumerable ReadFileContent(string filename) {
return File.ReadAllLines(filename);
}
}
This example will accompany us through both parts of the article. One class FileProvider represents a method ReadFileContent is available. In the first post, this method only calls the runtime method File.ReadAllLines without adding its own logic. The main focus lies in the consideration of the method File.ReadAllLines and the expected results. Let's look at the signature of the enclosing method ReadFileContent we can see the first possible result. The use case in which the content of the file is returned as required. The other possible results can be found in the form of exceptions in the documentation of the method File.ReadAllLines [Fig. 1].
A total of nine different exceptions are defined. Most of them relate to the content of the parameter pathothers on access rights. All of them specify their type and an associated Message Insight into the reason why the desired result cannot be delivered. This documentation makes it clear that behind the seemingly simple method signature File.ReadAllLines obviously involves a lot of work. Reading a file from the file system is not a one-liner. This method supposedly makes access for users a one-liner. However, on the way to a simple return value, there are rules that must be fulfilled. If they are not, meaningful exceptions are returned. These exceptions are not errors, but merely deviations from the regular result of the method. They represent early exits from a workflow, preconditions that have not been fulfilled. I have designed the graphic [Fig. 2] to illustrate the workflow. It is based on the design language Flow Designwhich we teach in our seminars.
The diagram illustrates what happens within the method. All steps are checked one after the other. If all conditions are met, the method returns the desired result. If not, the method uses throw generates an exception that provides information about the unfulfilled condition. There is an enormous benefit for callers of the method. Only a single call is required and all states are checked internally. Developers do not get one result, but ten possible states for which a solution can be created. They just have to make sure that they can handle them.
Conclusion
Exceptions are not errors, but results that describe unfulfilled conditions. They are a normal part of a workflow. A method that potentially delivers an exception delivers more than just a result. This only results in a state-related case differentiation in the program flow. Runtime does not differentiate between correct and incorrect. It delivers results. The determination of whether an error has led to an exception can only be answered in context. How exceptions are handled is the responsibility of the user of the method. With this in mind, part 2 of this article deals with the handling of exceptions.