Exception handling – why prevention is worse than cure
Tags:
Published: 21 June 2011
We were recently made to question our use of exception handling by a programmer at a new client.
We have always been fastidious about logging errors but ultimately trying to prevent exceptions from reaching the UI of the application. In the context of a website, we concede that sometimes unexpected exceptions occur and we can’t avoid the user being presented with a “Oopsie!” message. But our philosophy has always been to not upset the user. There are many ways to upset a user and moving the user to a new “Whoops, we did something wrong!” page would certainly upset the user.
So we would normally, try and catch errors as they happen and then handle them in some manner:
- Log the exception as standard using Log4net;
- Use ELMAH to catch all unhandled exceptions;
- Return a response object that encapsulates the return value and status of the method called;
- Do not move the user away from the page they are currently viewing but display a ‘friendly’ message instead;
- Ensure they can not easily repeat the action that caused the exception;
- Ensure the user cannot complete the process they are taking part in if the exception might cause further issue;
- With the exception of mis-configuration, ensure that exceptions are not thrown across the boundaries of independent libraries;
This is what we try to do. We thought it was good practice. Well, after a little thought and a little reading it seems that although our intention is right, the implementation is wrong diddly-wrong.
An epiphany
A programmer, Ollie, at my new client stated that in the project we would be working on together, exceptions will be allowed to bubble all the way up to the UI. We were surprised and reluctantly agreed but we did know that this approach had several merits:
- Provided that enough time was given to testing, the application would become more robust as all exceptions would be very visible;
- There is a reduction in code complexity as there are limited constructs, routines and general code paths for handling exceptions;
- All exceptions are logged in exactly the same way and in one place;
- The concept is straightforward and easy for new programmers to adopt.
We knew all this, so it made us think, why are we doing things differently in our own projects?
We realised that possibly our focus has been on “prevention rather than cure”. When we say “prevention”, we mean we have always coded in such a way that assumes that things will error and that we should prevent these errors from messing processes up. It’s the equivalent of manufacturing a car with a second brake that kicks in just in case the first fails. The first brake should not fail and there should not be a need for a second.
So, code should be allowed to error. We should let it error, and then fix the bug. Find more errors and fix the bugs.
Where did we go wrong?
So why do we code like this? Where does this come from?
We look back at the places and projects we have worked and coded. With a couple of exceptions, we have always seemed to work for companies where testing is considered surplus to requirement. The kind of places where the jobs are always under-quoted and timings are under-estimated. These places are great for learning quickly, but terrible for learning correct and well thought-out implementation practices.
Of course this is no excuse, we have learnt and taught ourselves a lot. We could have remedied this. We have worked with many fantastic developers, one in particular Paul Lemon at MadeByPi once said that “if we have not found any errors, there is something wrong” and this has always held true. There being no errors generally means that my exception handling is not working and not notifying of errors.
Questions?
What we need to know now is, where is the compromise? We don’t want the UI deployed, no matter how much testing it has been through, with the knowledge that an exception will bounce the user through to a generic 500 page. We want to be able to place handling code in the UI to allow as little interruption as possible in the case of an unhandled exception, but obviously this does not fit in with the concept of allowing all exceptions to bubble up.
Do we code in this handling after testing? Wouldn’t this defeat the purpose of testing and actually require further testing?
What we do know is that we have to stop placing secondary ‘brakes’ in our code. Doing so is actually preventing us from becoming better programmers.
