Error Handling
To build robust software you must handle things going wrong. In code this comes in the form of error handling. There are many ways of handling errors, some common methods include: exceptions, error codes, and return objects.
We must build robust code, however this should be done in a way that maintains the readability and reusability of the code. For which there are some helpful principles here.
Prefer exceptions to error codes
Whilst there are times when error codes are a better architectural solution than exceptions (When to Use Error Codes and Exceptions) within your own code exceptions increase the readability of your code due to the lack of case analysis after functions execute. Within exceptions you can provide the full context of why it was thrown, and should do this! Whilst doing this follow the Exception good practices.
Exception good practices
Link to original
- Provide context to an exception: The only time an exception is going to be read is when something has gone wrong in the program. Therefore write enough context in the exception for the person to quickly identify the cause of the issue.
- Define exception classes in terms of the callers needs: When considering how to define exceptions, via type of error or cause think about what will help the caller the most. This will likely be grouping errors based on how they will handle it.
- Wrap 3rd party libraries and group exceptions: It is a good practice to wrap 3rd party libraries and join up similar exceptions into one internally named exception. This saves people having to remember all the exceptions that can be thrown for a given library.
- Only split exception classes if you want to handle them differently: When deciding if a type of error needs its own class in your code, only choose to split it up if there are cases when you need to handle the two errors differently.
- Handle errors at the lowest possible level: Try to avoid try-catch logic wherever possible. If you need to use an exception for a special cases consider using the Special case pattern.
Write your Try-Catch-Finally statement first
At the end of the Try and Catch blocks if your code is continuing on you should have a consistent state. i.e. if in your try block you define a return object within the catch blocks you will also need to have defined a return object.
Don’t return null
Similar to Error codes but far worse - if you return null you have no context on what caused this issue and it can be REALLY HARD to track down what is causing it if it is not caught (especially in languages like python). Even if you do implement good typing and linting and handle them it litters your code with if null
statements that are highly unreadable. Consider replacing this with an exception or a special case pattern.
Aside: Don’t pass null
Unless into a Data structure that has optional values, having to handle null input has all the issues of returning null but just in the other direction. This is one of the function conventions.