I love religious arguments. In #5ESS, error handling was a large % of the code. It involved in line checks, dead man timers, separate auditing systems, and lots of code review to make sure there were no `don't care' cases. It was incredibly messy and fragile. Exceptions might have cleaned up some part of it but most of the process was trying to avoid being sloppy. Much of that could be automated through tools, path analysis, and testing. The complexity is still there with inline error handling or exception mechanisms. Separating the exception code can (but not always will) clean up the main flow. It also reduces the number of places you can 'forget' to check. Unfortunately, the further from the main flow it moves, the less likely that it will be updated when the main flow changes. This has caused us almost as many errors over time as missing an exception case. At the very least the exception catcher has to be in the scope of what its cleaning up or everything (recoverable) in the main flow has to be global. However, the important part is handling all the possible error states. The really unforseen ones (except for the odd nil pointer reference or divide by zero) don't throw exceptions. In #5ESS, the auditing code was the mechanism for these kinds of errors. The auditing code continually scanned the state of switch boards, processes, and memory to make sure states didn't exist that were considered 'impossible'. Of course this meant enumerating forseen states. If you stepped outside of the forseen, it was time for action (reset the switch, kill processes, switch to another processor, ...). This is a really hard way to program and not something I'ld normally do.