Is “Dying is Awesome” preferred?

2019-01-17 04:41发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

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.



回答4:

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?



回答5:

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.



回答6:

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.



回答7:

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.



回答8:

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!



回答9:

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).



回答10:

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.



回答11:

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.



回答12:

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"



回答13:

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.



回答14:

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.



回答15:

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.



回答16:

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)..