Why are error return codes evil? – level 100
Miller ran across some code today that used error codes for return values. We exchanged some bad examples of misusing error codes (or just using them, period).
In languages without structured exception handling, we often used some sort of indicator to communicate when an error happened. For instance, a DOS program (or batch file) has a return code retrievable using the ERRORLEVEL global variable.
With my platform of choice (.NET) supporting structured exception handling, using return codes for error reporting is quite far on the evil spectrum. Consider this code:
1: public int DoSomething(){
2: //omitted implementation
3: return -1;
4: }
This method should be void. It is using an integer to tell whether an error occurred or not. If something went wrong, throw an exception:
1: public void DoSomething(){
2: throw new SomethingException();
3: }Then there is no chance of the program continuing while being broken under the covers. Some folks call this method “failing fast”. If the program encounters a serios error that it can’t handle, fail fast because a program not operating according to plan can only cause harm. That’s why so many people hate “On Error Resume Next” in VB. Errors are simply ignored. Some folks rigged up error handling in VB by using:
1: On Error Goto ErrHandler
2: ‘some code
3: 4: ErrHandler:5: ‘do some error reporting
The team had to be very disciplined to make this work. One funny thing I came across was a statement like the following:
1: On Error Goto Hell
2: ‘some code
3: 4: Hell:5: ‘do some error reporting
In my Fox Pro days, it was:
ON ERROR DO ME
I completely 100% agree. They did something similar to this kind of crap at True.com (which is VB.NET) ALL THE TIME. They were all VB6/FoxPro developers and would catch all exceptions. log it, then keep running. So if a method that returned a string had an error, it just returned an empty string and kept on trucking. If it was supposed to return an int, it returned 0 or -1 or something.
it was so painful to actually debug and fix any given error.
Yeah, but I miss how “ON ERROR RESUME NEXT” was a magic elixir you sprinkled over your ASP code to make things work.
Oh man, that is one of my biggest pet peeves. Anytime someone asks me to debug a “really weird problem” with a VBS or ASP, I immediately tell them to comment out any occurrence of ON ERROR RESUME NEXT and get back to me if they still can’t figure out what the root problem is. I don’t think they ever come back.
is that simple.
but there is this thing with programmers and exceptions, I don’t know what it is, a lot of the people I know don’t like to use them, and when they use them they use them wrong
I’d also add the following mantra:
“Throw Low, Catch High”
Often you’ll see code that both throws and catches errors. While this may be okay in some circumstances, catching errors in business or data layers is almost always not a good idea, IMO…
Steve- On Error DO ME is hysterical. Man, I love finding stuff like that in code!
B
I’ve been through all of it since DOS box C and Batch development. The other side of your good point is to make exceptions exceptional.
To me that means return something useful like a string for user interaction or a number / enumerator. You shouldn’t throw an exception every time you want to return status to the caller. There isn’t always a user to respond of course. Then even if you did throw and exception, what if you had 100 possible options. Are you going to write 100 different exception classes or put a result type in an exception. Then you’re back to square one. It may be a batch process that needs to send the string to a log or act according to the error number to retry the operation or send it to MSMQ or email someone about the issue.
Also, returning void when you could have returned a status for the user or for extra processing seems like a waste of good stack space. No status and no string mean all went well.
Now exceptional means if the process is really bad and not just status, then you gladly take the big CLR hit to throw an exception. That gives all power to the developer. In reality, a good application will catch normal and exceptional errors and act accordingly.
These say it best:
http://blogs.msdn.com/ricom/archive/2003/12/19/44697.aspx
http://blogs.msdn.com/ricom/archive/2005/03/01/382756.aspx
Then Brad Abram’s the July 11th posting at:
http://blogs.msdn.com/brada/archive/2005/07.aspx
Some of the presentations with Brad and Rico touched on the fact that exception handling should be exceptional. Perform normal handling otherwise.
I agree with VinceB that exceptions aren’t appropriate in ALL cases. I also realize that figuring out whether something is an “excpetional” case isn’t always straightforward. But things don’t have to be as bad as using a “magic number”, we do have a real boolean data type, after all.
I think the TryParse methods are perfect examples of times when something will fail an awful lot, and its really unnecessary (and costly) to throw an exception.
Jeffrey Richter does a good job of covering this subject in his book.
I really have to take EXCEPTION to this blog entry. If a decently-trafficked website had to throw and catch an exception every time a user didn’t fill in something just right or some mis-match happened in the database, the site would be slow or down most of the time.
Simple scenario:
User wants to login to a popular website
User enters username, but fat-fingers it
Database takes username and finds no user record
So the Data Access Layer should throw an Exception?
No. That’s bad. Just return 0 instead of the user’s ID…or -1, maybe using -2 if their password didn’t match or -3 if the retry-limit has been exceeded. No need to throw exceptions all over the place.
The point is this: Just because a function is “not successful” does not mean it is exceptional. If you handle all possible cases of “not completely successful” with exceptions, you are just using exceptions as flow control. And THAT is the real evil.
Rick,
You make a very good point, and I think most people reading this blog understand not to abuse exceptions. Checking a method Exists() on a user object that returns bool is a great way in your example, but that not an “error code”. That’s a valid method with a concrete result.
It would be bad to have a LogIn() method that returned “-1” if the user didn’t exist, a 1 if the login was successful, and a 0 if the password was bad.
Yes, error codes are evil. Anything that is a purely numeric representation of something is a *bad* thing. Might have been necessary back in the days when we counted bits, but these days it’s a waste. Exceptions for exceptional processing and return codes for processing results. I don’t see any reason to do anything else in any language that supports exceptions.
Hello,
Answering VinceB comment:
“Then even if you did throw and exception, what if you had 100 possible options. Are you going to write 100 different exception classes?”
My answer is this:
– if those 100 possible error options are each handled in different ways, then yes, we should create 100 exception classes and throw them when the function
cannot complete its task
– if some of those 100 possible error options are handled in the same way, group the error details under the same Exception class derived type.
Jeffrey Richter in his book “CLR via C#” says in Chapter 19 – Exception
The exception-handling mechanism offers several advantages over a 32-bit number.
– An exception includes a string description of the problem;
If an invalid argument exception occurs, you will know exactly which argument is causing the problem. The string might also include additional information giving you guidance as to how to improve the code.
– Exceptions also include a stack trace that tells you which path the application took when the exception occurred.
– Exceptions is that they don’t have to be caught or detected at the place
they occur; any code in the thread’s call stack can handle a thrown exception.
This simplifies coding substantially because you don’t have to associate error detection and correction code with every statement or method call that might fail.
– perhaps the biggest benefit of all: an exception can’t be easily ignored.
If a Win32 function is called and returns a failure code, it’s all too easy
for the caller to ignore the return code, assume that the function worked as expected, and
allow the application to continue running as if all is fine.
All of the methods defined by types in the Microsoft .NET Framework throw exceptions to
indicate that the method couldn’t complete; no 32-bit status values are returned. All programmers must therefore have a full understanding of exceptions and how to handle them in their code.
Microsoft made a great decision here! Using exception handling in your code is straightforward and allows you to write code that’s easy to implement, easy to read, and easy to maintain.
In addition, exception handling allows you to write robust code that’s capable of
recovering from any application situation. Used correctly, exception handling can prevent
application crashes and keep your users happy.