Try to not explicitly handle exceptions (Part 1 - Exception Handling and rules for it )

Try to not explicitly handle exceptions (Part 1 - Exception Handling and rules for it )

As your project is slowly growing and became bigger and bigger, sometimes you realize, that the development rules you should know and you told your team at the begging of the implementation phase are not the standard for all. Or maybe, you and your team did not have any. Maybe, you told it very formerly during the development meeting or during the stand-up. For senior developers are knowledge of those rules normal. But for the rest of the team, it could be a new topic. Sometimes is good to refresh those rules to the rest of the team. In this article, we will talk about exception handling rules. Those rules are playing altogether and it is not easy to take and apply only one of them. The first rule is a very good example.

Try to not explicitly handle exceptions, ...

Really? Why is the exception handling in the programming language level exist then? Well, it depends. In the case of a bigger project, there is a big possibility that you are using some kind of infrastructure. And exception handling and logging are very often the first parts of what we are implementing.

You should try to concentrate on exception handling in one place. This place is sometimes called the global error handler. And the implementation of it depends on the kind of application. For example, in the case of ASP.NET Core application it could be those standard ways:

  • Middleware
  • Exception filter
  • Exception Handles Lambda (concrete implementation of the Middleware)

The rule could sound strange, and I have to add the second part of it. And it is: In the case, you have the exception handling infrastructure, try to always write an optimistic workflow.

What is an optimistic workflow? You are trying to write code with expectations of success. When you are trying to explicitly handle exceptions on all methods that can throw it, your code will start to be harder to read and maintain.

The second disadvantage of explicit exception handling grows with the team size. Everybody brings some knowledge of programming skills and standards as the heritage from previous jobs. More people, more heritage. There is a possibility that different parts of the system and their errors will be handled differently what brings system behavior inconsistency.

More specific means more information

Never try to handle the base exception types like Exception or ApplicationException. Those exception types are supposed to handle only by the global error handler. At that level, I do not care that was the source of the problem. Is it a file access problem? HTTP request timeout? Or disk full? I do not care. What I need is to handle it, log the error details and make the application able to continue, and keep it in the correct state.

More close you are to the exception source more specific you should be. There is some special case, where you can handle the base type Exception. And that is when your method logic or call is very simple, and for the next processing after the exception is thrown is the source of the problem is not important. Example:

public bool WriteInfo (string message) {
    try {
        using (var stream = File.AppendText ("Data.log")) {
            stream.Write (message);
        }
    } catch (Exception e) {
        _logger.Error (e.Message);
        return false;
    }

    return true;
}

This rule depends on the quality of information you want to transfer to the method consumer. For example, when you are writing the library you always have to have a way how to design interfaces and error propagation. Usually, every method has its own set of exceptions to know better describe the problem.

For example, when you look at the .NET method File.AppentText(string filePath). It could throw more exception types:

  • ArgumentException
  • PathTooLongException
  • DirectoryNotFoundException
  • ...

When consuming this method like the example above, catching the base Exception type is OK. In the case, you want special behavior after the error occurred, that depends on the reason you have to catch a more specific exception type. A good example is a scenario where you are processing more records during one request and it is acceptable to process the partial set of those data (only valid data).

public void ImportData (IEnumerable<Data> records) {
    try {
        IList<Data> validRecords = new List<Data> ();

        foreach (var record in records)
            try {
                _validator.Validate (record);
                validRecords.Add (record);
            }
        catch (ValidationException exception) {
            _logger.LogWarning (exception);
        }

        _httpClient.Send (validRecords);
    } catch (HttpException exception) {
        _logger.LogError (exception);
    } catch (Exception exception) {
        // Unexpected exception! Handling this exception type should be responsibility of the global exception handler
        _logger.LogError (exception);
    }
}