5 ms·
Interesting article and great blog post too. I was lucky to start my career with a mentor who considered well written exception handling core to software devel
by kinow 5y ago
Interesting article and great blog post too.
I was lucky to start my career with a mentor who considered well written exception handling core to software development.
And he was always happy to stop his work (maintaining a core middleware in a telco with millions of transactions every minute) to review this intern's poorly written try/catch, show how he would write it, and why.
To this day I am thankful and believe his concerns over exception handling, good and simple deployment techniques, knowing networking to OS/kernel level, helped me becoming a better developer (or at least knowing where I need to improve).
- jimbob45 5y agoTo be clear, did he consider exceptions something to be avoided when possible or did he consider them to be a natural element of programming? I know exceptions do cause slow-downs on systems and most programming does its best to minimize occurrences within reason.
- kaetemi 5y agoThrowing exceptions is slow. Adding exception handling code itself has little to no overhead. Only throw exceptions in exceptional cases, not for frequently expected outcomes.
- kaetemi 5y agoAdding to that, assume any C++ code can throw exceptions unless it's specifically stated it won't. If you're doing manual memory allocation in a function, your exception handling might be leaky. (gsl::finally is your friend.)
- renewiltord 5y agoJava exceptions build stacktrace at construct time, which makes for the fun fact that instantiating an exception is the majority of its cost. At least the last time I checked. Actually throwing it is some fifth of the cost. This is relevant in the log-and-throw-e situations.
- amelius 5y agoImagine you're writing a function that parses an integer from a string. Would you let it throw an exception on failure?
- kaetemi 5y agoDepends on whether you call the function `parse` or `tryParse`. :) Throw if you expect that the input should never be invalid. Don't throw, and do friendly logging and reporting of your parsing errors, if this is uncontrollable third party input.
- gwbas1c 5y agoThe downvotes are unfair for an extremely good question. (I used to ask questions along this line when interviewing candidates.) Exceptions are used to communicate an unexpected error. Specifically, an error that the caller doesn't expect to handle during normal operations of the program. These errors can range from unusual situations like a network failure, to even more perverse situations like true bugs in the program. The example I discussed with job candidates was implementing a database access function called GetUserById. (In C#, pre compiler enforced null checking.) I would ask if the function should return null or throw an exception when there was no user with that ID. What followed, (with the candidates who passed,) was a discussion about the trade-offs of returning null versus throwing an exception. Null allows the caller to know that there was no user with that ID without the overhead of the exception. But, returning null increases the risk of a NullReferenceException. This is risky, because it's harder to debug then a strongly typed exception with a useful error message. Thus, the "right" approach depended on if it was anticipated that someone calling GetUserById expected the user ID to always be for a valid user. When there was time, we'd even get into the TryGet pattern that the .Net dictionaries use. (By the way, now it's a good time to check out how Rust's enum type is used with error handling. It's really slick with no overhead.)
- wyager 5y ago> I would ask if the function should return null or throw an exception when there was no user with that ID. My answer would be “neither”. Both of these options are bad. Null is maybe less bad because it at least roughly approximates the algebraic type that accurately represents the image of the function. Ideally you would return some type like Optional<User>. I suspect this is what you’re referring to with the Rust reference. A slightly more generalized version of this is to use the Either/Result monad, which can nicely handle multiple types of exceptional behavior at once.
- jwdunne 5y agoHrm, not the OP but, personally, it depends. If you’re using a language without algebraic data types, you’d have to ask whether it’s perfectly normal for a record not to be found by ID at that point. If it’s unusual, you should probably just throw the exception at that point because: 1. You check null and throw anyway, which gives you a stack trace just a little off the mark. That isn’t too bad but it’s more code for little gain. 2. There’s every chance you could forget to handle the null (It’s not the nil, it’s my discipline, I know, but it happens, we’ve all been there) and then you get a null pointer exception at some point later on in the flow, which is like the above but even worse. If you’re using algebraic data types, you wouldn’t use an optional. There are a number of ways a DB look up can fail so Optional isn’t really ideal at all. Your suggestion of Either/Result is probably the ideal there. Chances that you expect an explicit DB look up by ID to return nothing is pretty rare. Where has the program or client code got that ID from in the first place? Either you want to know about the error in your own code or you want the client code to know the error so you probably want some form of exception or Either. In the rare case it is intended behaviour, yeah you could use null and push the responsibility for handling it to the calling code. Still, at that point, 2 kicks in - might be fine now but that assumption could easily change as the code evolves, which it can and often does. You could argue you can implement something like Optional in, say, Java w/o algebraic data types but they are simply not ergonomic when the language does not help you use them. But I don’t think you can hold up a single approach and say that’s the ideal. I doubt it was your intention but it’s rather dogmatic and doesn’t really play out nicely in real world code, especially legacy projects (not everything is written in Haskell|Rust).
- kinow 5y agoI think he never considered the exceptions to the something to be avoided since he wrote Java most of his professional career - which ended when he decided to quit programming and go sell ornamental flowers in his family business in his hometown. The middleware we worked had libraries from other companies (e.g. online prepaid transactions were handled by code from a jar/lib from Ericsson). But in the case the platform threw an exception, we could still charge the customer using a slower process (i.e. there's a window of time where the customer could use data/talk in excess until we realize there's no more credit when processing the slower transaction). For him it was important to write these exceptions well. If something went wrong in production, we couldn't turn off the system, and if we were not charging users when we should to, the company would be losing millions (it was in Brazil, ~200 mi at the time, and the telco had ~60mi users I think, with mother's day and big brother being the craziest days with millions of messages per second.)