views:

90

answers:

1

I think I have something like "programmer's OCD". I like my code to be esthetical and clean, and I want it to be "perfect" (as in handling all possible situations correctly and pretty). Often I find myself spending a lot of time just going over the same areas again and again to see where I can optimize and where I can fool-proof.

So when it comes to try...catch blocks, I experience somewhat of a paranoia regarding what to enclose. I mean, where do I draw the line for what the code should cater to? Take file handling, for instance. Should I put, say, every damn file operation in a try...catch block, in case something could have happened (file being locked by someone/something external to the application, disk corruption etc)?

Sometimes it burns my brain feeling that maybe there's something (that I'm not even aware of) that could trip up some piece of code..

Edit:

I'm not talking about using try...catch to cover shitty programming, I'm talking about when it comes to operations and procedures that are otherwise implemented properly, but rely on other factors outside of my control - even if they might be obscure (and this is the point) and only happen under extremely "unlucky" conditions that I haven't foreseen.

File handling is kind of an obvious example. When I tend to get the jitters, it's when I'm wondering about what kind of processing goes on behind the scenes of otherwise built-in functionality, and how it responds to my code.

Here's an example:

Dim serverUrl as String = My.Settings.ServerUrl

There's a disk operation involved there (reading from app.config). Should that be enclosed in a try...catch block? This is what I mean by where does it end.

Fear of memory leaks is another thing. Is it only unmanaged code that poses a threat there? How do I know what is unmanaged code? Is there a list?

More edit:

Another area where I don't feel confident is when there are access restrictions or policies at play somewhere under the hood.

When I read articles and discussions on programming, I see a lot of explanations along the lines of "well, your problem is that when you do a call to X, .Net is internally trying to access so-and-so, and unless your application is running in context type Y or you have privileges Z, it will throw an exception". Which just adds to my paranoia - when it comes to building water tight exception handling. Because I simply don't know all the inner workings of the language/platform, and don't know where to look (without having to devote my life to studying it).

I would love for there to be some form of compendium or concise wiki for this specifically, that would outline which areas of programming that need special attention (file handling etc), with example scenarios, typical challenges and culprits (with solutions), best practice models, programming patterns and, not the least, provide a set of guidelines for mere mortals like me, that unfortunately weren't involved in actually constructing the language and its libraries.

All this in one place, instead of having to track down scattered pieces of information in the language reference or random articles on the net - I don't even know what to look for, in many cases.

As for my particular, current project, it's within the context of a Windows Service. There is no UI, and one of the subtasks I'm working on is to create a robust bootstrapper that handles all problem scenarios gracefully. In this case, it's all about logging - and then either ignoring the exception (if it's trivial enough) - or simply exit! If the problem happens while attempting to log - then what do I do? Just quit - with no trace of what happened? This bootstrapper only logs its startup (after that, the main assembly - which is dynamically loaded - takes over and logs its own stuff, albeit with the same challenges), and does so to a simple "bootstrap.log" file. Would it be better (or a worthwile addition) to have it log to the EventLog? Or is the EventLog another area that could spawn a new world of problems (again, access restrictions etc. Does the EventLog also base itself on disk operations that would need to be "tried and caught"..?)

See? Paranoia.

+8  A: 

Should I put, say, every damn file operation in a try...catch block, in case something could have happened (file being locked by someone/something external to the application, disk corruption etc)?

Generally, yes.

Exceptions that must be handled

File operations are inherently unsafe, all hell can break loose when you use it. Consider the case that you’re reading a file stored on an USB storage device and mid-read the user pulls off the USB stick.

This will raise an exception, and you need to guard against that. This is also true in the particular example you mentioned.

On the other hand, it’s usually only the high-level code that needs to handle this kind of error, not the code deep down in your business logic. It is often OK to let a method fail and propagate an exception as the result of an IO failure. It’s merely important to prevent this exception from crashing the application, and notifying the user that something has gone wrong, or try again, or take evasive actions.

Which layers handle exceptions?

An article from 2003 by Ned Batchelder explains which parts of the code should guard against exceptions, and which shouldn’t:

He distributes code into three layers:

  • The A layer, adapting the API underneath it,
  • The B layer, building pieces of your system, and
  • The C layer, combining it all together.

I would choose slightly different layers:

  • The low-level layer (consisting of layers A and half B) which builds the low-level API of your system and adapts even lower-level APIs),
  • The middle layer that implements the bulk of the business logic (mostly layers B and C) and
  • The UI layer that the user interacts with.

In any case, the lowest layer should translate exceptions from the lower-level API into your domain.

The middle layer should let exceptions pass through: no exception handling in the business layer. This should also be the bulk of your code, which means that you don’t need to handle exceptions in most of the code (but there may of course be exceptions to this rule).

And the upper layer should catch exceptions and respond to them, or present them to the user in readable format. A good way of handling this kind of exceptions is by installing a global exception handler, or by offloading work in GUI applications into an extra thread (e.g. a BackgroundWorker) and catching an exception when the thread dies.

Exceptions not to handle

Not all exceptions need to be handled in this way. For example, you shouldn’t try to handle a StackOverflowException (you can’t!) or an OutOfMemoryException (you usually can’t).

Such exceptions mean that there is something seriously wrong with either the code or the machine and your application has no chance of recovering from the failure.

Konrad Rudolph
What about the example I added in an edit to the question: Accessing application settings..?
d7samurai
@d7samurai: see rewrite.
Konrad Rudolph
In my current project, I'm working on a Windows Service that has no UI, so it's all about logging. But the logging functions themselves encounter exceptions <i>while attempting to log</i> (like writing to the log file), they can't really log that.. So I'm left with a feeling of incompleteness. Within the context of <i>my</i> app, there are also a set of "layers": First is a bootstrapper that logs its startup success (to a log file only - it's very basic) and scans for the most eligible "engine" plugin. The engine is the next layer, and it has a more redundant system.
d7samurai
It will emit to an event log, a disk log, a memory log (that can be polled remotely by contacting a built in WCF service), a database, and via email/sms, arranged (in the code, not in my listing here) in order of how much potential they have for failing or how much they rely on other components (that might prevent them from performing). That means it has several "safety nets" for at least getting some info to the administrator. Still, at the lowest level, it seems like there's only two options: Either to "skip" the logging after failing (which is dangerous, since the same problem that [cont]
d7samurai
caused the logging attempt to fail could easily be a show stopper for the actual functioning of the application itself down the line - and then without any logging), or to simply quit the application (but with no way of letting the outside world know what happened).
d7samurai
[first comment should say "But IF the logging functions themselves encounter exceptions.." - and I just realized tag formatting doesn't work in these comments :|]
d7samurai
if logging fails, you can either try other logging channels (send an e-mail to the admin) or store the data and retry later.
Philipp