If you google “error handling .net or “error handling java you get results about exceptions and exception handling. Therefore, many of us think using exceptions is the silver bullet for handling errors in modern .Net/Java applications. Yes, we could definitely use exceptions for application errors but I think we can do it in a much cleaner way.
The idea which is discussed in this article is initially introduced by Martin Fowler in 2004 in his article Notification.
Whats wrong with exceptions?
Exceptions are not completly wrong. In some of his articles (here and here) Vladimir Khorikov described in which cases exceptions should be used and in which cases not. I fully agree with his opinion, thus I try to summarize it.
There are two types of errors. First, there are errors, we don’t know how to deal with. This type of error should be handled by exceptions because we don’t know how to handle this case. The best option is to terminate the current operation.
Second, there are errors, we know how to deal with. For example validation errors or transient/recoverable software and hardware failures.
If you need help separating these two error types, read that. We skip the handling of the first error type, because it is not issue in this article.
Handling errors we know how to deal with
Okay, so we want to handle errors we know how to deal with but without using exception handling. Any ideas? We could return a null object or false in case of failure but then we would lose a bunch of information concerning the reason of the failure. So a good solution (highly inspired by Martin Fowler and Vladimir Khorikov) is to return a Result object which indicates success or failure of an operation.
Here is some sample code:
public Result DoSomething()
{
// do something
// return success result
return Results.Ok();
// return error result
return Results.Fail("An error occured.");
}
public void CallDoSomethingMethod()
{
var result = DoSomething();
if(result.IsFailed)
{
// handle error
Console.WriteLine(result);
return;
}
// continue with program
}
The first method returns a Result object indicates success or failure. The second method — the client — checks the Result object and have to handle both cases, the error and the success case.
Pros
- Clear program flow, no intransparent jumps from throw in a catch.
- Method signature shows the returning Result object. So from the client’s point of view the error handling mechanism is absolutely clear. Returning more then one error is possible
Cons
- The client have to manually check if the operation fails. If you use exception handling you have no chance to ignore an exception.
- No built-in language support in C# or Java.
I built a small .Net library called FluentResults which provides a lightweight Result object. You can return multiple error or success messages. Each error can be designed as class and you can also describe the reasons or meta data of an error in an hierarchical way. Of course, returning a Result object with a value is also possible.
Links
This post was originally published on medium.com
Top comments (22)
I have to say, I disagree completely. Results indicate the request was processed and the caller got what they asked. Exceptions indicate that something went wrong. There are two classes of exceptions: (i) system exceptions: unexpected issues that are not the fault of the caller. The caller can try again with the same request (but only if the method is marked as idempotent), and (ii) Request-related exceptions: issues that resulted from something wrong the the request, and indicating that the caller must change something before retrying.
For example in REST, 200 means result obtained, 50* means system error, and 40* means request error. The header status code neatly indicates what the resultant payload will be.
This is not what a Result type represents. A Result is an abstract type that can represent either success or failure. A function that can fail should always return a result type, and it is up to the programmer to handle the Success case and the Failure case. The problem is, languages that already have exceptions built-in don't generally have a type-system that can accommodate Result types in a useful way.
If there were no built-in exceptions, you would be right - a result could be anything. That isn't the case, so a result in anything - it is the happy path. Everything else is an exception. Exceptions can have types and data...
The whole purpose of this article is to advocate a Result type as an alternative to exceptions. Like exceptions, they also have a type and associated data. But yes, they do not make sense in a language with exceptions.
This is not the reality of things though.
Check this post from Eric Lippert about how C# exceptions are usually used.
And most exceptions shouldn't even be thrown, if you, for example, supply an invalid string to a parser you ideally shouldn't get back an exception to handle (here Lippert's take.
And Eric Lippert was a part of C# design team.
The result pattern still makes sense for some use cases (for other TryDoSomething is better, for other Exception handling is the way to go), even in a language like C#.
Assuming Exceptions are always the way to go seems like a good recipe for sub-optimal design choices.
I'm saying that exceptions are the most appropriate error handling mechanism in languages that have exceptions. I'm not saying every method should throw exceptions.
I agree that a Result would be more appropriate for the
TryDoSomething
pattern if we could throw everything out and redo it, but we can't. So, introducing a Result type means that we now have: exceptions, theTryDoSomething
pattern, and a user-defined Result type.Having so many ways of doing similar things is a sub-optimal design choice.
You are right, you have multiple options. I think the problem is you don't distuingish between the different types of errors. Depend on that you habe to choose the correct error handling mechanism. Doing all with exceptions is Not the correct way.
Here is a great example using Result object: jimmybogard.com/domain-command-pat...
This article is not about handling errors over a REST service. We used the Result object to return the result of a method in a class. See above example, I thought it was clear enough.
I implemented in the last years also some REST services and there I used the Statuscode of the response to indicate failed or successful operations. So we used the baked-in error handling solution and it worked well. Sometimes we return also a detailed error message/error code in the body, so I think my approach in doing REST server error handling was very similar to the Result object approach described in this article.
It's becoming clear to my that my choice of example was unfortunate, but the point I was trying to make is that the language (or protocol) provides error mechanisms, and we should preferably use these, rather than bake our own.
In your example I'd consider the http Response similar to a Result object.
There are many REST advocates who suggest only using 200 responses for this very reason, and that HTTP status codes were not intended for REST error semantics.
I just think it's a shame that we can't use the built-in error mechanisms and have to bake our own.
Well we're somewhat on the same page, I very much dislike getting 200 when something is not OK!
As much as I would love to use a Result type in languages like Java and C#, the type system does not accommodate them. Furthermore, using a Result type in a language that already has exceptions means you have to contend with both error handling mechanisms. The very basic requirement for a Result type is a) algebraic data types and b) exhaustive pattern matching. Without these, you really shouldn't bother.
When I use / see a Result object, I expect the callee to take care of exceptions. But indeed, that's not a given.
Exactly!
I strongly believe the arguments against exception handling are due largely in part to the crappy syntax we have to deal with them. It is bulky to catch errors, it is smaller to get returns.
In my language Leaf I am ensuring that catching errors is as trivial as looking at return codes (easier actually), to ensure this argument can be put to rest.
I've written several articles on the topic of error handling, here are perhaps the most prominant:
Result
s are great when they're supported by a language (e.g. Haskell, Rust, Elm), but I think it's not unreasonable for callers of a given function to assume (non-null) returned values are 'correct' absent additional information.Also, you're losing all the benefits of any native type hierarchy, so you'd have to hand-craft any system for when you want to handle some
Result
s but not others. You'd similarly have to painstakingly wrap everything that could throw an exception, and pray that whoever comes along after you does the same, lest native exceptions escape.When creating a client API, I try to avoid taking sides in these kinds of debates. So I ask myself: How is the client supposed to behave, and how do I best communicate to the client's users? For example, a thin wrapper around HTTP calls should probably only throw exceptions to communicate a failure to make the HTTP call, but an API that hides its HTTP connections as implementation details may encapsulate 3/400-class errors in "it's the caller's fault" exceptions and 500-class errors in "it's the service/library's fault" exceptions. This is especially important if the client language uses exceptions to guide developers in their use of an API.
I generally prefer to let the conventions of the language and of the community of its users guide me. Both Java and C# contain rich resources for expressing ways things can go wrong. I generally use those resources unless I see a powerful reason to not do so.
But I see no hard rules here.
We've used this pattern a bit at my work. One pro for throwing exceptions is that they integrate with your error reporting software if you have any (e.g. Rollbar or Raygun).
You can rescue the exception and have it report, then return a
Result
object, but I've found this to be a bit cumbersome, and have gone back to a pattern of raising and handling exceptions.That's just our codebase though (Rails/custom Ruby framework microservices), so perhaps it's not a good fit for us, but might be for different apps :-)
I don't say that it is completely forbidden to use exceptions. Using exceptions is a great way to handle errors we don't know how to deal with. If its really an exceptional situation it should be logged somehow, for example in a error reporting software. In this case exceptions are great.
But for errors we know how to deal with it was for us a great benefit to use exceptions. For example we implemented a CSV Upload in C# and the CSV have to be valid, so the file have to pass many validation rules successfully. First we implemented it with exceptions => bad idea. One problem was that we want to display all broken validation rules and not only the first one.
Then we implemented a very basic Result object and tried it. We were satisfied with the code and so we also used this Result object in other components in our software. Until now we are very satisfied with it.
I do not believe this is better than exceptions. It forces every method to check for failure for all the methods it calls. Simple methods become huge constructs with lots of "if"s, because suddenly you have to check for all possible failures directly when calling a method instead of catching the ones you can do something about and give the other further outside automatically, completely ignoring them.
I agree with this, I have seen the dark side of avoid all exceptions, and it becomes a real mess and you have error checking code all over the place. I actual prefer exceptions for the reasons you mention in C#.