loading...
Cover image for Error handling - Returning Results

Error handling - Returning Results

michael_altmann profile image Michael Altmann Originally published at Medium ・3 min read

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

Discussion

pic
Editor guide
Collapse
rmorschel profile image
Robert Morschel

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.

Collapse
dubyabrian profile image
W. Brian Gourlie

Results indicate the request was processed and the caller got what they asked.

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.

Collapse
rmorschel profile image
Robert Morschel

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...

Thread Thread
dubyabrian profile image
W. Brian Gourlie

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.

Thread Thread
matteosaporiti profile image
Matteo Saporiti

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.

Thread Thread
dubyabrian profile image
W. Brian Gourlie

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, the TryDoSomething pattern, and a user-defined Result type.

Having so many ways of doing similar things is a sub-optimal design choice.

Thread Thread
michael_altmann profile image
Michael Altmann Author

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...

Collapse
michael_altmann profile image
Michael Altmann Author

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.

Collapse
rmorschel profile image
Robert Morschel

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.

Collapse
courier10pt profile image
Bob van Hoove

In your example I'd consider the http Response similar to a Result object.

Collapse
rmorschel profile image
Robert Morschel

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.

Thread Thread
courier10pt profile image
Bob van Hoove

Well we're somewhat on the same page, I very much dislike getting 200 when something is not OK!

Collapse
mortoray profile image
edA‑qa mort‑ora‑y

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:

Collapse
swizzard profile image
sam

Results 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 Results 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.

Collapse
dubyabrian profile image
W. Brian Gourlie

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.

Collapse
courier10pt profile image
Bob van Hoove

When I use / see a Result object, I expect the callee to take care of exceptions. But indeed, that's not a given.

Collapse
rmorschel profile image
Collapse
florianschaetz profile image
(((Florian Schätz)))

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.

Collapse
mrlarson2007 profile image
Michael Larson

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#.

Collapse
raquelxmoss profile image
Raquel

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 :-)

Collapse
michael_altmann profile image
Michael Altmann Author

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.

Collapse
adamvinueza profile image
adamvinueza

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.