Shoggoth
kiwifarms.net
- Joined
- Aug 9, 2019
Like in everything in life, I think the answer is "it depends"I have to say, for all the insults flying back and forth on this, I'm learning quite a bit from this kerfuffle. Some of it is above my head but it's fun to read.
I have a question for the big brains here about exception usage. I've been programming for a few years, I use exceptions plenty and I know how they work. But I have never found a good overview on the best way to approach error handling in a project. Oh, a tonne of articles and instructions on how to throw an exception or make a new type of exception, etc. But whilst I can find a bajillion articles and books on how to use SOLID, how to use OO and on and on... if there's anything that gives a really good set of best practices for exception handling it's lost in the chaff.
I feel I don't even know it well enough to form good questions on it. Off the top of my head:
Should you use Exceptions for process control. I mean, should I be returning false from a method on failure and checking for that or should I be throwing an exception and surrounding calls to the method with try {...} catch {...}. I see a lot of the latter rather than the former.
How far up do you go before you catch and handle an exception. My IDE complains if it finds an Unhandled Exception possibility in a method and... I agree with it. To me, exceptions should be handled then and there but other people seem to think it's fine for a method to throw an exception, that passes up to a calling method which passes up to a calling method which then has a try catch to pick that (or particular types) of exception up and then and only then have some handling.
How and when do you start introducing custom types of exception? Do you have some kind of general exception / error handling class across a whole project? And if so, how do you approach that?
I could go on - it's more of a general lack of good practices than a lack of understanding of how to actually do something. Like I say, if I want to find "this is how you throw an exception" crap, there's no end to it. If I want "composition vs. inheritance" articles, there's a legion (most of them copy-pasting each other). I have no real idea what the best practices and standard approaches are for error handling across a large project nor ever seen a really good book on the subject.
For example, it depends on the language and runtime at your disposal.
Generally, control flow introduces complexity. Both implicit (as you do with exceptions) and explicit (just control structures). Exceptions are like goto but you don't even know where you're going, just trusting someone will catch you when you fall.
The exact opposite of control flow is logic programming. In it, control flow is abstracted entirely in the runtime and things are just resolved correctly. The price is usually performance, so unless you code is turning into unmaintainable spaghetti, I'd avoid it.
A good middle ground is pattern matching. Languages like Rust, Scala, ML and Haskell allow you to match on both type and value. They're usually compiled to optimal control flow structures.
If you do use exceptions, think about the domain you're working in. It may warrant its own exception type(s). Apache Kafka's java client library throws exceptions with the base class of KafkaException, or just regular java exceptions. A RuntimeException is just what it says on the tin, no need to invent a new one. If it fits the use case, use it instead of a new type.
I agree with you that exceptions should ideally be handled locally, even if handling is just rethrowing it. You should ask yourself if you're programming defensively, though. I came across a file once where every method started with try and ended with catching a general exception. To me it means the programmer didn't really know what they were doing and had no control over their inputs. Try to a line between what you know will succeed (no exceptions needed), what may fail and what should fail. Exceptions are for exceptional circumstances. Sometimes you want the runtime to explode in your face and throw an exception. Sometimes you want to return a "Result" type. Take a hash map lookup, for example. Should a missing key throw, or return a value which indicates a failure?
Exceptions can be nice when they let you escape deep call stacks or structures, but like I said previously, you don't know where you're going. If it's up to the programmer, I'd rather you returned an Option<T> rather then threw an exception.
There are also performance considerations to exceptions, where on the JVM try blocks can't get fully JITed.