I was recently made to question my use of exception handling by a programmer at a new client.
I 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, I concede that sometimes unexpected exceptions occur and I can’t avoid the user being presented with a “Oopsie!” message. But my 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 I 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 I try and do. I thought it was good practice. Well, after a little thought and a little reading it seems that although my intention is right, the implementation is wrong diddly-wrong.
So like I said, 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. I was surprised and reluctantly agreed but I 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.
I knew all this, so it made me think, why am I doing things differently in my own projects?
I realised that possibly my focus has been on “prevention rather than cure”. When I say “prevention”, I mean I have always coded in such a way that assumes that things will error and that I should prevent these errors from fucking 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 freakin’ brake should not fail and there should not be a need for a second.
So, code should be allowed to error. I should let it error, and then fix the bug. Find more errors and fix the bugs.
Where did I go wrong?
So why do I code like this? Where does this come from?
I look back at the places I have worked and coded. With a couple of exceptions, I 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, I have learnt and taught myself a lot. I could have remedied this. I have worked with many fantastic developers, one in particular Paul Lemon at MadeByPi once told me that “if I 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.
What I need to know now is, where is the compromise? I don’t want my deploy my UI, 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. I want to be able to place handling code in my 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 I code in this handling after testing? Wouldn’t this defeat the purpose of testing and actually require further testing?
What I do know is that I have to stop placing secondary ‘brakes’ in my code. Doing so is actually preventing me from becoming a better programmer.