Question

I was watching the following video by Vladimir Khorikov, which recommends to "Refactoring Away from Exceptions" pluralsight.com - Applying Functional Principles in C# - Refactoring Away from Exceptions and instead using a Result object. You can also find a blog about it here: enterprisecraftsmanship.com - Functional C#: Handling failures, input errors

To summarize it, the recommendation is to prefer returning a result object then throwing an exception. Exceptions should be used to signalize a bug only. The arguments for this approach are the following:

  • Methods which throws exceptions are not "honest". You can't recognize if a method is expected to fail or not, by looking at its signature.
  • Exception handling adds a lot of boiler plate code.
  • When exceptions are used to control the flow, it has a "goto" semantic, where you can jump to specific line of code.

On the other hand return values can be ignored (at least in C#), which exceptions can not.

Is it a good idea to refactor a existing enterprise application in this direction? Or is a less radical approach the better one? (I belive that it make sense for sure to avoid Vexing exceptions by using return types for method like ValidateUserInput(string input))

Note that Are error variables an anti-pattern or good design? is a similar question. The difference is, that I am not talking about "Error by magic values" (returning a error code or even worse null) which is clearly an anti pattern. I am talking about the pattern presented by Vladimir Khorikov, which doesn't have the same drawbacks like just returning a primitive error code. (For example: Result objects have a error message, like exceptions does)

Was it helpful?

Solution

the recommendation is to prefer returning a result object than throwing an exception

This is bad advice IMHO, because they should be used in different circumstances. Using a result object when an exception is most appropriate is bad.

If ValidateUserInput is not able to perform its task because (for example) the process is out of memory, then throwing an exception is the right thing to do. Returning a value that indicates that the input is either valid or invalid would obviously be wrong since we don't know that. Calling "out of memory" a validation error would be misleading, since it is not an error in the input. Creating three cases: valid, invalid and out-of-memory would be bizarre.

Exceptions should be used to signal when a method is not able to complete its task. Result objects is for when a method is able to complete its task, but the result can falls into different cases.

For example a Search() method might return an option type with a case when a match is found and another if no match is found. But it should throw an exception if the database is disconnected, there was a timeout or anything else which means the search couldn't be performed.


So what about the argument in the linked blog post, which say you should always use result-objects instead of exceptions?

The post suffers IMHO from a a symptom I would call language envy. It tries to shoehorn patterns from a different language without considering the different context of C#. Typically you end up with worst of both languages. Some points to consider:

  1. .net still have exceptions. Whether you like it or not, the framework and 3rd party libraries will still throw exceptions.
  2. .net exception are unchecked which means you don't know at compile time exactly which exceptions a method may throw.

This means you can't really get the purported advantages by turning all errors into a result-object. For example:

Methods which throws exceptions are not "honest". You can't recognize if a method is expected to fail or not, by looking at its signature.

If you think this is an issue, it is not going away. The framework and 3'rd party libraries still don't advertise what exceptions they may throw, which means your code could also pass on an unadvertised exception.

If you consider this a problem, consider using a language like Java instead, which does have checked exceptions - or a language without exceptions.

Exception handling adds a lot of boiler plate code.

No more that encoding errors in result values! Result values require boilerplate to wrap, unwrap and to pass up the up the call stack - something which happens automatically (no boilerplate) for exceptions.

In a language like Rust with uses result-objects throughout, it was quickly realized how much boilerplate it required, so a shortcut syntax (first the try macro, then the ? operator) was introduced to alleviate that. Haskell has the do-syntax. C# does not have anything similar, and doesn't have macros, so you have to suffer the boilerplate.

The article ends up rewriting everything into non-idiomatic C# with a chain of OnSuccess calls which end up being much more complex than the initial code it was supposed to simplify! (The article cheats a bit by have some needless redundancy in the "before" code which is magically gone in the "after" example.)

More importantly, the initial problem which the code attempted to solve, the transaction rollback, could be more elegantly solved in an idiomatic way by using a using-block. The use of RollbackLastTransaction seem like is could be vulnerable to race conditions.

When exceptions are used to control the flow, it has a "goto" semantic, where you can jump to specific line of code.

This is a red herring because it is recognized as an anti-pattern in C# to use exceptions for control flow. That is not their purpose. But in any case, comparing exceptions to GOTO is a misunderstanding of why GOTO's are considered bad in the first place. If an exception is like a GOTO, then so is a return.

Bottom line: Choose the language which you prefer or which is most appropriate for the task. Then understand the design principles and idioms of the chosen language, and use them to your advantage instead of fighting them,

OTHER TIPS

There are use cases for both exceptions and result objects, so "cleanliness" of the code is dependent upon the use case. It all depends on how critical it is to stop the execution of the program.

Exceptions are the hand grenades of programming. Pull the pin and throw it. The only thing to keep your application from blowing sky high is to catch it and handle it (and we'll imagine for a moment you can put the pin back in to keep it from exploding). Exceptions communicate that you have encountered an error condition such that you absolutely cannot continue operation, and must halt immediately.

Result objects communicate something differently. They say "one or more things went wrong, and someone needs to correct them before continuing." Result objects can also say "Things went fine, but a few little warnings you should know about are ..." The intent of a result object is track errors and warnings so they can be reported back to an end user or some other form of output, like a log file. An operation that returns a "results" object is usually fine with failure, because some other process can recover from the failure.

Result objects provide feedback so another process or person can correct errors, and attempt the operation again. Exceptions are meant to halt the program execution before real damage is done to data.

The exception to using exceptions would be if damage (real or virtual) would be done by allowing the exception to propagate unchecked. For instance, if crashing the application would cause a loss of data then you might be better off catching the exception, and returning a results object. More importantly if a crashed program would cause physical, psychological or financial harm to people or animals, then you definitely do not want to leave exceptions unchecked. Again, a "results" object or "error code" is the right way to go, so the error can be corrected, and the operation attempted again.

Methods which throws exceptions are not "honest". You can't recognize if a method is expected to fail or not, by looking at its signature.

Compared to [older versions of] Java this was argued to be one of .Net's biggest failings. Java laces Exceptions right into the Method signature; .Net doesn't.

Exception handling adds a lot of boiler plate code.

Only when it is done wrongly.

You add an Exception Handler where you can do something useful with that Exception, not just because it's a "good idea" not to "leak" Exceptions. (This sort of boiler-plating is a very Bad Idea).

  • When exceptions are used to control the flow, it has a "goto" semantic, where you can jump to specific line of code.

"Goto semantic"? OK.

"Specific line of code" - not so much.

You should throw Exceptions not knowing where or even if they will be caught. if you're using them to break out of loops and such like, then you're misusing them.

Exceptions are the coding equivalent of "I give up!" and hoping that someone else will step in to help.

If you have a language that doesn't expect to throw Exceptions, like C, then returning Results is probably OK. But .Net and Java expect you to throw Exceptions and, if you try to force a Results strategy onto them, you will lose because, eventually, they will throw an Exception by themselves, blowing your carefully constructed call and return structures apart.

Licensed under: CC-BY-SA with attribution
scroll top