Skip navigation

My sister gave me one of those peel-off desktop calendars for Christmas, the theme of which was “Stupidest Things Ever Said” or something like that.

February 8’s title was “On Microsoft Programming, Typically Top-Notch”.  The quote was:

Vista Error 10107: A system call that should never fail has failed.

Hardy-har-har.  As someone who writes a lot of error messages, I don’t find that message to be stupid.  I’ve written dozens of messages just like that, typically in exception scenarios that can only occur when something goes catastrophically wrong, for example, the computer returns 5 when evaluating 2+2.  Which do you think is a better message:

Your system is catastrophically broken.  Go buy a new one immediately!

Or what about:

2+2 does not equal 5.  Please call technical support.

Personally, I prefer the former.  Look, if it’s never going to happen anyway, why not have some fun with it?

I think it’s a testament to programmers to begin with that we even bother writing exception messages in API level code, like for example, the Windows Vista kernel.  An API level exception message should never be seen by a user because an API level exception should be properly caught and handled by the application developer.  Of course they aren’t, because the typical application developer doesn’t bother reading the IntelliSense provided by the XML commentary that the good API developer took the time to write about the exceptions that should be handled, and the typical application developer certainly doesn’t bother try/catching those exceptions.  Even the exceptional application developer who is careful about handling API exceptions is going to miss something somewhere – particuarly something like an exception that the API developer may not have even documented because it can only happen if there’s a serious hardware level malfunction, like a kernel level method, for example.

I spend most of my time writing APIs since most of the development I do is system and platform programming – you know, the stuff that has to be as close to fool proof as possible so that the idiot application developers have only themselves to blame.  I am very fastidious about exceptions.  When I write unit tests for my code, I include tests to ensure the right exceptions are being thrown where I expect them.  I wrote this handy method that I use in my testing for this purpose:

         
public static void ExpectException<TException>(Action action, string reason = "")
        {
            try
            {
                action();
                var message = "Expecting exception " + typeof(TException).Name;
                if (string.IsNullOrWhiteSpace(reason) == false)
                {
                    message += ", reason: " + reason;
                }
                Assert.Fail(message);
            }
            catch (AssertFailedException)
            {
                throw;
            }
            catch (AssertInconclusiveException)
            {
                throw;
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAssignableTo(typeof(TException)) == false)
                {
                    var message = string.Format(CultureInfo.InvariantCulture,
                        "Wrong exception.  Expected {0}, got {1}: {2}",
                        typeof(TException).Name,
                        ex.GetType().Name,
                        ex.GetFullExceptionMessage());
                    Assert.Fail(message);
                }
            }
        }

I don’t write user-friendly exception messages.  I write programmer-friendly exception messages.  If I am raising an exception, it’s because they did something wrong.  99% of the exceptions I throw are one of the following:

  • ArgumentNullException
  • ArgumentException
  • InvalidOperationException

The first two are obvious, and the last one is thrown when an argument isn’t bad but the state of the object prevents the method from executing correctly, for example, calling the server delete method on an object that hasn’t actually been saved to the server yet.

While the argument exceptions could theoretically occur if the app dev is lazy about input checking and just throws whatever the user types as input somewhere at an API method, InvalidOperationExceptions are typically only called when the programmer made a stupid mistake, and my messages generally reflect that, e.g.:

 You cannot call Delete() before this item has actually been saved.

If a user saw this message because the exception went unhandled, they’d be confused and probably curse at the developer for being a dumbass.  You know, like all programmers do when they see specific, common, obvious exception messages in modal dialogs from poorly written applications, such as the famous:

Object reference not set to an instance of an object.

That’s the mother of dumb errors.  If an app dev – or god forbid, a systems dev – lets one of those through, that app dev needs a crash course in unit testing.

I use Code Analysis on all of my code – again, because I write platform code, it has to be extremely tight.  And Code Analysis is very picky about some things, for example, using return values.  A lot of times, the return value is useless and you don’t care, like, for example, ICollection.Remove.  If I know definitively that the object is in the collection, why do I need to check the return value of that method?  Furthermore, what on earth am I supposed to do with that value?  This?

var result = myCollection.Remove(myValue);
if (result == false)
throw new InvalidOperationException("Hell hath frozen over.");

This is the kind of error message that would make it on to the “stupidest things ever said” but it isn’t stupid.  It’s there to satisfy Code Analysis.

This is also an interesting example of another problem: code coverage.  I use code coverage to make sure that every line of code is touched by a unit test.  While this does not ensure correctness since code coverage does not guarantee good state coverage, it’s a big start.  I like to see 100% come back from block analysis, but this particular block poses a serious problem, because this situation is so rare that I can’t even fake it.  Suppose in the above example that the code immediately preceding that call to remove was a call to Contains() to ensure the existence of that object.  The only way that Remove would then return false is if a context switch occurred and some other thread came in and removed the object from the collection.  But don’t be silly; I use critical sections.  So that can’t happen.  It’s literally impossible to write a unit test to cover that block unless you cheat.

One way of cheating – the way I usually do- is by adding extra code that is only activated by compiler switches.  I usually use #debug.  I define an internal field, usually a bool, that is only included in debug builds.  I then add another block inside the method that I’m testing that checks the value of that field.  If it’s been set (by the unit test code), then I do something that forces my error condition.  In this case, it might look like this:

#ifdef DEBUG
internal bool HellHathFrozen = false;
#endif
#ifdef DEBUG
if (HellHathFrozen)
myCollection.Remove(myValue);
#endif
var result = myCollection.Remove(myValue);
if (result == false)
throw new InvalidOperationException("Hell hath frozen over.");

But you see, it is possible for that error message to be exposed to users, if for example, you accidentally shipped a debug build if your assembly.  By the way, that’s the reason why you should never, ever, ever use Debug.Asserts in your code.  Debug assemblies are bigger and a little slower; they should not significantly change the behavior of the application if they accidentally ship, and Debug.Assert will throw up giant ugly error mesages to users if it’s used incorrectly.  In reality there’s no good reason to ever use them, so don’t.

Since the typical user doesn’t understand any of this and doesn’t have the faintest idea how software is built, it’s easy for them to laugh at error messages and think programmers must be stupid.  But if you’re a good programmer, you will raise exceptions even when the impossible has occurred, and when the impossible has occurred, what are you supposed to say?  What would be a good error message in that case?  Even if you could describe what impossible sitaution has occurred, what possible good would it do, since there’s nothing the user or the app dev could have done to avoid it?

/rant.  Have a nice day.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: