There's been something of a brouhaha amongst the Microsoft technical bloggers in the last few days about this post by Eric Brechner, a director of engineering learning in the Engineering Excellence group at Microsoft1. The short version is that Eric asserts (hah—puns abound) that asserts in code aren't good ways to solve problems. For those not in the know, an assert will crash a program whenever some specified condition isn't met.
I think Eric's point is that we need to be intelligent about error conditions, and handle them gracefully wherever possible. I agree entirely with this. But, the commenters take him to task for some supporting statements he makes about asserts. The commenters make some valid points, but I think they miss the purpose of the post: don't be dumb about errors.
But since this is the internet, I'm going to contribute to the discussion by expressing my opinions about asserts, because the larger issue of handling errors is well-discussed in the post that started this all. The first two points below are mentioned in the comments on Eric's post.
- Asserts should never be used when an error condition is understood. If you're calling an API that could fail, asserting is a terrible way to handle failures. For example, this code bites:
HRESULT hr = SomeApiThatCouldFail();
ASSERT(SUCCEEDED(hr));
hr = SomeOtherApi();
- Asserts before crashes are useless. This code helps nobody, unless you've got a system with accessible pages at 0, in which case you've got bigger problems than dealing with asserts.
int *ptr = (int*)malloc(sizeof(int));
What purpose does the assert have if a failed allocation will ultimately result in dereferencing NULL? You'll crash the executable either way.
ASSERT(NULL != ptr);
*int = 1;
- Asserts should never be reachable by abusing an API. If you require someone to call object->MethodA() before calling object->MethodB(), MethodB shouldn't assert if MethodA wasn't called first. It turns out this isn't always achievable, since API design sometimes doesn't anticipate the functionality inside, so you can end up in cases where you really do need to give up at some point.
- Asserts shouldn't be used to validate parameters. Clever readers will note this is essentially identical to the previous point. I reiterate this because I see it all the time.
- Don't assume anyone else will run debug binaries. Asserts are disabled in practically all shipped binaries, and don't assume anyone will ever use the debug (or chk) varieties unless you specifically ask them to do so. If you're an API developer and have added asserts to your code to help developers understand when they're doing dumb stuff, odds are good nobody will know about it unless you make it super-clear that the asserts exist. Those of you building APIs will understand how difficult it is to make anything super-clear about what you're doing, let alone when to use debug bins.
- Asserts are not security mechanisms. Again, asserts don't ship in most binaries. If you really need to crash an executable when something goes wacky and can't handle the situation gracefully or exit the process cleanly, force a debug break.
So why do asserts exist? I can think of one good reason (also outlined in the comments in Eric's post):
- Use asserts as a way to identify cases you would never hit by design. Here's an example: let's say you've got a queue, and a bunch of threads that remove items from the queue and don't signal completion until the queue is empty. An assert in the completion code would probably be OK:
WaitForThreadsToComplete();
ASSERT(0 == m_queue.Length);
This would allow you, the developer, to catch cases where the queue wasn't properly emptied even though it's designed to be. Note that this isn't a replacement for proper cleanup code, since it's possible you'll hit this condition in production and leaking items in the queue probably isn't acceptable even if they end up there accidentally.
Congratulations for making it to the end of this post.
1 Corrected Eric's title.

Leave a comment