Sunday, August 14, 2005
Why I Hate Debug.Assert

I've recently been involved in a project focused on developing teaching materials for High School Computer Science courses. As I've thought more and more about my experiences as a student and professor in high school and college CS, I've noticed some clear patterns in my evolution as a developer and those I've worked with over the years. Reading Alfred Thompson's recent blog entries on teaching students to comment (here and here) got me thinking specifically about how commenting and other non-algorithmic techniques have really improved the quality of my development experience over the years.

While teachers can try to pound away at why students should follow certain coding practices, it seems as though everyone needs to have their own personal epiphany before they can truly appreciate why certain mechanisms exist in CS. I think the reason that most students shrug off many of these development best practices is that they are primarily useful during debugging, which is where the vast majority of professional developers spend the vast majority of their time. Students, however, can often get away with completing their assignments on a single printed page, so debugging barely even enters the equation.

Thinking about how I've changed over the years, I can identify three specific ways (in evolutionary order) I've grown:

  1. Commenting. Sure, everyone says you need to comment, but it's not until you have to read someone else's code (or try to read your own from years ago) that this really starts to hit home. Code without comments is almost a sure F on any homework I've ever graded. Sure, the algorithm might be correct, but that doesn't mean you've done a good job engineering the software. Commenting is a key tool for communicating what the code does. Trying to debug someone else's code without having useful comments really sucks.
  2. Naming conventions. Some people argue in favor of Hungarian (fewer and fewer these days) and some against (the rest). The one thing that everyone agrees on is that you have to be consistent, whichever direction you take. Naming conventions need to go beyond variables and touch on methods, events, delegates, classes, etc. While I don't agree that a good naming convention can remove the need for commenting, I do understand why many people preach it. Using useful names like "numberOfIterations" explains what a variable is for as well as any comment could. The official naming guidelines from MS are a very useful tool here.
  3. The Debug class. I must admit that it took me much longer to warm up to the Debug class than I would have ever expected. I never really saw it used in many samples or solutions, so I just figured it was only offered to keep a small segment of the development population happy. Boy was I wrong. Debug.Fail and Debug.WriteLine are two of the most useful debug tools out there. While I love breakpoints and the call stack and so on, the Debug class is my best friend when it comes down to algorithmic debugging. For those who haven't touched the Debug class, Debug.WriteLine will send a string to any debug listeners (by default just the Output window in VS), Debug.Fail will stop execution to display a message, after which you can either ignore the message or press "Retry" to debug.

So how does this all of this relate to my title, "Why I Hate Debug.Assert"?

As I've mentioned, the Debug class is awesome because it enables you to do debugging stuff at runtime (such as logging and breaking into debug mode). When you compile and build in "release" mode, all of the debugging stuff gets stripped out, leaving you with more optimized code. This makes it very easy for you to develop and test in debug mode, and then not have to do anything besides change the build configuration when deploying (in other words, you don't need to comment out or delete the Debug class calls).

This is where the problem occurs. When I see code like this:

public void MyMethod(MyClass myClass)
{
    Debug.Assert(myClass != null, "myClass is null in MyMethod");
    myClass.DoSomething();
}

I end up screaming "You were SO close!!!".

In this scenario, a null might get passed in as myClass and the developer will hit the assert, which will pop the Assert dialog that they can use to stop execution and discover why myClass was null when MyMethod was called. They'll fix the calling code and run it again. Everything will work, they'll build it for Release, and toss it over the wall. Unfortunately, this code isn't robust because it's still possible that null could be passed in (unless you have absolute faith in every caller, which you shouldn't).

Alternatively, the better solution is:

public void MyMethod(MyClass myClass)
{
    if (myClass == null)
    {
        Debug.Fail("myClass is null in MyMethod");
        // either return here or throw and exception, but don't let execution continue in release mode!
    }
    myClass.DoSomething();
}

Now we get all of the benefits of the Debug class, but maintain a level of robustness for Release code. Sure, if this comes up you'll still need to debug, but at least you'll get better detail than a NullReferenceException. It's also possible you could even recover from this somewhere up the stack.

I can't really think of any place Debug.Assert is better than Debug.WriteLine (if just logging) or Debug.Fail (as described above). Then again, I could be wrong!


8/14/2005 10:28:34 AM (Pacific Standard Time, UTC-08:00)  #    Comments [5]  
Tracked by:
"health insurance leads" (health insurance leads) [Trackback]
"prepaid cards" (prepaid cards) [Trackback]
"memphis hotels" (memphis hotels) [Trackback]
"meridia buy" (meridia buy) [Trackback]
"play free slot machines" (play free slot machines) [Trackback]
"mortgage lead generation" (mortgage lead generation) [Trackback]
"buy tamiflu" (buy tamiflu) [Trackback]
"lesbian sex" (Jackson_Blog) [Trackback]
"anal sex" (anal sex) [Trackback]
"porn" (porn) [Trackback]
"wet pussy" (wet pussy) [Trackback]
"incest stories" (incest stories) [Trackback]
"lesbians" (lesbians) [Trackback]
"shemale sex" (shemale sex) [Trackback]
creampie video [Trackback]


8/17/2005 1:45:17 AM (Pacific Standard Time, UTC-08:00)
What you would really like is a type of ConditionalAttribute that would allow you to make a method callable if the preprocessing identifier is NOT present (or you must always specify something like /d:RELEASE on the command line for those kinds of builds).

Then you could create a pair of methods that either asserted if you were in debug mode (and allowed you to enter the debugger) or simply threw an exception (if in release mode, perhaps after writing the call stack to a log).
8/17/2005 2:24:35 AM (Pacific Standard Time, UTC-08:00)
Debug.Assert isn't a general purpose conditional check. It is for *additional* checks that should never be necessary but that you want to make absolutely sure during development to highlight any problems elsewhere in your code.

In your last example you test for null, do a debug check and then say return or throw an error.

Well, guess what. The following line, myClass.DoSomething(); will already throw an exception if myClass is null....

[)amien
8/17/2005 8:17:31 AM (Pacific Standard Time, UTC-08:00)
Roland: Yes, preconditions would be great to have, but I think the misuse of Debug.Assert applies in many other scenarios, such as when a return value from another method is expected to be within a certain range.

Damien: Letting a NullReferenceException rise up is poor practice unless you're absolutely sure where it came from and that you can't recover. For example, if this method were on a background thread we might be able to accept that if a null were passed in we could ignore the call, otherwise we're better off raising an exception that's meaningful further up the call stack. Just letting it fall through could likely result in big problems, especially security ones if the scenario were tweaked slightly.
5/4/2006 12:28:52 PM (Pacific Standard Time, UTC-08:00)
Ed, Debug.Assert isn't intended to do be used for public argument checks. You should use exceptions to guard your public APIs. It's that simple. Bad example bro.
steve
5/26/2006 1:24:20 AM (Pacific Standard Time, UTC-08:00)
Sorry Steve, but this is definitely not a bad example because it exists in so much code. Obviously, code that only lives in Debug mode isn't intended to be used in production-level code, but people still do it because they're taught to do it in school. My point, which is still valid, is that a lot of developers don't get that the Debug class gets ignored in a Release compile, resulting in code that seems to work fine on their machine but blows up in the real world. If you follow the pattern I describe, you'll avoid having this happen and still get the benefits of the Debug class when debugging. If all you do is throw exceptions, you might miss bugs that get swallowed by a global try {} block (which is another bad things developers do to make scary exception dialogs disappear).
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):