Conventions for exceptions or error codes [closed]
Asked Answered
L

22

138

Yesterday I was having a heated debate with a coworker on what would be the preferred error reporting method. Mainly we were discussing the usage of exceptions or error codes for reporting errors between application layers or modules.

What rules do you use to decide if you throw exceptions or return error codes for error reporting?

Leatherjacket answered 31/10, 2008 at 12:24 Comment(0)
L
70

I normally prefer exceptions, because they have more contextual information and can convey (when properly used) the error to the programmer in a clearer fashion.

On the other hand, error codes are more lightweight than exceptions but are harder to maintain. Error checking can inadvertently be omitted. Error codes are harder to maintain because you have to keep a catalog with all error codes and then switch on the result to see what error was thrown. Error ranges can be of help here, because if the only thing we are interested in is if we are in the presence of an error or not, it is simpler to check (e.g., an HRESULT error code greater or equal to 0 is success and less than zero is failure). They can inadvertently be omitted because there is no programmatic forcing that the developer will check for error codes. On the other hand, you cannot ignore exceptions.

To summarize I prefer exceptions over error codes in almost all situations.

Leatherjacket answered 31/10, 2008 at 12:25 Comment(8)
"error codes are more lightweight than exceptions" depends on what you measure and how you measure. It's pretty easy to come up with tests that show exception-based APIs can be far faster.Forepeak
@MooingDuck, Not possible, unless those tests are unfair/biased. You are effectively claiming that there's an environment which manipulates objects and strings more efficiently than it manipulates numbers. Definitely possible, but hardly run-of-the-mill.Alfaro
@smink, Good points, but how do we address the overhead of exceptions? Error codes are not just light-weight, they are basically weightless; exceptions are not just medium-weight, they are heavy-weight objects containing stack information and misc stuff which we don't use anyway.Alfaro
@Pacerier: You're only considering tests where exceptions are thrown. You're 100% right that throwing a C++ exception is significantly slower than returning an error code. Hands down, no debate. Where we do differ, is the other 99.999% of the code. With exceptions, we don't have to check error code returns between each statement, making that code some 1-50% faster (or not, depending on your compiler). Which means the full code can faster, or slower, depending entirely on how the code is written and how often exceptions are thrown.Forepeak
@Pacerier: With this artificial test I just wrote, the exception based code is as fast as the error codes in MSVC and Clang, though not GCC: coliru.stacked-crooked.com/a/e81694e5c508945b (timings at the bottom). It appears that the GCC flags I used generated unusually slow exceptions compared to the other compilers. I'm biased, so please critique my test, and feel free to try another variant.Forepeak
@Mooing Duck, If you tested both error codes and exceptions at the same time, then you must have had exceptions enabled. If that is the case, your results would suggest that using error codes with exception handling overhead is not any slower than using only exceptions. The error code tests would need to be executed with exceptions disabled to get meaningful results.Imbalance
@MikaHaarahiltunen: Well observed, I need a new artificial test coliru.stacked-crooked.com/a/3564a61ef4c9bcfb. I found an excellent one once, but I can't recall it now. I think it was akin to naive Fibonacci.Forepeak
"They can inadvertently be omitted because there is no programmatic forcing that the developer will check for error codes": it's not true anymore, see how rust error handling works, in some languages compiler checks if you handled the errorCockahoop
R
89

In high-level stuff, exceptions; in low-level stuff, error codes.

The default behaviour of an exception is to unwind the stack and stop the program, if I'm writing a script an and I go for a key that's not in a dictionary it's probably an error, and I want the program to halt and let me know all about that.

If, however, I'm writing a piece of code which I must know the behaviour of in every possible situation, then I want error codes. Otherwise I have to know every exception that can be thrown by every line in my function to know what it will do (Read The Exception That Grounded an Airline to get an idea of how tricky this is). It's tedious and hard to write code that reacts appropriately to every situation (including the unhappy ones), but that's because writing error-free code is tedious and hard, not because you're passing error codes.

Both Raymond Chen and Joel have made some eloquent arguments against using exceptions for everything.

Rebellion answered 31/10, 2008 at 13:3 Comment(9)
I found two of those posts to be very interesting (I didn't read the "and"), but particularly with Raymond's post, it was difficult to tell how much of it was truly language-agnostic. I think you could write an equally eloquent argument for using exceptions rather than error codes.Misconception
+1 for pointing out that the context has something to do with the error handling strategy.Aulea
@Tom, Good points, but exceptions are guaranteed to be caught. How do we ensure that error codes are caught and not silently ignored due to mistakes?Alfaro
So your only argument against exceptions is that something bad can happen when you forget to catch an exception yet you don't consider the just as likely situation of forgetting to check the returned value for errors? Not to mention you get a stack trace when you forget to catch an exception while you get nothing when you forget to check an error code.Hukill
@Esailija: no, the major arguments against are that it's more hard to understand the program flow, to write exception-safe code and to recognize bad code. That said, I think the advantages are much greater.Guenzi
I don't completely agree with using error codes when you must know the behavior in every possible situation. Even with error codes it's easy to simply forget to handle a few. Java encourages to use exceptions in these situations anyways. One of the reasons why Java makes the handling of possible exceptions mandatory. I do agree with low level vs high level, but only because throwing and catching exceptions is a relative costly operation in terms of CPU cycles. On other reason to use exceptions is clean code. Your code becomes a lot cleaner when you don't mingle logic with error handling.Bowra
C++17 introduces the nodiscard attribute that will give a compiler warning if the return value of a function is not stored. Helps a little bit to catch forgotten error code checking. Example: godbolt.org/g/6i6E0BVisakhapatnam
@Esailija's comment is exactly on point here. The argument against exceptions here takes a hypothetical error-code-based API where all error codes are documented and a hypothetical programmer who reads the documentation, correctly identifies all error cases that are logically possible in her application, and writes code to handle each of them, then compares that scenario to a hypothetical exception-based API and programmer where for some reason one of those steps goes wrong... even though it's equally easy (arguably easier) to get all those steps right in an exception-based API.Phyte
Working link (as of this writing of course) for Raymond Chen's article: devblogs.microsoft.com/oldnewthing/20050114-00/?p=36693 (title of the post is "Cleaner, more elegant, and harder to recognize"Haaf
L
70

I normally prefer exceptions, because they have more contextual information and can convey (when properly used) the error to the programmer in a clearer fashion.

On the other hand, error codes are more lightweight than exceptions but are harder to maintain. Error checking can inadvertently be omitted. Error codes are harder to maintain because you have to keep a catalog with all error codes and then switch on the result to see what error was thrown. Error ranges can be of help here, because if the only thing we are interested in is if we are in the presence of an error or not, it is simpler to check (e.g., an HRESULT error code greater or equal to 0 is success and less than zero is failure). They can inadvertently be omitted because there is no programmatic forcing that the developer will check for error codes. On the other hand, you cannot ignore exceptions.

To summarize I prefer exceptions over error codes in almost all situations.

Leatherjacket answered 31/10, 2008 at 12:25 Comment(8)
"error codes are more lightweight than exceptions" depends on what you measure and how you measure. It's pretty easy to come up with tests that show exception-based APIs can be far faster.Forepeak
@MooingDuck, Not possible, unless those tests are unfair/biased. You are effectively claiming that there's an environment which manipulates objects and strings more efficiently than it manipulates numbers. Definitely possible, but hardly run-of-the-mill.Alfaro
@smink, Good points, but how do we address the overhead of exceptions? Error codes are not just light-weight, they are basically weightless; exceptions are not just medium-weight, they are heavy-weight objects containing stack information and misc stuff which we don't use anyway.Alfaro
@Pacerier: You're only considering tests where exceptions are thrown. You're 100% right that throwing a C++ exception is significantly slower than returning an error code. Hands down, no debate. Where we do differ, is the other 99.999% of the code. With exceptions, we don't have to check error code returns between each statement, making that code some 1-50% faster (or not, depending on your compiler). Which means the full code can faster, or slower, depending entirely on how the code is written and how often exceptions are thrown.Forepeak
@Pacerier: With this artificial test I just wrote, the exception based code is as fast as the error codes in MSVC and Clang, though not GCC: coliru.stacked-crooked.com/a/e81694e5c508945b (timings at the bottom). It appears that the GCC flags I used generated unusually slow exceptions compared to the other compilers. I'm biased, so please critique my test, and feel free to try another variant.Forepeak
@Mooing Duck, If you tested both error codes and exceptions at the same time, then you must have had exceptions enabled. If that is the case, your results would suggest that using error codes with exception handling overhead is not any slower than using only exceptions. The error code tests would need to be executed with exceptions disabled to get meaningful results.Imbalance
@MikaHaarahiltunen: Well observed, I need a new artificial test coliru.stacked-crooked.com/a/3564a61ef4c9bcfb. I found an excellent one once, but I can't recall it now. I think it was akin to naive Fibonacci.Forepeak
"They can inadvertently be omitted because there is no programmatic forcing that the developer will check for error codes": it's not true anymore, see how rust error handling works, in some languages compiler checks if you handled the errorCockahoop
S
24

I prefer exceptions because

  • they interupt the flow of logic
  • they benefit from class hierarchy which gives more features/functionality
  • when used properly can represent a wide range of errors (e.g. an InvalidMethodCallException is also a LogicException, as both occur when there's a bug in your code that should be detectable before runtime), and
  • they can be used to enhance the error (i.e. a FileReadException class definition can then contain code to check whether the file exists, or is locked, etc)
Soto answered 31/10, 2008 at 12:30 Comment(4)
Your fourth point is not a fair one: An error status when converted into an object, can also contain code to check whether the file exists, or is locked, etc. It's simply a variation of https://mcmap.net/q/168285/-whats-the-right-approach-to-return-error-codes-in-cAlfaro
"they interupt the flow of logic". Exceptions have more or less the effects of gotoVittoria
@peterchaula There are no evil features, just feature abuse.Paltry
@Alfaro but it is actually. Exceptions allow you to bubble up the exception all the way where it is actually relevant, and still preserve information from the stack that is long gone by the time user is checking the error code. If I'm using curl to make a http request, how will I know what and where and why and how failed in SSL? I am not even the one using SSL library, let alone knowledgeable enough in its context to guess what went wrong from an arbitrary integer with no clue where it even originated in.Andro
R
24

Error codes can be ignored (and often are!) by the callers of your functions. Exceptions at least force them to deal with the error in some way. Even if their version of dealing with it is to have an empty catch handler (sigh).

Reform answered 31/10, 2008 at 12:39 Comment(1)
That's true for some languages and false for others. For example, Java forces you to either catch Exceptions or throw them, but there are other languages like JavaScript or Python where exception handling is not enforced by the language.Devinna
L
18

You should use both. The thing is to decide when to use each one.

There are a few scenarios where exceptions are the obvious choice:

  1. In some situations you can't do anything with the error code, and you just need to handle it in an upper level in the call stack, usually just log the error, display something to the user or close the program. In these cases, error codes would require you to bubble up the error codes manually level by level which is obviously much easier to do with exceptions. The point is that this is for unexpected and unhandleable situations.

  2. Yet about situation 1 (where something unexpected and unhandleable happens you just wan't to log it), exceptions can be helpful because you might add contextual information. For example if I get a SqlException in my lower-level data helpers, I will want to catch that error in the low-level (where I know the SQL command that caused the error) so I can capture that information and rethrow with additional information. Please note the magic word here: rethrow, and not swallow. The first rule of exception handling: do not swallow exceptions. Also, note that my inner catch doesn't need to log anything because the outer catch will have the whole stack trace and may log it.

  3. In some situations you have a sequence of commands, and if any of them fail you should cleanup/dispose resources(*), whether or not this is an unrecoverable situation (which should be thrown) or a recoverable situation (in which case you can handle locally or in the caller code but you don't need exceptions). Obviously it's much easier to put all those commands in a single try, instead of testing error codes after each method, and cleanup/dispose in the finally block. Please note that if you want the error to bubble up (which is probably what you want), you don't even need to catch it - you just use the finally for cleanup/dispose - you should only use catch/retrow if you want to add contextual information (see bullet 2).

    One example would be a sequence of SQL statements inside a transaction block. Again, this also a "unhandleable" situation, even if you decide to catch it early (treat it locally instead of bubbling up to the top) it's still a fatal situation from where the best outcome is to abort everything or at least abort a large part of the process.
    (*) This is like the on error goto that we used in old Visual Basic

  4. In constructors you can only throw exceptions.

Having said that, in all other situations where you're returning some information on which the caller CAN/SHOULD take some action, using return codes is probably a better alternative. This includes all expected "errors", because probably they should be handled by the immediate caller, and will hardly need to be bubbled up too many levels up in the stack.

Of course it's always possible to treat expected errors as exceptions, and catch then immediately one level above, and it's also possible to encompass every line of code in a try catch and take actions for each possible error. IMO, this is bad design, not only because it's much more verbose, but specially because the possible exceptions that might be thrown are not obvious without reading the source code - and exceptions could be thrown from any deep method, creating invisible gotos. They break code structure by creating multiple invisible exit points that make code hard to read and inspect. In other words, you should never use exceptions as flow-control, because that would be hard for others to understand and maintain. It can get even difficult to understand all possible code flows for testing.
Again: for correct cleanup/dispose you can use try-finally without catching anything.

The most popular criticism about return codes is that "someone could ignore the error codes, but in the same sense someone can also swallow exceptions. Bad exception handling is easy in both methods. But writing good error-code-based program is still much easier than writing an exception-based program. And if one by any reason decides to ignore all errors (the old on error resume next), you can easily do that with return codes and you can't do that without a lot of try-catchs boilerplate.

The second most popular criticism about return codes is that "it's difficult to bubble up" - but that's because people don't understand that exceptions are for non-recoverable situations, while error-codes are not.

Deciding between exceptions and error codes is a gray area. It's even possible that you need to get an error code from some reusable business method, and then you decide to wrap that into an exception (possibly adding information) and let it bubble up. But it's a design mistake to assume that ALL errors should be thrown as exceptions.

To sum it up:

  • I like to use exceptions when I have an unexpected situation, in which there's not much to do, and usually we want to abort a large block of code or even the whole operation or program. This is like the old "on error goto".

  • I like to use return codes when I have expected situations in which the caller code can/should take some action. This includes most business methods, APIs, validations, and so on.

This difference between exceptions and error codes is one of the design principles of the GO language, which uses "panic" for fatal unexpected situations, while regular expected situations are returned as errors.

Yet about GO, it also allows multiple return values , which is something that helps a lot on using return codes, since you can simultaneously return an error and something else. On C#/Java we can achieve that with out parameters, Tuples, or (my favorite) Generics, which combined with enums can provide clear error codes to the caller:

public MethodResult<CreateOrderResultCodeEnum, Order> CreateOrder(CreateOrderOptions options)
{
    ....
    return MethodResult<CreateOrderResultCodeEnum>.CreateError(CreateOrderResultCodeEnum.NO_DELIVERY_AVAILABLE, "There is no delivery service in your area");

    ...
    return MethodResult<CreateOrderResultCodeEnum>.CreateSuccess(CreateOrderResultCodeEnum.SUCCESS, order);
}

var result = CreateOrder(options);
if (result.ResultCode == CreateOrderResultCodeEnum.OUT_OF_STOCK)
    // do something
else if (result.ResultCode == CreateOrderResultCodeEnum.SUCCESS)
    order = result.Entity; // etc...

If I add a new possible return in my method, I can even check all callers if they are covering that new value in a switch statement for example. You really can't do that with exceptions. When you use return codes, you'll usually know in advance all possible errors, and test for them. With exceptions you usually don't know what might happen. Wrapping enums inside exceptions (instead of Generics) is an alternative (as long as it's clear the type of exceptions that each method will throw), but IMO it's still bad design.

EDIT 2020-10-11:

Since C# 7.0 (March 2017) instead of Generics I prefer to use the new Tuples syntax which allows multiple return values (so we can use GO-like syntax where methods return a result OR an error).


public enum CreateUserResultCodeEnum
{
    [Description("Username not available")]
    NOT_AVAILABLE,
}

public (User user, CreateUserResultCodeEnum? error) CreateUser(string userName)
    // (try to create user, check if not available...)
    if (notAvailable)
        return (null, CreateUserResultCodeEnum.NOT_AVAILABLE);
    return (user, null);
}

// How to call and deconstruct tuple:
(var user, var error) = CreateUser("john.doe");
if (user != null) ...
if (error == CreateUserResultCodeEnum.NOT_AVAILABLE) ...

// Or returning a single object (named tuple):
var result = CreateUser("john.doe");
if (result.user != null) ...
if (result.error == CreateUserResultCodeEnum.NOT_AVAILABLE) ...

EDIT 2021-01-09:

A few days ago I wrote this blog post about how we can (in some cases!) use multiple returns instead of exceptions (like golang convention explained above, not supposed to replace all your exceptions but supposed to give you arsenal to decide between when to use exceptions and when to use return codes). By the end of the post I'm mixing two models - basically I'm using the ValueTuple syntax (which is very concise and elegant) but yet using Generics as the underlying structure. Basically I use implicit conversion operator and type deconstructors to convert back and forth between ValueTuple and CommandResult<TEntity, TError>.

Lowrey answered 19/10, 2017 at 6:28 Comment(0)
K
17

Exceptions over error codes, no doubt about it. You get much of the same benefits from exceptions as you do with error codes, but also much more, without the shortcomings of error codes. The only knock on exceptions is that it is slightly more overhead; but in this day and age, that overhead should be considered negligible for nearly all applications.

Here are some articles discussing, comparing and contrasting the two techniques:

There are some good links in those that can give you further reading.

Krafftebing answered 31/10, 2008 at 12:48 Comment(0)
J
17

I would never mix the two models...it's too hard to convert from one to the other as you move from one part of the stack which is using error codes, to a higher piece that is using exceptions.

Exceptions are for "anything that stops or inhibits the method or subroutine from doing what you asked it to do" ... NOT to pass messages back about irregularities or unusual circumstances, or the state of the system, etc. Use return values or ref (or out) parameters for that.

Exceptions allow methods to be written (and utilized) with semantics that are dependent on the method's function, i.e. a method that returns an Employee object or List of Employees can be typed to do just that, and you can utilize it by calling.

Employee EmpOfMonth = GetEmployeeOfTheMonth();

With error codes, all methods return an error code, so, for those that need to return something else to be used by the calling code, you have to pass a reference variable to be populated with that data, and test the return value for the error code, and handle it, on every function or method call.

Employee EmpOfMonth; 
if (getEmployeeOfTheMonth(ref EmpOfMonth) == ERROR)
    // code to Handle the error here

If you code so that each method does one and only one simple thing, then you should throw an exception whenever the method cannot accomplish the method's desired objective. Exceptions are much richer and easier to use in this way than error codes. Your code is much cleaner - The standard flow of the "normal" code path can be devoted strictly to the case where the method IS able to accomplish what you wanted it to do... And then the code to clean up, or handle the "exceptional" circumstances when something bad happens that prevents the method from completing successfully can be siloed away from the normal code. Additionally, if you can't handle the exception where it occurred, and must pass it up the stack to a UI, (or worse, across the wire from a mid-tier component to a UI), then with the exception model, you don't need to code every intervening method in your stack to recognize and pass the exception up the stack... The exception model does this for you automagically.... With error codes, this piece of the puzzle can get onerous very rapidly.

Janaye answered 31/10, 2008 at 16:16 Comment(1)
Great answer! Out of the box solution just in time!Chromolithograph
G
12

In the past I joined the errorcode camp (did too much C programming). But now I have seen the light.

Yes exceptions are a bit of a burden on the system. But they simplify the code, reducing the number of errors (and WTF's).

So use exception but use them wise. And they will be your friend.

As a side note. I have learned to document which exception can be thrown by which method. Unfortunately this is not required by most languages. But it increases the chance of handling the right exceptions at the right level.

Godiva answered 31/10, 2008 at 12:30 Comment(1)
yap C does leave a few habits in us all ;)Leatherjacket
G
12

There may be a few situations where using exceptions in a clean, clear, correct way is cumbersome, but the vast majority of the time exceptions are the obvious choice. The biggest benefit exception handling has over error codes is that it changes the flow of execution, which is important for two reasons.

When an exception occurs, the application is no longer following it's 'normal' execution path. The first reason why this is so important is that unless the author of the code goes well and truly out of their way to be bad, the program will halt and not continue doing unpredictable things. If an error code doesn't get checked and appropriate actions aren't taken in response to a bad error code, the program will keep on doing what it's doing and who knows what the result of that action will be. There are lots of situations where having the program do 'whatever' could wind up being very expensive. Consider a program that retrieves performance information for various financial instruments a company sells, and delivers that information to brokers/wholesalers. If something goes wrong and the program keeps going, it could ship erroneous performance data to the brokers and wholesalers. I don't know about anybody else, but I don't want to be the one sitting in a VPs office explaining why my code caused the company to get 7-figures worth of regulatory fines. Delivering an error message to customers is generally preferable to delivering wrong data that could look to be 'real', and the latter situation is much easier to run into with a much less aggressive approach like error codes.

The second reason why I like exceptions and their breaking of the normal execution is that it makes it much, much easier to keep the 'normal things are happening' logic separate from the 'something went wrong logic'. To me, this:

try {
    // Normal things are happening logic
catch (// A problem) {
    // Something went wrong logic
}

...is preferable to this:

// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}

There are other little things about exceptions that are nice, as well. Having a bunch of conditional logic to keep track of whether any of the methods being called in a function had an error code returned, and return that error code higher up is a lot of boiler plate. In fact, it's a lot of boiler plate that can go wrong. I have a lot more faith in the exception system of most languages than I do a rats nest of if-else-if-else statements that 'Fresh-out-of-college' Fred wrote, and I have a lot better things to do with my time than code reviewing said rat's nest.

Gettogether answered 21/2, 2015 at 17:17 Comment(0)
I
5

My approach is that we can use both, i.e. Exceptions and Errors codes at the same time.

I'm used to define several types of Exceptions (ex: DataValidationException or ProcessInterruptExcepion) and inside each exception define a more detailed description of each problem.

A Simple Example in Java:

public class DataValidationException extends Exception {


    private DataValidation error;

    /**
     * 
     */
    DataValidationException(DataValidation dataValidation) {
        super();
        this.error = dataValidation;
    }


}

enum DataValidation{

    TOO_SMALL(1,"The input is too small"),

    TOO_LARGE(2,"The input is too large");


    private DataValidation(int code, String input) {
        this.input = input;
        this.code = code;
    }

    private String input;

    private int code;

}

In this way i use Exceptions to define category errors, and error codes to define more detailed info about the problem.

Iconic answered 31/10, 2008 at 13:50 Comment(1)
erm... throw new DataValidationException("The input is too small")? One of the advantages of exceptions is allowing for detailed information.Monogamous
C
4

My reasoning would be if you are writing a low-level driver that really needs performance, then use error codes. But if you're using that code in a higher-level application and it can handle a bit of overhead, then wrap that code with an interface which checks those error codes and raises exceptions.

In all other cases, exceptions are probably the way to go.

Converge answered 31/10, 2008 at 13:3 Comment(0)
R
4

I may be sitting on the fence here, but...

  1. It depends on the language.
  2. Whichever model you choose, be consistent about how you use it.

In Python, use of exceptions is standard practice, and I'm quite happy to define my own exceptions. In C you don't have exceptions at all.

In C++ (in the STL at least), exceptions are typically only thrown for truly exceptional errors (I virtually never see them myself). I see no reason to do anything different in my own code. Yes it's easy to ignore return values, but C++ doesn't force you to catch exceptions either. I think you just have to get into the habit of doing it.

The code base I work on is mostly C++ and we use error codes almost everywhere, but there's one module that raises exceptions for any error, including very unexceptional ones, and all the code that uses that module is pretty horrible. But that might just be because we've mixed exceptions and error codes. The code that consistently uses error codes is much easier to work with. If our code consistently used exceptions, maybe it wouldn't be as bad. Mixing the two doesn't seem to work so well.

Richmound answered 31/10, 2008 at 14:38 Comment(0)
A
4

Since I work with C++, and have RAII to make them safe to use, I use exceptions almost exclusively. It pulls error handling out of the normal program flow and makes the intent more clear.

I do leave exceptions for exceptional circumstances though. If I'm expecting that a certain error is going to happen a lot I'll check that the operation will succeed before performing it, or call a version of the function that uses error codes instead (Like TryParse())

Alban answered 31/10, 2008 at 19:42 Comment(0)
J
3

Method signatures should communicate to you what the method does. Something like long errorCode = getErrorCode(); might be fine, but long errorCode = fetchRecord(); is confusing.

Joist answered 31/10, 2008 at 12:37 Comment(0)
C
2

Exceptions are for exceptional circumstances - ie, when they are not part of the normal flow of the code.

It's quite legitimate to mix Exceptions and error codes, where error codes represent the status of something, rather than an error in the running of the code per se (e.g. checking the return code from a child process).

But when an exceptional circumstance occurs I believe Exceptions are the most expressive model.

There are cases where you might prefer, or have, to use error codes in place of Exceptions, and these have been adequately covered already (other than other obvious constrains such as compiler support).

But going in the other direction, using Exceptions allows you to build even higher level abstractions to your error handling, that can make your code even more expressive and natural. I would highly recommend reading this excellent, yet underrated, article by C++ expert Andrei Alexandrescu on the subject of what he calls, "Enforcements": http://www.ddj.com/cpp/184403864. Although it's a C++ article the principles are generally applicable, and I have translated the enforcements concept to C# quite successfully.

Cherri answered 31/10, 2008 at 14:47 Comment(0)
S
2

First, I agree with Tom's answer that for high-level stuff use exceptions, and for low-level stuff use error codes, as long as it is not Service Oriented Architecture (SOA).

In SOA, where methods may be called across different machines, exceptions may not be passed over the wire, instead, we use success/failure responses with a structure like below (C#):

public class ServiceResponse
{
    public bool IsSuccess => string.IsNullOrEmpty(this.ErrorMessage);

    public string ErrorMessage { get; set; }
}

public class ServiceResponse<TResult> : ServiceResponse
{
    public TResult Result { get; set; }
}

And use like this:

public async Task<ServiceResponse<string>> GetUserName(Guid userId)
{
    var response = await this.GetUser(userId);
    if (!response.IsSuccess) return new ServiceResponse<string>
    {
        ErrorMessage = $"Failed to get user."
    };
    return new ServiceResponse<string>
    {
        Result = user.Name
    };
}

When these are used consistently in your service responses it creates a very nice pattern of handling success/failures in the application. This allows easier error handling in async calls within services as well as across services.

Scrupulous answered 27/3, 2017 at 19:25 Comment(0)
O
1

For most applications, exceptions are better. The exception is when the software has to communicate with other devices. The domain I work in is industrial controls. Here errors codes are preferred and expected. So my answer is that it does depend on the situation.

Offbeat answered 31/10, 2008 at 12:46 Comment(0)
A
1

I would prefer Exceptions for all error cases, except when a failure is an expectable bug-free result of a function that returns a primitive datatype. E.g. finding the index of a substring within a larger string would usually return -1 if not found, instead of raising a NotFoundException.

Returning invalid pointers that might be dereferenced (e.g. causing NullPointerException in Java) is not acceptable.

Using multiple different numerical error codes (-1, -2) as return values for the same function is usually bad style, as clients might do a "== -1" check instead of "< 0".

One thing to keep in mind here is the evolution of APIs over time. A good API allows to change and extend failure behavior in several ways without breaking clients. E.g. if a client error handle checked for 4 error cases, and you add a fifth error value to your function, the client handler may not test this and break. If you raise Exceptions, this will usually make it easier for clients to migrate to a newer version of a library.

Another thing to consider is when working in a team, where to draw a clear line for alldevelopers to make such a decision. E.g. "Exceptions for high-level stuff, error codes for low-level stuff" is very subjective.

In any case, where more than one trivial type of error is possible, the source code should never use the numeric literal to return an error code or to handle it (return -7, if x == -7 ...), but always a named constant (return NO_SUCH_FOO, if x == NO_SUCH_FOO) .

Ariel answered 14/10, 2013 at 15:11 Comment(0)
K
1

If you work under big project, you can't use only exceptions or only error codes. In different cases you should use different approaches.

For example, you decide to use exceptions only. But once you decide to use async event processing. It is bad idea to use exceptions for error handling in this situations. But use error codes everywhere in application is tedious.

So my opinion that it is normal to use both exceptions and error codes simultaneous.

Koala answered 22/3, 2014 at 12:31 Comment(0)
P
0

I think it also depends on whether you really need information like stack trace from the result. If yes, you definitely go for Exception which provide object full with lots of information about problem. However, if you are just interested in result and don't care why that result then go for error code.

e.g. When you are processing file and face IOException, client might interested in knowing from where this was triggered, in opening file or parsing file etc. So better you return IOException or its specific subclass. However, scenario like you have login method and you want to know it was successful or not, there either you just return boolean or to show correct message, return error code. Here Client is not interested in knowing which part of logic caused that error code. He just know if its Credential invalid or account lock etc.

Another usecase I can think of is when data travels on network. Your remote method can return just error code instead of Exception to minimize data transfer.

Precipitancy answered 3/3, 2016 at 9:3 Comment(0)
O
0

My general rule is:

  • Only one error could appear in a function: use error code (as parameter of the function)
  • More than one specific error could appear: throw exception
Olecranon answered 15/5, 2017 at 13:46 Comment(0)
S
-1

Error codes also don't work when your method returns anything other than a numeric value...

Superpower answered 31/10, 2008 at 12:40 Comment(2)
Um, no. See win32 GetLastError() paradigm. I am not defending it, just poiting out that you are incorrect.Sherl
In fact there are many other ways to do it. Another way is to return an object that contains the error code and the real return value. Yet another way is to do reference passing.Alfaro

© 2022 - 2024 — McMap. All rights reserved.