This site will look much better in a browser that supports web standards, but is accessible to any browser or Internet device.

Anomaly ~ G. Wade Johnson Anomaly Home G. Wade Home

May 30, 2005

Joel on Exceptions

As I said in Joel on Wrong-Looking Code, I find Joel on Software to be a good source of information on programming. However, I don't always agree with him. In Joel on Software - Making Wrong Code Look Wrong, Joel states that he does not like exceptions. He also refers to a previous article Joel on Software - Exceptions, where he first talked about his dislike of exceptions.

Rather than attempting to explain his rationale, I suggest that you read his essays before continuing. I think he makes some valid points and argues his case well, even though I disagree with his conclusion. I do not intend to try to prove him wrong, but to give a different view of exceptions based on my experience and understanding.

Joel's arguments against exceptions fall into five categories:

  • the goto argument
  • too many exit points
  • it's hard to see when you should handle exceptions
  • not for mission critical code
  • they're harder to get right than error returns

And, just to show that I'm not blindly defending exceptions, here's a few that he didn't cover.

  • not safe for cross-language or cross-library programming
  • possibly inconsistent behavior in threaded programs
  • lack of programmer experience

Let's go over these points one by one.

The goto Argument

Let's start with the goto argument. Joel states that exceptions are bad because they create an abrupt jump from one point of code to another (from Exceptions, above). He does have a point. However, the if, else, switch, for, and while all create jumps to another point in the code. For that matter, so does return.

The original letter about the go to did not refer to every kind of branch in code. The fact is that Djikstra did state that limited forms of branching like if-else statements or repetitions like while or for don't necessarily have the same problems as the go to, His point was that unconstrained jumps to arbitrary locations make reading the code almost impossible.

The thing that separates an exception from a go to is the limits placed on where the exception can transfer control. A thrown exception is in many ways, just a variation of return. It may return through multiple levels (unwinding the stack as it goes), but an exception does not jump arbitrarily in your code.

Obviously, we wouldn't be willing to give up if in order to remove an abrupt jump to another place in the code. The difference between the goto and the if is limits. The if is constrained in where it is allowed to jump. Likewise, any exception is only allowed to transfer control back up the call stack.

While it is obvious that throwing an exception is more powerful than an if, it should also be obvious that it is much more constrained than an arbitrary go to construct. So the comparison of exceptions to go to is at best an appeal to emotion. Unfortunately, it is an argument I've seen far too often.

It's Hard to See When You Should Handle Exceptions

In Joel on Software - Making Wrong Code Look Wrong, he states that one of the reasons he doesn't like exceptions is because you can't look at the code and see if it is exception-safe. Given his earlier statement about how important it is to learn to see clean, I find this statement particularly interesting.

Although I understand Joel's commentary about the difficulty of making certain that code is exception-safe, I maintain that a good use of exceptions can help create cleaner code, that is easier to maintain. The key to creating exception-safe code is using the guarantees provided by the exception mechanism. Unlike Java, C++ makes a hard guarantee, Any local variables are destructed properly when an exception leaves the scope they were created in. This has lead to the resource allocation is initialization idiom (RAII). I'll use Joel's example to explain:


dosomething();
cleanup();

This actually jumps out at me as incorrect code. What am I cleaning up? This sounds like either a resource cleanup issue or a dumping ground for a collection of things that happen to be done at this time. If it's the first case, code probably looks more like this:


setup();
...
dosomething();
cleanup();

In which case, the RAII approach would be to create an object that manages the resource. The equivalent of setup() is the object's constructor. The equivalent of cleanup() is the object's destructor. This changes the code to:


Resource res;
...
dosomething();

This code is safe in the face of exceptions and cleaner to boot. More importantly, in my opinion, the setup and cleanup of the resource is now defined in the one right place, the definition of this class. Now if I ever use the resource, I'm guaranteed that the cleanup code will be called at the appropriate time. There's no need to remember to call cleanup() at the right times.

Unfortunately, this idiom will not work for Java, since that language specifically does not promise timely destruction of objects. In that case, as Joel points out, you are required to use the try ... finally mechanism to ensure cleanup. For this reason, Java does not always allow exceptions to clean up the code.

Not For Mission Critical Code

Joel goes on to suggest that exceptions would not be useful on larger or more critical systems. In fact, a large portion of my career has been focused on long running programs (including systems with 24x7 uptime requirements). What I've found is that the code without exceptions tends to fail in strange ways because error returns are tested inconsistently, and error codes are handled differently in different places in the code. Keeping all of these different places consistent requires a large investment in time as the code changes.

Now, one could make the argument that if all of the error returns were tested and if all of the errors were handled consistently and properly, the error-return based programs would be perfectly robust. Then again, you could also make the argument that if the exceptions were all handled correctly, the code would be perfectly robust, too.

The main difference here is that if the programmer does nothing, error returns are lost by default and exceptions terminate the code by default. For this reason, exceptions tend to be more likely to be handled because they would otherwise terminate the code. If an error return check is accidentally left out, no one may notice and testing may not point out the problem. If an exception is not caught, the program is likely to terminate during testing and, therefore, the problem will be found.

I do realize that either error returns or exceptions can be ignored or handled badly by any particular programmer. But in my experience, exceptions have been harder to ignore. While I probably would not say that exception code is easier to get right than error return handling, I would say that error return code is easier to get wrong.

Harder to Get Right Than Error Returns

Handling error returns explicitly in the code tends to increase the amount of error handling code spread throughout the program to the point that it hard to see the actual code logic for the error handling logic. I agree with Joel that your best programming tool is the pattern recognition engine between your ears. I find it much easier to understand and fix code logic if I can see it, without a large amount of extraneous code in the way.

In many ways, error return checking and handling code (if done completely) can obscure the main logic of the code to the point that maintenance becomes a nightmare. To extend Joel's cleanup example a bit, let's see what error return checking code does when you need to perform cleanup.


setup();
doSomething();
doSomethingElse();
doOneMoreThing();
cleanup();

Let's say that each of the functions above returns an error condition that we need to check. Let's assume further that we need to return the error from this routine. Finally, we will need to perform the cleanup in any case. So the code might end up looking like this.


ErrorType err = SUCCESS;
if(SUCCESS != (err = setup()))
{
// no need to clean up.
return err;
}
if(SUCCESS != (err = doSomething()))
{
cleanup();
return err;
}
if(SUCCESS != (err = doSomethingElse()))
{
cleanup();
return err;
}
if(SUCCESS != (err = doOneMoreThing()))
{
cleanup();
return err;
}
cleanup();

or like this


ErrorType err = SUCCESS;
if(SUCCESS != (err = setup()))
{
// no need to clean up.
return err;
}
if(SUCCESS != (err = doSomething()) ||
SUCCESS != (err = doSomethingElse()) ||
SUCCESS != (err = doOneMoreThing()))
)
{
cleanup();
return err;
}
cleanup();

or any of several variations on this theme. In any case, the call to cleanup() ends up duplicated.

In my experience, anything that is duplicated will probably be left out at some point during maintenance. More importantly, if the cleanup is more than one statement instead of a single function call, the different places where it is written are likely to get out of sync. Careful code reviews can reduce this problem, but it still hangs over the process.

More importantly, to my mind, is the fact that the actual logic of the program is now obscured by the mechanics of the error handling. This is the problem that exceptions were created to reduce.

If we once again apply the RAII idiom this code becomes.


Resource res;

doSomething();
doSomethingElse();
doOneMoreThing();

In this code, it is much easier to see the main line of the logic. What you can't see is exactly what exceptions might be thrown. But, the original code did nothing specific with the error returns, it just passed on the error condition to the calling code. This code does the same, it just does it in terms of exceptions.

Cross-Language/Library Programming

Unfortunately, the C++ standard cannot promise compatibility of exception across language boundaries. There are almost no guarantees if your C++ code calls a C routine that uses a C++ callback that throws an exception. Obviously, this is not a situation you'll run into every day, but it is a problem. This issue may also apply to JINI code that uses C++.

A bigger problem is the fact that exceptions are not necessarily binary-compatible between modules, even in C++. It is possible for a library compiled with different options to produce exceptions that do not quite match the exceptions from the rest of the code. (See C++ Coding Standards for the details.)

In the worst case, this just means dealing with error codes at the boundaries of these two cases. So it is no worse than dealing with error returns normally.

Behavior in Threaded Programs

Some C++ implementations may have difficulties with exceptions thrown in threaded code. In the past, I've seen exceptions improperly propagated into the wrong thread. Most modern C++ compilers and libraries should have solved these problems. As far as I know this has never been a problem in Java.

Lack of Programmer Experience

This is probably the biggest problem with exceptions. A large number of programmers are not very experienced in using exceptions correctly. I've seen exceptions used in cases where an if or switch would have been much more appropriate. I've seen cases where an exception was caught as a generic exception and then a series of type tests were performed in the catch block. I've seen people use empty catch blocks to ignore exceptions.

The solution to this problem is education and experience. Unfortunately, this one takes time. Fortunately, many of the bad practices above can be found relatively easily in code reviews.

Conclusion

I would like to conclude this (overly long) essay with a summary. My goal in this essay was not to prove Joel Spolsky wrong in his dislike of exceptions. My purpose is to give an alternate view. My experience has shown (to me at least) that there is quite a bit of truth in the arguments against exceptions. On the other hand, I have seen several cases of exception usage increasing the robustness and correctness of large and small programs.

To link this back to Joel's essay, the main issue here is the need to learn to see well-done exception handling. This is something that our industry needs a lot of practice with. However, exceptions are a powerful tool and ignoring them may not be a viable technique in the long run.

Posted by GWade at 04:00 PM. Email comments

May 24, 2005

Review of C++ Primer (Fourth Edition)

C++ Primer (Fourth Edition)
Stanley B. Lippman, Josée Lajorie, and Barbara E. Moo.
Addison -Wesley, 2005.

Every now and then, someone asks me to recommend a book for learning the C++ programming language. Until very recently, I didn't have a good answer. The book I learned C++ from went out of print years ago (and would be horribly out of date by now). There are lots of books that teach beginning C++, but I hadn't read one in years.

That has all changed. In the last few years, there have been two books with a new approach to teaching C++ that I have actually liked. The first was Accelerated C++. That book provides a good way to get your feet wet in C++. Unfortunately, the C++ language is so big and rich that it was not able to really do the subject justice. Now, the fourth edition of the C++ Primer comes out and uses a similar teaching technique and strives to cover the whole language. Amazingly enough, I think it succeeds.

The approach to teaching C++ that I learned was to start with an understanding of C. We start with the C way of doing things and introduce the C++ features on that base. Eventually we replace C features with C++ and the training is complete. This book takes a different approach. Since the authors are teaching C++, the C way of doing things is ignored from the beginning.

We get std::iostreams and std::strings almost from the very beginning. When we first need to collect multiple items of the same type, we are introduced to std::vector. C-style strings and arrays are discussed much later in the book, as relatively minor features of the language. In fact, when arrays are eventually introduced, the authors spend a fair amount of time describing their deficiencies when compared to vectors.

The readers of the book are also introduced to the use of classes and objects the same way they are introduced to int and floats. Time is spent explaining how to use the library classes with plenty of exercises and examples long before the reader is shown how to construct their own classes. I feel this approach does a much better job of acclimating the new programmer to objects than the older approach which began by trying to build classes.

In addition to the different style of teaching, this book also has relatively complete coverage of the language and libraries. Some of the sections on the standard containers and algorithms actually made me think about those tools in new ways. This is despite my having used those classes for a large number of years. The book also includes a few sections on more advanced topics that might not be appropriate for the C++ beginner. But these sections are well labeled and the reader is warned to skip ahead until some of the earlier material is completely mastered.

In a book this size, it would be hard to get everything exactly right. I found a number of minor editing errors, as well as a few style issues that I thought might not serve a junior programmer well. Other than that, my only complaint is that it is a big book (weighing in at 885 pages including index).

All in all, I plan to recommend this book to anyone wanting to learn all of C++. This is a terrific introductory book that covers enough of the language to keep someone learning for quite a long time. It also covers enough best practice sort of information to make certain that a novice programmer is steered away from some of the worst mistakes.

Posted by GWade at 10:22 PM. Email comments

May 23, 2005

Joel on Wrong-Looking Code

I want to preface this article with the comment that I really enjoy reading Joel on Software. I find his essays to be knowledgeable, well-thought-out, and well presented. While I don't always agree with his conclusions, I always feel that any of his articles is worth a read.

So, recently I ran across Joel on Software - Making Wrong Code Look Wrong. The title looks like something I've tried to explain to junior programmers before, so I figured I'd take a look. If nothing else, I thought he would give me new arguments to use. You should definitely read his essay before continuing. I want to be sure not to ruin his set up and delivery. I also want to make sure that you understand his points before I explain where and why I disagree with him.

The essay started off with some commentary on code cleanliness and an interesting anecdote from his early work life. So far, so good. Then, he throws in his curve ball. In this essay, he plans to defend Hungarian Notation and criticize exceptions. I have to admit that I thoroughly dislike Hungarian Notation and I find exceptions to be a pretty good idea. If anyone else had stated this as a goal, I probably would have just left the page. I've got plenty of other stuff to read. But, this I had to see.

After an interesting example, Joel goes on to explain that what we all know as Hungarian Notation is actually a corruption of the original Hungarian Notation. The original does not add a wart for type, which is useless and leads to bad code. It added a prefix for the higher level concept that the variable represents. A good simple example is using col on the front of any variable referring to a column, and row on variables referring to rows. This makes it obviously wrong when you assign a row value to a column. The goal is to add semantic information into the variable name. Unlike, syntactic information there is no way for the compiler to help you with the semantics. This version of Hungarian Notation actually has the possibility to help programmers, rather than just creating unreadable variables.

The funny thing from my point of view is that this idea is the only vestige of Hungarian Notation that I kept from a brief stint of using it years ago. Apparently, I (and probably loads of other programmers) accidentally stumbled across what Simonyi had originally intended, despite loads of literature and examples misusing it. So, by the end of this part of the article, I find myself agreeing with Joel, despite the fact that I was adamantly against what I thought was his position.

As the article continues, Joel goes on to bash exceptions (his words, not mine). In keeping with the topic of his essay, he states that one of the reasons he doesn't like exceptions is because you can't look at the code and see if it is exception-safe. Given his earlier statement about how important it is to learn to see clean, I find this statement particularly interesting. Since it would take another whole article to refute all of his points, I'll save that for another day.

Posted by GWade at 07:02 AM. Email comments

May 17, 2005

The Importance of Programming Play

For various reasons , I've recently been thinking on the importance of programming play, Those times when we tinker with code without any goal other than making something run on the computer. Sometimes this involves playing with a new language or a feature of an old language that we've recently discovered.

Unlike projects for work, these exercises rarely produce any grand system that makes money or provides services. Often they result in minor amusements, small tools, or throw-away code. The main purpose of the exercise is stretching our programming muscles. Trying out a new technique or feature without the pressure of having to succeed gives you the opportunity to stretch in ways you can't when a deadline is looming. Not having a deadline also allows you to experiment with ideas that may be too risky for production use. Like brainstorming, this appears to be a very effective way of developing new approaches and keeping sharp.

Lately, it seems that many programmers that I know and ones I read have lost sight of this simple pleasure and its importance. But, many of the best programmers I've known were always playing with code. They have numerous little side projects going on, some of which will never be completed. Some are huge, ornate systems and some are little utilities. The main thing they all have in common is that the main goal of each of these projects is enjoyment.

Fortunately, not everyone in our field has forgotten. In Fun, Simon St. Laurent talks about the thing that got him into the field. Dave Thomas suggests the concept of programming Katas as a way to keep your skills sharp. These are more exercise than play, but the goal is the similar. In the book Slack, Tom DeMarco explored some aspects of this issue. The Perl community has had a long-standing tradition of playing with code including the Obfuscated Perl Contest (which has it's origins in a similar contest for C) and Perl Golf.

The key point here is that programming is a creative activity. It is hard to be creative without taking risks and trying wild ideas. In our day jobs, we very rarely have the opportunity to risk a project on a wild idea. (At least not more than once.) But when you are playing, you have that chance. The only thing you risk is some time, and whichever way the idea goes you gain experience. Trying and refining wild ideas gives us new techniques that can be used when we really need them.

Posted by GWade at 10:51 PM. Email comments

May 02, 2005

Review of Eric Meyer on CSS

Eric Meyer on CSS
Eric A. Meyer
New Riders, 2003

Styling web pages with CSS allows for a very good separation of structure and presentation. In this book, Eric Meyer focuses on the design aspects of using CSS. Many other books describe individual features of CSS and even show how to generate particular effects with CSS. This book walks through a series of projects and shows, step by step, how a real design expert lays out a site.

In the process, Meyer shows how many CSS attributes should be used. He shows some of the tradeoffs involved in using different features and effects. As you follow along with the exercises, you get more than just the mechanics of changing styles with CSS. You get a sense for how someone who really knows CSS and web design goes about applying his craft.

I found this particularly helpful. I have more than a basic grasp of CSS, but I don't really grok the design aspects. Although I'm not ready to trade in my programmer hat for a web designer hat, this book has given me a better feel for generating a good-looking design.

I would definitely recommend this book to anyone trying to really design nice looking web content. I also can't wait to get a chance to read the sequel to this book.

Posted by GWade at 06:55 PM. Email comments