Exceptions or Error Codes

Yesterday I had a fierce debate with a colleague about what the preferred method of reporting errors would be. We mainly discussed the use of exceptions or error codes to report errors between application tiers or modules.

What rules do you use to decide whether you throw exceptions or return error codes for error messages?

+83
language-agnostic design
Oct 31 '08 at 12:24
source share
22 answers

I usually prefer exceptions because they have more contextual information and can convey (if used correctly) the error for the programmer in a more clear way.

Error codes, on the other hand, are lighter than exceptions, but are more difficult to maintain. Error checking may be inadvertently omitted. Error codes are more difficult to maintain, because you need to save a directory with all error codes, and then include the result to find out which error was selected. Error ranges can be useful here, because if the only thing that interests us is if we are in the presence of an error or not, it is easier to check (for example, an HRESULT error code greater than or equal to 0 is successful and less than zero is a failure) . They may be inadvertently omitted because there is no software impact that the developer will check for error codes. On the other hand, you cannot ignore exceptions.

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

+43
Oct 31 '08 at 12:25
source share

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

The default behavior for the exception is to unwind the stack and stop the program if I write script an and I go for a key that is not in the dictionary, this is probably a mistake and I want the program to stop and let me know all about it .

If, however, I am writing a piece of code that should know the behavior in any possible situation, then I need error codes. Otherwise, I must know every exception that can be thrown by each line in my function in order to know what it will do (read the Exception that the airline founded to understand how difficult it is). It is tiring and difficult to write code that responds appropriately to every situation (including unfortunate ones), but this is because writing free code is tedious and difficult, and not because you pass error codes.

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

+63
Oct 31 '08 at 13:03
source share

I prefer exceptions because

  • they intercept the flow of logic
  • they benefit from a class hierarchy that provides more features / functionality
  • if used correctly, it can be a wide range of errors (for example, InvalidMethodCallException is also a LogicException, since both occur when there is an error in your code that must be detected before execution), and
  • they can be used to improve the error (that is, the definition of the FileReadException class may contain code to check for a file or lock, etc.)
+22
Oct 31 '08 at 12:30
source share

Error codes can be ignored (and often are!) Calling your functions. Exceptions, at least, force them to somehow cope with the error. Even if their version deals with it, you need to have an empty catch handler (sigh).

+15
Oct 31 '08 at 12:39
source share

Exceptions to error codes, no doubt. You get almost the same benefits from exceptions as error codes, but also much more, without the disadvantages of error codes. The only thing to do with exceptions is that it is slightly more than overhead; but on this day and age, this overhead should be considered negligible for almost all applications.

Here are a few articles discussing, comparing and contrasting the two methods:

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

+14
Oct 31 '08 at 12:48
source share

I would never mix the two models ... it's too hard to convert from one to the other when you go from one part of the stack that uses error codes to the higher part that uses exceptions.

Exceptions for “anything that stops or prevents a method or subroutine from doing what you asked to do” ... DO NOT send messages about violations or unusual circumstances, system status, etc. Use return values ​​or ref (or out) for this.

Exceptions allow you to write (and use) methods with semantics that depend on the function of the method, that is, a method that returns an Employee or List of Employees object can be introduced to do this, and you can use it by calling,

Employee EmpOfMonth = GetEmployeeOfTheMonth(); 

With error codes, all methods return an error code, so for those who need to return something else that will be used by the calling code, you need to pass a reference variable that will be filled with this data, and check the return value for the error code and process it when 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 execute the desired method. Exceptions are much richer and easier to use than error codes. Your code is much cleaner. The standard stream of the “normal” code path can be strictly devoted to the case when the method is capable of doing what you wanted it to do ... And then the code for cleaning or processing the “exceptional” circumstances when something bad happens that interferes successfully complete the method, you can drop it from regular code. In addition, if you cannot handle the exception where it occurred and pass its stack to the user interface (or, even worse, through the conductor from the middle-level component to the user interface), then with the exception model you do not need to code each intermediate a method on your stack to recognize and pass an exception from the stack ... The exception model does this automatically for you .... With error codes, this puzzle piece can be very burdensome.

+12
Oct 31 '08 at 16:16
source share

In the past, I joined the error camp (too much C programmed). But now I saw the light.

Exceptions are a bit of a load on the system. But they simplify the code by reducing errors (and WTF).

So use an exception, but use them wisely. And they will be your friend.

As a note. I learned to document which exception can be selected by which method. Unfortunately, this is not required by most languages. But this increases the likelihood of handling the right exceptions at the right level.

+9
Oct. 31 '08 at 12:30
source share

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

When an exception occurs, the application ceases to follow the "normal" execution path. The first reason this is so important is that if the author of the code is not good and really out of his way to be bad, the program will stop and not continue to do unpredictable things. If the error code is not checked and the corresponding actions are not taken in response to a bad error code, the program will continue to do what it does, and who knows what the result of this action will be. There are many situations where the “everything” program can be very expensive. Consider a program that extracts performance information for various financial instruments that a company sells and delivers this information to brokers / wholesalers. If something goes wrong and the program continues, it may send erroneous performance data to brokers and wholesalers. I don’t know about anyone else, but I don’t want to be the one sitting in the VPs office explaining why my code forced the company to receive 7-digit amounts of regulatory fines. Providing clients with error messages is usually preferable to delivering incorrect data that may look "real", and in the latter situation it is much easier to work with a much less aggressive approach, for example, error codes.

The second reason I like exceptions and their violation of normal execution is that they greatly facilitate the logic of “normal things”, other than “something went wrong.” For me it:

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

... preferred:

 // 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 good too. Having a bunch of conditional logic to track whether any of the methods called in a function returned an error code and return the error code above is a lot of boiler plates. In fact, this is a lot of boiler stoves that can go wrong. I have much more faith in the exception system of most languages ​​than rat nests in if-else-if-else expressions written by Fred-out-of-College by Fred, and I have much better things to do with my time than code considering the indicated rat nest.

+9
Feb 21 '15 at 17:17
source share

Method signatures should tell you what this method does. Something like long errorCode = getErrorCode (); may be fine, but long errorCode = fetchRecord (); confusing.

+3
Oct 31 '08 at 12:37
source share

My reasoning would be if you are writing a low-level driver that really needs performance, then use error codes. But if you use this code in a higher-level application and can handle a bit of overhead, then wrap this code with an interface that checks for these error codes and throws exceptions.

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

+3
Oct. 31 '08 at 13:03
source share

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

I use to define several types of exceptions (for example: DataValidationException or ProcessInterruptExcepion) and within each exception defines 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; } 

Thus, I use Exceptions to identify category errors and error codes to determine more detailed information about the problem.

+3
Oct 31 '08 at 13:50
source share

I can sit on the fence here, but ...

  • It depends on the language.
  • Whichever model you choose, be consistent in how you use it.

In Python, using exceptions is standard practice, and I'm quite happy to define my own exceptions. In C, you have no exceptions at all.

In C ++ (at least in STL) exceptions are usually generated only for truly exceptional errors (I almost never see them). I see no reason to do something else in my own code. Yes, it is easy to ignore returned values, but C ++ does not force you to catch exceptions. I think you just need to get used to it.

The code base I'm working on is mostly C ++, and we use error codes almost everywhere, but there is one module that throws exceptions for any error, including very unusual ones, and all the code that uses this module is pretty terrible . But this can only be because we mixed exceptions and error codes. Code that consistently uses error codes is much easier to work with. If our code consistently used exceptions, perhaps that would not be so bad. Mixing the two doesn't work so well.

+3
Oct 31 '08 at 14:38
source share

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

However, I leave exceptions for exceptional circumstances. If I expect some error to occur, I will check if the operation will succeed before it is executed or will call a version of the function that uses error codes instead (e.g. TryParse() )
+3
Oct 31 '08 at 19:42
source share

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

It is perfectly acceptable to mix exceptions and error codes, where error codes represent the status of something, and not an error when starting the code as such (for example, checking the return code from a child process).

But when an exceptional circumstance arises, I believe that Exceptions are the most expressive model.

There are times when you may prefer or use error codes instead of Exceptions, and they have already been adequately covered (apart from other obvious limitations, such as compiler support).

But in another direction, using Exceptions allows you to create higher-level abstractions to handle errors that can make your code even more expressive and natural. I would highly recommend reading this excellent but underestimated article by a C ++ expert Andrei Alexandrescu about what he calls "Forced Actions": http://www.ddj.com/cpp/184403864 . Although this article is in C ++, the principles are usually applicable, and I have successfully translated the concept of enforcement into C #.

+2
Oct 31 '08 at 14:47
source share

Firstly, I agree with Tom answer that for exceptions to use at a high level, and for low-level files, error codes are used if it is not a Service Oriented Architecture (SOA).

In SOA, where methods can be called on different machines, exceptions may not be passed by wire, instead we use success / failure answers with a structure similar to the one 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 they are used sequentially in service responses, it creates a very good template for handling success / failure in an application. This simplifies error handling in asynchronous calls within services, as well as through services.

+2
Mar 27 '17 at 19:25
source share

You must use both. The point is to decide when to use each .

There are several scenarios where exceptions are an obvious choice :

  • In some situations, you cannot do anything with the error code , and you just need to process it at the top level in the call stack , as a rule, register an error, display something to the user or close the program. In these cases, error codes will require that you reset the error codes manually to a level that is obviously much easier to do with exceptions. The fact is that this is for unforeseen and unreliable situations.

  • However, about situation 1 (where something unexpected and inexplicable happens, you just don’t want to register it), exceptions can be useful because you can add contextual information . For example, if I get a SqlException in my junior data helpers, I want to catch this error at a low level (where I know the SQL command that caused the error) so that I can capture this information and re-challenge with additional information. Pay attention to the magic word here: rethrow, not swallow . The first rule of exception handling: does not swallow exceptions . Also, please note that my internal catch does not need to be registered, because the external catch will have all the stack trace and can register it.

  • In some situations, you have a sequence of commands, and if any of them fails , you must clear / delete resources (*), regardless of whether it is an unrecoverable situation (which should be selected) or a restored situation (in this case you can handle locally or in caller code, but you don't need exceptions). Obviously, it’s much easier to put all these commands in one attempt, instead of checking the error codes after each method and cleaning / deleting in the finally block. Note that if you want the error to bubble (which you probably want), you don’t even need to catch it - you just use it permanently to clean / delete - you must use catch / retrow if you want to add contextual information (see bullet 2).

    One example is the sequence of SQL statements inside a transaction block. Again, this is also an “uncontrollable” situation, even if you decide to catch it at an early stage (treat it locally and not bubble up), it is still a “fatal” situation, of which the best result is to interrupt everything or, at least interrupt most of the process.
    (*) This is similar to the on error goto we used in the old Visual Basic

  • In constructors, you can only create exceptions.

Having said that in all other situations where you are returning some information on which the caller MAY / SHOULD take some action , using return codes is probably the best alternative. This includes all the expected "errors" because they probably should be handled by the immediate caller and it is unlikely that too many levels will bubble up on the stack.

Of course, you can always consider expected errors as exceptions, and then immediately go one level higher, and you can also cover every line of code in an attempt to catch and take action for every possible error. IMO, this is a poor design, not only because it is much more detailed, but specifically because the possible exceptions that can be selected are not obvious without reading the source code - and exceptions can be selected from any deep method, creating invisible gotos . They break the structure of the code by creating several invisible exit points that make the code difficult to read and verify. In other words, you should never use exceptions as flow control , because it will be difficult for others to understand and maintain. It may even be difficult to understand all the possible streams of code for testing.
Again: for proper cleaning / deletion, you can use try-finally without catching anything .

The most popular criticism regarding return codes is that "someone can ignore error codes, but in the same way, someone can also swallow exceptions. Easy exception handling is easy in both methods. But writing a good code-based program errors are still much easier than writing an exception-based program, and if for some reason you decide to ignore all errors (the old on error resume next ), you can easily do this with return codes, and you cannot do it without much number of try- templates catch

The second most popular criticism about return codes is that “it's hard to bubble” - but this is because people don’t understand that exceptions are for unreacted situations, and error codes aren’t.

The solution between exceptions and error codes is the gray area. It’s even possible that you need to get the error code from some reuse of the business method, and then you decide to wrap this in an exception (perhaps by adding information) and let it bubble. But this is a design error, suggesting that ALL errors should be selected as exceptions.

Summarizing:

  • I like to use exceptions when I have an unforeseen situation in which there are not many, and usually we want to interrupt a large block of code or even the entire operation or program. This is similar to the old "on error goto".

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

This distinction between exceptions and error codes is one of the principles of designing the GO language, which uses "panic" for unpredictable unforeseen situations, while regular expected situations are returned as errors.

However, on GO, it also allows multiple return values , which helps a lot in using return codes, since you can return an error and something else at the same time. In C # / Java, we can achieve this with parameters, Tuples or (my favorite) Generics, which, combined with enumerations, can provide explicit 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 to my method, I can even check all the callers if they cover this new value in the switch statement, for example. You really cannot do this with exceptions. When you use return codes, you usually know all the possible errors in advance and check 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 clears the type of exceptions that each method will throw), but IMO is still a bad design.

+2
Oct 19 '17 at 6:28
source share

, , , . For example. -1, , , NotFoundException.

, (, NullPointerException Java), .

(-1, -2) , "== -1" "< 0".

, , API . API , . For example. , , . , , , .

, , - , alldevelopers . For example. " , " .

, , , (return -7, x == -7...), ( NO_SUCH_FOO, x == NO_SUCH_FOO).

+1
14 . '13 15:11
source share

, . .

, . . . .

, , .

+1
22 . '14 12:31
source share

. , . , , . . , .

0
31 . '08 12:46
source share

, , , . , Exception, , . , , , .

eg. IOException, , , .. IOException . , , , , , , . , , . , ..

, , - . Exception, .

0
03 . '16 9:03
source share

My general rule:

  • : ( )
  • : throw exception
0
15 '17 13:46
source share

, , ...

-one
31 . '08 12:40
source share



All Articles