Functional exceptions yes or no?
In a recent blog
post Anko raises the question if functional exceptions exist or if business rule validations should be handled differently. He states that he strongly believe that functional exceptions should not exist.
I have to say that I disagree with him. I do believe there is a case for both technical and functional exceptions. That doesn’t mean that exceptions are the only error notification strategy though.
Take for example a daybook entry in an accounting system. This consists of two general ledger accounts and the amount to be charged to each account. If the data entry form is designed so that the user can only pick two existing general ledger accounts they should never cause a business rules violation right? Wrong, because another user might delete one of the accounts before the daybook entry was written. Now the business rules could first check if the general ledger accounts exist but as the user can only pick valid ones this is not going to fail very often. Even if this check was done it there is always a moment between the check and the actual update. Sure you can prevent the account from being deleted by either locking the data, god forbid, of by wrapping everything in a single transaction. While this would work it would also mean that the connection to the database is kept open longer and the transaction lasts longer, both bad for scalability.
So what was the definition for an exception? According to Anko “An exception is the fact that something happens during the execution of the program you do not expect.” something I completely agree with. So can we consider the case where one user deletes a general ledger account at the same time as another user first uses it an unexpected event. You can argue this either way and both would have a point :-) However this is not going to happen very often. In fact it is very unlikely to happen at all during the lifetime of the application. Now writing the code inside a transaction that is automatically reverted in the case of an exception is easy and takes very little, if any, code. Writing the checks and reverting the updates manually takes quite a bit more code and most, if not all, of the exception code is never going to execute. I don’t like writing code that is never going to execute, seems like a waste of time :-(
That leaves another point, the execution speed.
It is completely true that exceptions are slow, even every slow, as compared to checking for and returning a boolean or enum indicating the failure. For that reason I would never use exceptions as the only means to indicate failure in a tight loop. However the scenario described above is the result of a user saving a single daybook entry, hardly a tight loop in my book :-) Suppose the same business code can be executed from another business process that might be in a loop. In that case I would add a validate function to check, without exceptions, of an entry is valid. This code can be independent of the first update code, manage it’s own connections etc. The tight loop could use this extra function to validate bookings before actually committing them, greatly decreasing the likelihood of an exception.
So what is my recommendation?
- Write the code using exceptions as this produces more reliable and readable code.
- Add optional validation functions that validate the business data without throwing exceptions.