Live data from Hacker News

Code Design Decision – Always throw custom exceptions

github.com

71–80 of 92 posts

Re: Code Design Decision – Always throw custom exceptions

#71
post #62
post #8

Just because it's useful to wrap exceptions in 5% of the cases doesn't mean you should wrap the rest 95% just in case . YAGNI. 1. You don't know which exceptions will be raised in advance. Anything that involves IO can fail in a plethora of ways, and you don't even know which calls involve IO (e.g. a library might choose to cache something on disk). 2. Consumers of your code will not know how to deal with those excep…

I can't agree with any of these points. 1. You don't know which exceptions will be raised in advance. Anything that involves IO can fail in a plethora of ways, and you don't even know which calls involve IO (e.g. a library might choose to cache something on disk). Not sure how this relates to the code design decision 2. Consumers of your code will not know how to deal with those exceptions. Not sure the point here bu…

> Really don't think an extra trace in the stack is a reason to not create a well defined contract.

It absolutely is IMO. Stack traces are the main reason to use exceptions at all, and having multiple layers of useless wrapping around them is one of the biggest frustrations when trying to understand and debug an issue.

Re: Code Design Decision – Always throw custom exceptions

#72
post #8

Just because it's useful to wrap exceptions in 5% of the cases doesn't mean you should wrap the rest 95% just in case . YAGNI. 1. You don't know which exceptions will be raised in advance. Anything that involves IO can fail in a plethora of ways, and you don't even know which calls involve IO (e.g. a library might choose to cache something on disk). 2. Consumers of your code will not know how to deal with those excep…

> Most of exceptions are unrecoverable (that's why they are called exceptions), the best course of action is to crash, which happens by default.

This is why D separates exceptions into two categories:

1. Error - these are not recoverable. The only reason to catch them is to maybe try to save some state or log a message or shut down the reactor before crashing.

2. Exception - these are recoverable

(I'm being facetious. Any system design where, while unwinding a fatal error, one relies on it to shut down the reactor is a horrible, terrible design.)

Re: Code Design Decision – Always throw custom exceptions

#73

There are some nice arguments for not overengineering in the comments here, as well as some for there still being merit in explicitly saying what could (typically) go wrong in the execution of a program. We've already seem the extreme of "never create your custom exceptions", or even something to the tune of "exception handling is a maintenance burden", but I can't help but to wonder about going in the exact opposite…

> What if we had a language that only had checked exceptions and forced you to deal with anything that can go wrong. Launching a program? You better have some code to deal with an out of memory exception, or some code for dealing with a stack overflow exception. Working with some maths? Well, you better define what should happen in the case of number underflows or overflows, as well as division by zero or whatever else can be inferred by what operators you use. Dealing with a network? Well, be prepared to handle dozens of types of network failures, the brittleness of networks being laid bare to you. Want to deal with reflection? Well, there would probably be none, to avoid too much dynamic behavior.

> It's rather obvious why we don't really have languages like that, but at the same time - if writing code in a language like that would be "possible", then surely there's a domain or two out there that might benefit not just from enforced 100% test coverage, but also every single possible error being handled, or at least laid bare. If a program should crash upon particular errors, then the developer might say so explicitly, or otherwise provide logic to recover from those, without ever missing any place where things could go wrong.

A lot of functional languages go in that direction. E.g. if you stick to the non-IO fragment of Haskell then it mostly works like that - things that can error return Either that you have to handle explicitly, and while there's sugar to let you work with that in a similar way to exceptions it will never be entirely hidden. (Within IO you can have exceptions, of course; making a proper algebraic model of how e.g. network I/O works is pretty daunting). Idris or especially Noether go even further in that direction.

Re: Code Design Decision – Always throw custom exceptions

#74
post #53

Earlier quoted context omitted.

Very few languages actually support Either or Result types ergonomically. I don't have a problem with people using exceptions for these but I do think if the language has compile time type checking it should provide a distinction between checked exceptions and non checked exceptions. I am very much in the minority here but I find it very useful to let robots tell me I forgot something than to discover I forgot it in…

Yeah I agree. I think the problem with checked exceptions in Java is a combination of misuse on both the consuming and the throwing end. They're amazing when they provide a compile time checked way to make sure consumers handle stuff they actually definitely want to handle , but if you add too much noise then all the consuming code is just going to catch and rethrow even the important ones.

I can't remember a single use case when library could definitely know that consumer must handle that particular exceptions.

100% of checked exception uses must be unchecked.

Re: Code Design Decision – Always throw custom exceptions

#75
post #62
post #8

Just because it's useful to wrap exceptions in 5% of the cases doesn't mean you should wrap the rest 95% just in case . YAGNI. 1. You don't know which exceptions will be raised in advance. Anything that involves IO can fail in a plethora of ways, and you don't even know which calls involve IO (e.g. a library might choose to cache something on disk). 2. Consumers of your code will not know how to deal with those excep…

I can't agree with any of these points. 1. You don't know which exceptions will be raised in advance. Anything that involves IO can fail in a plethora of ways, and you don't even know which calls involve IO (e.g. a library might choose to cache something on disk). Not sure how this relates to the code design decision 2. Consumers of your code will not know how to deal with those exceptions. Not sure the point here bu…

If the database is unresponsive, I would prefer to return 5xx to the caller as soon as possible. May be this service is not so important and it's better to present to user an incomplete page rather than waiting for hours or days until database is available.

If it is important, caller will call the service until he got response.

Again: fail fast. Even in distributed systems. Unless you're 100% sure that you know better how to handle this particular issue.

Re: Code Design Decision – Always throw custom exceptions

#76

Like others have said, in theory this is great. In reality, I never see custom exceptions being handled differently than whatever exception was wrapped. And I have worked on some large distributed systems where failure is common. For new engineers these custom exceptions add abstract complexity and exception class hierarchy into a code base when it really isn’t needed.

YAGNI principle should be followed here. Do not throw custom exception until you need it. If the time arises, do a simple refactoring.

For libraries that's more nuanced and good unchecked exception hierarchy is a part of good API.

Re: Code Design Decision – Always throw custom exceptions

#77
post #6

In short: if you have a meaningful recovery pathway for a particular exception this can be useful, but I found that 9 out of 10 exceptions/errors in code cannot be recovered from. This is checked exceptions all over again... Frankly, I usually do exactly the opposite. Most cases I've seen the exceptions that can arise are not widely known in advance, and for most spots where exceptions can be raised from I simply do…

> This is checked exceptions all over again... That is an interesting topic, as I went through the cycle of adding and removing them, they always felt like the right thing, but they felt like "work". They make coding less fun, having to think through failure conditions, like you say, you don't care about. I guess the answer is really there is no one sized fits all solution, we have some applications where the correct…

"exception specifications" are deprecated since c++11 and completely removed in c++17 if I'm not mistaken.

Re: Code Design Decision – Always throw custom exceptions

#78
post #62

Earlier quoted context omitted.

I can't agree with any of these points. 1. You don't know which exceptions will be raised in advance. Anything that involves IO can fail in a plethora of ways, and you don't even know which calls involve IO (e.g. a library might choose to cache something on disk). Not sure how this relates to the code design decision 2. Consumers of your code will not know how to deal with those exceptions. Not sure the point here bu…

If the database is unresponsive, I would prefer to return 5xx to the caller as soon as possible. May be this service is not so important and it's better to present to user an incomplete page rather than waiting for hours or days until database is available. If it is important, caller will call the service until he got response. Again: fail fast. Even in distributed systems. Unless you're 100% sure that you know bette…

Totally agree about returning a 5XX error. In my app that would be a single RetryableException back to the caller instead of the internal db error.

Re: Code Design Decision – Always throw custom exceptions

#79
post #34

Earlier quoted context omitted.

The whole DomainError thing only makes sense if you expect someone to handle this error (and by handle, I mean doing something other than logging and aborting some operation), or if you are writing a very generic library. Otherwise, it is wasted time and extra complexity that makes the code harder to read.

It depends on how much context is needed. Imagine you have a Go library that parses JavaScript. Parsing can return an error, and it's very helpful to know information like the line and column number. So you might have an error like this: type ParseError struct { Line int Col int Message string } The user of your library isn't expected to handle this error explicitly, but when they print the output, they can see exact…

Do you expect any piece of code to touch err.Line and err.Col? I definitely don't - I would expect the line and col to be included in the message of a basic string error.

Re: Code Design Decision – Always throw custom exceptions

#80
post #54
post #50

Earlier quoted context omitted.

> If the caller has no way to handle it, it should wrap the checked exception in an unchecked exception and throw the unchecked exception. Not you have a new problem: How is the code calling the caller supposed to know about that exception? You can't even catch it (even if you know about it!) using normal try-catch because what you have to do is catch the wrapper exception and then check inside that for the exception…

The error here is not that they are wrapping the exception necessarily. It is that they re-threw it as an unchecked exception. I strongly believe the only good use of unchecked exceptions is if the code should do the closest reasonable thing to crash safely. Everything should be clearly communicated to the callers so they can make good decisions about what to handle here and what to pass up the chain. If the complain…

> The error here is not that they are wrapping the exception necessarily. It is that they re-threw it as an unchecked exception.

You often don't have a choice. Think of generic interfaces -- for example the humble apply method on https://docs.oracle.com/javase/8/docs/api/java/util/function... .

If you have a Function which needs to do some interruptible work you cannot throw InterruptedException -- you must wrap it. This is a fundamental design flaw in Java's exception system and cannot be handwaved away just by saying that Function is badly designed. This problem is pervasive.

The ultimate problem here is one of variance -- throws clauses have the opposite variance rules from method implementations: Subclasses (whether of interfaces or classes) frequently need to more than could be foreseen by the implementor of the interface, so they need to be able to throw "more things", but checked exception clauses explicitly disallow widening the set of thrown exceptions in subclasses (for obvious reasons -- since a FooImpl can be used a runtime where a Foo is expected).

This is a fundamental flaw that was overlooked in the checked exceptions design and there's no fixing it now.

Post reply on HN