Avoiding exceptions at all costs

      3 Comments on Avoiding exceptions at all costs

Story:

A while back, a colleague and I where trying to get a program to start in order to add some new features to it, but the GUI was blank and there where no error messages. We could see in the debug output console, that exceptions where thrown but no detailed information about what the problem was. Obviously, the Visual Studio settings for “Exceptions: break when thrown”, had them all disabled, but I didn’t think that much of it at the time.

We enabled breaking on all exceptions in Visual Studio and did not have to wait long before the debugger broke the execution. The occurrence of the error seemed strange, but we thought we might have an old version of the database. Quickly we fixed the error and restarted debugging. Another strange but simple error occurred and we fixed that too, and that is what we did for about an hour, before we started to realize that the exceptions being thrown actually did not have that much to do with why the application was not working. The application was build on a home grown framework which swallowed exceptions, most of them without logging or making any notice of anything. The exceptions being thrown was probably something the previous developers had not been aware of, since the framework ate them. It also made debugging a lot harder, since the program was developed in a way that removed the use of many of the modern developer tools, many of us have grown to need. 

This actually reminds me of the old basic statement “ON ERROR RESUME NEXT”, which means that no matter what error occurs, keep running like nothing has happened.

An exception is an exception to normal program flow. An error which parts of the program can not recover from.

I would say that writing code that make your code more difficult to maintain and debug, is very odd.

(Some of) The rules of exceptions

Rule #1: You should only throw exceptions when there is no other reasonable way for a method to signal that an error has occured. IT IS NOT ACCEPTABLE AS A TRI STATE RETURN.
Rule #2: You should predict and prevent exceptions from occurring, instead of reacting to them.
Rule #3: Only exceptions worth logging and notifying the user / administrator about should be thrown.
Rule #4: Log all exceptions, and follow up in your logs which exceptions are occuring and if there are any ways to prevent them.
Rule #5: The only time it is allowed to “swallow” exceptions is when our own unit tests is causing them on purpose.
Rule #6: Beware of the borderline exceptions, like TaskCanceledException is no exception from the rules. It is thrown when the executing code must stop as soon as possible.
Rule #7: Exceptions should only be thrown when there is no good reason to expect them and prevent the cause.
Rule #8: As an exception to the rules – in some cases it is more or less impossible to prevent exceptions and in those cases, catching the exception and reacting to it may be our only option.

Preventing exceptions

For example – We are writing a program trying to read the contents of a file and insert it into a database and then deletes the file.

Trying to make a point about exceptions, the rest of the code is simplified.

This is what it might look like if we are uncertain about how to handle exceptions:

Now why is this wrong, you ask…

Every time the method is run and the file does not exist, it throws an exception. Exceptions are exceptionally bad performance and the deeper the executing code is in the call stack, the slower the exception. Every time an exception is thrown, a lot of debug information is generated in the program, behind the scenes.

If the above code is being run every 5ms, 200 exceptions will be thrown every second, eating a whole lot of CPU.

Consider the following code instead:

We can add all kinds of code preventing exceptions. When reading the configuration file containing the path and filename, we can use System.IO.Path methods to ensure that the path exists and that the characters in the path and filename are valid for paths and filenames.

Another example is the misuse of the indexer dictionary method, when the key is not guaranteed to exist in the dictionary.

Use this instead:

 

 

3 thoughts on “Avoiding exceptions at all costs

  1. Fabio

    I agree with you, but in some case, if you react to exception you have a performance gain.
    For example if you have 1000 calls and only in one the file is missing, you wasted time for 999 calls called System.IO.File.Exists(filename).

    Fabio

    Reply
  2. Joel

    Oh man, I can’t agree more with this. I once worked on a project that would routinely throw exceptions when parsing a string field on a particular model. The whole exception could have been avoided properly with a carefully done regex check beforehand. It always made debugging a pain in the butt because you’d have to step over this one particular statement a dozen times or so, hoping that you didn’t hit (F10? F11?) one more time than you intended…

    I figure Exceptions are called Exceptions because they should only be raised in exceptional cases. They should never be used in the regular flow of an application. (Just never tell that to a Python developer!)

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *