Now and again I come across patterns that I see in codebases and pull requests, especially when the project or team is maturing. This covers one of them.
I've found that developers that have picked up more advanced patterns and become familiar with a codebase that is full of compromises made during it’s organic growth, they begin to apply those same patterns to solve the problem with a broad stroke, sometimes at the risk of not fixing the underlying issue.
One such pattern is try-catch. Once the power of a try-catch is learned, it starts being used everywhere. I’ve seen the whole contents of a method wrapped in a try-catch, catching anything it can. When all you have is a hammer, everything looks like a nail.
When you ask why they have used the try-catch, the answer is usually that they are trying to avoid the application producing unexpected errors. You might ask, "Why don't you wrap the whole application in a try-catch?", though it could be mistaken as flippant, it does promote the right thinking.
Uncle Bob explains that if you’re having to write in exception handling in your method, then you’re probably doing it wrong. Exceptions are meant to be exceptional.
Error handling is “one thing”.
— Uncle Bob Martin (@unclebobmartin) June 18, 2018
Similarly, catching an exception to then just report it back to the user isn’t great because you’re treating it as if it’s expected behaviour, when it isn’t. Exceptions should be exceptional.
Catching an exception only to report it is a sure overkill. Simply because uncaught exception is a fatal error already, and it will be reported by itself. Without that blunt try/catch/die sequence, making your code much cleaner.
Sadly, the PHP manual is especially bad at it, showing such examples all over the place. On the one hand, it is understandable as such an example is guaranteed to output an error message, regardless of the user settings. On the other hand, this when this approach gets mindlessly copy-pasted into the live code, it turns out to be superfluous, user-unfriendly and harmful.
https://phpdelusions.net/top#try_catch
Catching all “exceptions” or “catchables” makes it really hard to debug what’s going wrong. Avoid catching all “exceptions” or “catchables”, instead use exception handlers. This will maintain that exceptions are exceptional and handled exceptionally, and makes the application easier to understand and debug in the event of an issue.
If you’re validating some data, you usually shouldn’t be using exceptions to signal validation failures, if you’re expecting the failure, then perhaps it’s not an exception. Exceptions are exceptional.
“if a failure is expected behavior, then you shouldn't be using exceptions”
https://www.martinfowler.com/articles/replaceThrowWithNotification.html
You shouldn’t need to catch all exceptions. You’d have to have an exceptional reason to catch an exception, so use try-catch sparingly. Next time you see a try-catch, think, how can I refactor this so that the try-catch becomes redundant. Remember, exceptions are meant to be exceptional.
Exceptions come at a cost, and they are expensive. They have a performance impact on the software and a maintenance overhead.
Top comments (6)
IMHO, in the huge moajority of cases, try-catches fall into one of the following uses:
Not rethrowing an error in an inner try-catch is very rare, and 99% of the times, is wrong.
We still need to use exceptions. Dont misinterpret the article. Exceptions have an unique property: bubbling, and thats why they are necessary. Just dont use them where they are not meant to be used.
Good point...
Preparing our software to still rely with unexpected exceptions is crucial. But, as you referred, unexpected exceptions should be treated as something exceptional! There are few scenarios that are quite easy to identify those situations - depending on 3rd party systems/components, which we are abstracted about the implementation or using connections over the network, for example.
It would be interesting to consider a post which defined some guidelines when to think about a try...catch block to handle some unexpected behaviours. I'd be glad to help on that.
You're right. Exceptions are more important when you're using third party components because unexpected behaviour is, well exceptional to us.
It's difficult to handle unexpected behaviours, because they would be expected.
You shouldn't use a try-catch for unexpected behaviours, those should bubble up to the exception handler, rather than being caught by your code.
It's excepted behaviour that require a try-catch, for example, a failing connection is expected if the client throws an exception, in which case you might want to try something else when that happens.
Hope this helps to clarify.
I think we are aiming on the same spot, but with different perspectives :D
When I mentioned unexpected exceptions, I'm talking about behaviours that are not controlled by us (as developers of our own source code). If you rely on a framework, or library, and you are not into developing on it, there are some behaviours that you can predict, but you can't improve - those are the kind of unexpected behaviours that I'm talking about.
All the exceptions thrown by your own source code should be treated as bugs, not as exceptions!
Exceptions shouldn't be seen as bug handlers, but failure management mechanisms to manage communication between different pieces of software.
What do you do about input validation? I find i use exceptions the most when I have to handle input (from user or another system) and I have no idea what they can send me.
For instance, an empty string to a method where I do string manipulation. How do you let your caller know they input bad stuff? If you provide a Boolean “isSuccess()”, sure they can check for it, but you can’t be sure they will.
It’s in those cases I usually throw an e.g.: UserRegistrationException with a message like “Name not provided” to let them know
I think this may be a language specific scenario. For example, .NET has a
ValidationException
class which is used internally for server-side issues. Client-side validation hopefully does not throw an Exception, butValidationException
is often used internally for 400 series responses instead of 500 series responses.On the .NET side, another important note is that exceptions are not just different flow, but they're expensive operations as they require the framework to grab the full call stack, so I imagine other languages have similar cautions / limitations.
In general, understand that using exceptions adds complexity to the control flow of your application, making the application quality harder to guarantee.