Skip to content

Instantly share code, notes, and snippets.

@cies
Created November 7, 2019 13:55
Show Gist options
  • Save cies/f0314dc30e7e800cca30de1bf521ff91 to your computer and use it in GitHub Desktop.
Save cies/f0314dc30e7e800cca30de1bf521ff91 to your computer and use it in GitHub Desktop.
xceptions in general and in Java/Kotlin/TS in particular

Exceptions in general and in Java in particular

The use of exceptions for anything but unrecoverable errors is greatly contested in programming. In this article we try to shed some light on this matter. But before we start digging deeper, here some considerations:

  • Unrecoverable is not very well defined. Sometimes it makes sense for software to crash: print some final log lines, try to do some cleanups and abort. Sometimes it does not: when we are in some code that simply needs to fail hard at that particular task/job/request (and surely log the "bug hinting error") but remain in service. Unrecoverable could also mean: cannot continue without a retry.

  • There is a common phrase "exceptions should be used for exceptional conditions; things you don't expect to happen" which does not add much to the discussion. What is truly exceptional is about as vague as truly unrecoverable.

  • Some languages, notably Java (but not Kotlin!), allow for "checked" exceptions. In Java these checked exceptions (inheriting from Exception but not from RuntimeException) are part of a method's signature (behind the throws keyword) and have to be caught or the code will not compile.

Some opinions (mostly from StackExchange)

This is the topic of a lot of debate in the software community. There are those like @thatidiotguy who believe that an exception would be perfectly suitable in this case. And then there are others like me who believe exceptions should only be used for exceptional circumstances where something actually went wrong; your scenario strikes me as an alternate flow easily solved with a conditional. But I am also not religious about this. If you go the exception route, just remember two things:

  • Make sure it is a checked exception as @thatidiotguy implied.
  • Make sure your exceptions don't duplicate existing exceptions provided by Java to say the same things. For example, if a client provides a string where you expect a number, don't make up your own "InvalidFormatException" or something. Use Java's built-in IllegalArgumentException.

From: https://stackoverflow.com/questions/19575951/java-exceptions-to-handle-user-inputs

Personally I like throwing Exceptions for business rule validation (not so much for user input validation) because it forces the problem to be handled upstream. If my business objects returned some kind of validation result, it could be ignored by the caller. Call me a cowboy if you wish :)

And...

Using exceptions and catching them can really clean up code, especially in multi layered systems.

And...

The performance of throwing an exception is a thousand instructions. It's nothing compared to end-user time, but in inner code it is slow.

An additional problem is that, using Exceptions, your validation is limited to reporting the first failure (and you will have to do it all again next time to find the next failure).

And...

In the title you call it "validating" data. That can happen on several levels. In (near) the GUI where you are checking user entered data, you should be expecting errors and have ways to report the errors back. Exceptions are inappropriate in this case.

But Data Validation can also happen at other boundaries, say between business-rule classes. There, errors in the data are uncommon and unexpected. You should throw when you detect one.

And...

I would suggest that using exceptions as described in the question (for flow control within a function) not usually the best idea. I'd go further and saying validation throwing exceptions isn't the best approach; instead return a Boolean and store a list of validation error messages that can be accessed. [...] As a user I'd prefer to get all validation errors returned at once so I can correct them all before trying again.

From: https://stackoverflow.com/questions/1504302/is-it-a-good-or-bad-idea-throwing-exceptions-when-validating-data

If you use exceptions only for exceptional cases you can run in your debugger with the debugger setting "stop when exception is thrown". This is extremely convenient because you drop into the debugger on the exact line that is causing the problem. Using this feature saves you a fair amount of time every day. [...] When I program Java, this is not really possible because so many things uses exceptions for non-exceptional cases, including a lot of the standard java libraries. So this time-saving feature is not really available for use in java. I believe this is due to checked exceptions

And...

Exceptions create non-obvious code paths, and could therefor be hard to maintain [paraphrased]

And...

When using exceptions, the error handling code is separated from the code causing the error. This is the intent of exception handling - being an exceptional condition, the error can not be handled locally, so an exception is thrown to some higher (and unknown) scope. If not handled, the application will exit before any more harm is done. [...] [Instead of using exceptions for recoverable errors, you] can define an abstract ErrorProvider interface, or return a complex object representing the error rather than a simple code. There are many, many options on how you retrieve error reports. Using exceptions because the are convenient is so, so wrong.

And...

The problem with exceptions in most languages is they change the rules of program flow, this is fine in a truly exceptional circumstance where it is not necessarily possible to figure our what the valid flow should be and therefore just throw an exception and get out however where you know what the flow should be you should create that flow (in the case listed it would be to raise a message to the user telling them they need to reenter some information). Exceptions were truly overused in an application I work on daily and even for the case where a user entered an incorrect password when logging in, which by your logic would be an exception result because it is not what the application wants. However when a process has one of two outcomes either correct or incorrect, I dont think we can say that, incorrect, no matter how wrong, is exceptional.

And...

One problem with using exceptions is a tendency to detect only one problem at a time. The user fixes that and resubmits, only to find another problem! An interface that returns a list of issues that need resolving is much friendlier (though it could be wrapped in an exception).

From: https://stackoverflow.com/questions/410558/why-are-exceptions-said-to-be-so-bad-for-input-validation

Exceptions were invented to help make error handling easier with less code clutter. You should use them in cases when they make error handling easier with less code clutter. This "exceptions only for exceptional circumstances" business stems from a time when exception handling was deemed an unacceptable performance hit. That's no longer the case in the vast majority of code, but people still spout the rule without remembering the reason behind it.

Especially in Java, which is maybe the most exception-loving language ever conceived, you shouldn't feel bad about using exceptions when it simplifies your code. In fact, Java's own Integer class doesn't have a means to check if a string is a valid integer without potentially throwing a NumberFormatException.

Also, although you can't rely just on UI validation, keep in mind if your UI is designed properly, such as using a spinner for entering short numerical values, then a non-numerical value making it into the back end truly would be an exceptional condition.

From: https://softwareengineering.stackexchange.com/questions/184654/ive-been-told-that-exceptions-should-only-be-used-in-exceptional-cases-how-do

[Some guidelines] influenced by the great book "Code complete":

  • Use exceptions to notify about things that should not be ignored.
  • Don't use exceptions if the error can be handled locally
  • [...]

And...

Reasons to use exceptions:

  • The code flow for the common case is clearer
  • Can return complex error information as an object (although this can also be achieved using [a Result object] passed by reference)
  • [...]

From: https://codereview.stackexchange.com/questions/11724/is-it-better-practice-to-have-void-method-throw-an-exception-or-to-have-the-meth

Conclusion

The tendency is to greatly restrict the use of exceptions more recently designed programming langugaes, i.e.: Kotlin, Go, Rust. These languages often do not provide a mechanism that is useful beyond unrecoverable exceptions. They all point to Result objects as the answer.

Java is probably the most exception-happy language out there, it provides a rather clean implementation of exceptions (especially compared to C++). The use of Java's checked exceptions for very-recoverable-errors is what Java programmers expect in APIs: they are widely used for communicating that a method can "return by exception" a recoverable error state.

Within the Java communinty there are some that argue against the use of exceptions for recoverable errors, considered purists, that some that argue in favor of them, considered pragmatists. The point that Java does not have a terse way to represent "sum types" (a.k.a. discriminated union types) with exhaustability checks at switch statements, makes it hard to express Result objects (which non exception-happy languages point at to use instead of "recoverable/checked exceptions").

To conclude for Java:

  • Use unchecked exceptions only for cases that are not recoverable.
  • Avoid the use of frequently occuring exceptions in tight/hot loops for performance reasons.
  • Any exceptions for recoverable situations should be checked and caught as close to the throw site as possible.
  • The use of an Either or Result type of object is not idiomatic in Java, yet is possible and provided by FJ.

To conclude for Kotlin:

  • Only use exceptions for unrecoverable errors, always try to use Result objects.
  • Kotlin has sealed classes (an OO way to represent sum types a.k.a. discriminated unions) which require switch (when) statements to be exhaustive. These are perfect for Result objects and are the idiomatic way of dealing with errors in Kotlin.

To conclude for TypeScript

  • Like Kotlin: Only use exceptions for unrecoverable errors, always try to use Result objects.
  • TS has tagged unions (similar to sum types a.k.a. discriminated unions) and exhaustivity checks. These are a perfect fit for complex Result object, albeit not very idiomatic in TS (yet), probably because of the JS legacy which does not have these features.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment