views:

1446

answers:

17

Recently I attended Jeffrey Richter's training courses about .NET. He mentions one strategy of coding "Dying is awesome". That is, don't write "catch (Exception ex)" even at the root of program or event loop. If some exception thrown that is not handled, just let the process die.

I'm not sure this is right. Personally, I prefer to have a "try {...} catch(Exception ex) {log and try to recover}" to wrap at the top level of execution. Actually, ASP.NET doesn't die if any exception is throw from asXx. If it does die for exception, then one silver-bullet request can silence the whole service.

What do you think?

+7  A: 

That all depends on how you treat exceptions. If you only throw exceptions when something truly exceptional has happened (and not when a database query return no results, or is ill-formed) then you really shouldn't need to ever catch exceptions. Because your program shouldn't be able to recover, anything you can recover from is not an exception, but an error.

toast
Exactly. Perfectly said.
Beska
Yeah but what about my printer not being there. Is that _exceptional_ ? I don't think it is. However what I do know is someone called .Print() and I cannot fulfill their request. Therefore I generally throw. I personally don't think the "exceptional exceptions" is a good rule for where to use exceptions. I throw when I cannot do what someone has asked me to do.
Quibblesome
If it's not exceptional, then it is at least expected. If it is expected, your program should be handling the situation. If the printer is not available, you shouldn't even try to print. This is something you can plan for. Throwing exceptions for things you cannot do is bad lazy. If you are writing a calculator, you should never throw a divide by zero exception. You should instead make sure hat it will never happen by checking divisors before use.
toast
+5  A: 

This is very microsoft.

MS want you to throw all unhandled exceptions to WER. (Windows error reporting, its the dialog you get to send your error to microsoft when an app crashes)

That way you get usage metrics and can focus on the key unhandled exceptions which are causing customers grief. I believe the idea is it forces you to think about where the exceptions will come from.

But I wholeheartedly agree with you. Even if you rethrow after, you would always catch an unhandled at the root, log what happened. The only problem with this is if your unhandled ex is an out of memory in which case your call to log could fail as the JIT can't allocate any more memory etc. I think C++ programs deal with this by taking a bubble of memory, releasing it on an unhandled exception and then running a very tight log routine to try and fail gracefully.

Spence
+2  A: 

I am not familiar with Richter's material, but I think the philosophy/justification is that in the vast majority of cases there is really nothing that the program can do to recover from an unhandled and unexpected exception.

Letting the program die in front of a user, however, is not a very good idea - it leaves a very bad impression. I prefer to log unhandled exceptions to a database, notify the developers, and track them to make sure that they get fixed.

Then again, I sell a product for exactly that purpose, so I am a bit biased in this area!

Steven A. Lowe
+1  A: 

What does your customer expects?

It all comes back to that. If the customer can handle the program dying and they can restart it, so be it.

Sometimes, it is better to let a process die and create another to handle requests.

Sometimes, it is better to attempt to resolve the problem.

David Segonds
+28  A: 

I think it depends on what kind of app you are running and what the consequences of 'dying' are. For many client apps, dying is awesome. For servers, often not so much (and swallow-and-log is appropriate). There's no one-size-fits-all solution.

Brian
+1 Totally agree with there is no one-size-fits-all solution. On a network or web handler error with app logic, why would you ever want to bring the entire system down without a good reason? What about medical devices? I can think of 100 other cases.
Elijah
On the other hand... What about dying - and allowing a watchdog process restart the application?
Arafangion
Spot on. We have a principle of "dying" but our top level catches all _unexpected_ exceptions and transforms the death into a more user-friendly experience. No "continue" though. Hitting a key restarts the application. It's an embedded app for transport btw.
Quibblesome
+1  A: 

I say you should always have a root exception catcher... Especially if you can fit some kind of information in the exception saying what went wrong, a code or something and print it out to the user. Then, the user can always ask you(or support or whatever) what went wrong and provide some info.. rather than "it crashed with a protection fault"

Earlz
+16  A: 

Also known as Offensive Programming.

You should check out, "Offensive Programming: Crash Early, Crash Often".

Which is a very different ideology from the norm of Defensive Programming:

[Defensive Programming] is intended to ensure the continuing function of a piece of software in spite of unforeseeable usage of said software.

I personally like the "Crash Early, Crash Often" philosophy.

I've seen far too many:

try  
{  
    // ... Huge block of code goes here  
}  
catch(Exception ex)  
{  
   // Do nothing...  
}

Which is far worse than crashing hard. If exceptions are handled properly then a little defensive programming is fine.

Simucal
This should be a firing offense unless there's a very good reason for it.
Loren Pechtel
Firing *squad*, that is.
Rob
+2  A: 

If you can't handle the problem adequately--and if it's anything other than rejecting bad input in some form (this includes something you tried to read from the disk or web) you probably can't--then you should die. Unless you're 100% certain you can continue safely, don't.

However, I wouldn't simply let an exception go. Catch it and gather as much information as you can. If your program manipulates a document of some kind save it UNDER A NEW NAME--it might be corrupt, you don't want to overwrite the original.

Loren Pechtel
+2  A: 

If an exception bubbles up unhandled, that means your software doesn't know how to deal with it. It's something unexpected and silently ignoring it can have broad variety of consequences depending on complexity of your code.

By having a global silent handler, not only you're ignoring the error but you're also preventing the user from knowing about it. So user assumes everything is going fine where there is no way you can guarantee that because you don't know what has happened in the first place.

Just showing an informative popup and continuing execution also gives user incorrect message about seriousness of the problem occured. When you continue execution, user thinks "everything can continue as it has been" which is something you actually don't know.

One might say global exception handlers could be useful when you "save critical files and exit" to allow recovery of unsaved work in the future. However this isn't the right approach to that problem because not every exception can be handled by .NET exception handlers, such as access violations (yes .NET can have those too). In that case you're creating a false sense of security which can be very annoying if user cannot recover their files when they expect to. Instead the software should be using temporary files and check their existence in the next startup.

This is similar to "blue screen of death" or "kernel panic" notion where operating system intentionally stops further execution to avoid "unknown consequences". Because once a simple access violation can turn into complete corruption of your file system. Same principle applies to .NET exceptions too.

ssg
A: 

The problem with having try catch around everything is that you often end up 'handling' things that you don't know how to recover from. The user gets a false impression that things are going fine, but internally you turn to swiss cheese and eventually break in really strange ways.

On the other hand, throwing the exception up to the user isn't all that helpful when they don't have some way of reporting it too you

  • the new windows services provide that feature when you register with MS.
  • installing drwatson can also capture the data (dump files) for your users to send to you manually.

If your providing a service library and it's run in process, if your service messes with the overall server messing up memory or settings and such, then perhaps the server will realy need to shut down when the exception is raised.

By contract APIs will tend to provide a way to say 'can I do this' and then a way to 'do it', and many APIs like the MS file open will allow you to change between raising an exception and returning an error code.

Greg Domjan
A: 

You can always setup a custom error handler and ignore catching exceptions that you aren't prepared to handle. Trying to recover from unknowns may have negative consequences too if the right conditions are met. But then again, that really does depend on what the exception is and how you're handling them.

On the other hand - I think people get to overwhelmed with the whole "exceptions are evil" mentality. They are a tool, and if used appropriately, can have astonishing perks. However, many people misuse them by wrapping root with catch(Exception)..

Chance
+1  A: 

There's one advantage of not handling any exceptions at all. If there's a problem, you'd rather have a crash and the user complain, instead of having the program continue in an indeterminate state.

For example, if you're writing a real time trading system, and there's an unexpected error, you're better off letting it crash. Otherwise, your program might keep going and make stupid trades. The users will complain right away "WTF?", but at least you don't lose millions of dollars.

+2  A: 

Nothing destroys user confidence faster than a stack trace. At a minimum, catch the exception and log as much information as you can. Then provide the user with a friendly message and instructions as to what to do to either work around the issue or report the problem to support.

There is concern here as to continuing in an indeterminate state. If this is a web app, then this is not a problem unless you are overly dependent on session and app state. If this is a windows app, then feel free to exit (after giving the user a chance to save).

Glenn
+7  A: 

Sounds like Guruspeak

This sounds like another one of those general guidelines preached by gurus that is not a bad piece of advice in itself. However, this guideline could easily be applied to places where it does not belong. I think the key phrase to remember that you used above was "one strategy of coding" because this stategy is very useful in certain domains, yet can be quite harmful in others.

Dying is awesome - if you have a lot of tightly coupled components whose state depends on each other, then an exception could easily be a catastrophic event. However, one of your goals should be to code in such a way that a single failure does not have to bring down the entire system (notice goal).

What would you think of the following applications dying on a run-of-the-mill exception:

  • Medical Devices
  • Power Plants
  • Intrusion Detection Systems

For exceptions that you are catching in try / catch - you really should be expecting them and handling them. For all other cases, it is good to fail fast to a predicted run-level. So, if you are at a network or web handler, why not just have the current operation die? Do you really need the whole app to go down?

This becomes increasingly important if you are developing an app that is mission critical and has a public interface. If there are exceptions available that can bring the app down, then that becomes a vector of attack for black-hat hackers intent on causing a denial of service attack.

The edge cases of abusing this strategy are a little too big to give it much praise. A much better approach, is to solve the problem of your domain. Understand what this stategy is getting at and apply the appropriate parts to your problem.

Caveats: I work on server-side systems where uptime and security is critical.

EDIT: I guess I got confused by what was meant by "process die" - was this a reference to the entire app or just the running thread, etc?

Elijah
+1  A: 

Awesome answers here already, especially by Simucal. I just want to add a point:

Offensive programming is excellent for debugging. In my experience, "fail early, fail often" can be thought of as like setting traps for bugs in your code. When something goes awry--whether it's a memory leak, memory stomp, unexpected NULL, etc.--it's generally much easier to see the problem if the program fails immediately (with associated debugging data such as a callstack).

Defensive programming is excellent for production environments. Once your application has shipped, you may not want your end-users to ever see a nice little dialog saying "unhandled exception at foo()." That might be great if your app is in beta and you're still gathering debug info, but if you're shipping a stable app you might simply want to not bother the user with cryptic output in the rare case that something does fail.

Sometimes you can have it both ways. I've often wrote C code along the following lines:

void foo(char* data) {
    ASSERT(data);
    if (!data) { return; }
    // ... work with data
}

If this code is compiled in a test environment, the assert statement traps the error condition. If it is compiled in a production environment, the assert is removed by the pre-processor and foo() fails silently.

Exceptions are more expressive and flexible than asserts, and there are various ways to manage them such that an app fails early in a test environment and chugs along while logging errors in a production environment. Depending on the type of project, this sort of design may make sense for you.

Parappa
"in a test environment, the assert statement traps the error condition."...and then I hope the code that calls foo with uninitialized data is corrected I hope, because it generates an error? That makes the if test useless in my opinion.
jan
Yes, the whole idea here is to make the if test redundant, however if you're coding business logic in C (not the best tool for the job) you may want that redundancy. There could be rare conditions not caught by testing that occur out in the wild.
Parappa
+1  A: 

Since you don't know what every subclass of Exception IS, you simply CANNOT know it's OK to catch them. Therefore, you should mind your own business: catch exceptions you know and care about, which will mainly be the ones you've explicitly created for your own program, or the ones created for errors from the library calls you're using. In particular, catching the Exception class is just lazy, bad programming --- essentially, you're saying "I don't care what the problem is, and don't tell me; just do this." Take a look at how java programs handle exceptions, as the exception handling practices there are usually pretty good.

Lee B
+4  A: 

Karl Seguin says the following about exception handling:

  1. Only handle exceptions that you can actually do something about
  2. You can't do anything about the vast majority of exceptions

Good intro to the subject.

Nick
Sucinct and correct. +1.
Beska