Friday, July 02, 2004

The Microsoft Code Follies: "Writing Solid Code"

Writing Solid Code: Microsoft's Techniques for Developing Bug-Free C ProgramsI've mentioned the Steve Maguire book entitled "Writing Solid Code" before in this space. In the fast-moving world of software, it's now ancient. It was published in 1993 by Microsoft Press and it outlines "Microsoft's Techniques for Developing Bug-Free C Programs". Don't laugh, that's on the cover.

Pete and I had a copy at Alpha. As I read through it, back in the day, I remember getting more and more upset. In my mind, it was a recipe for disaster. I'll explain why in a moment.

In retrospect, I believe that history has borne out my original thinking regarding the approaches espoused by Microsoft. And I can't help but think that many of Microsoft's oft-publicized security issues are due in no small part to the kind of thinking engendered by this book. So if you're wondering why Internet Explorer exposes your PC to a new exploit on a regular basis, or why Outlook takes 30 seconds to shut down from time to time, my contention is that we should look at the techniques that Microsoft's developers were taught.

And, apparently, Dave Moore (former director of Development at Microsoft) endorsed these approaches, claiming that these techniques let his teams produce "zero defect" code. No, seriously, I'm not joking.

Let's review, shall we? And, just for the record, these examples are taken directly from the book, so no one can accuse me of putting words in the author's mouth.

Flawed Thinking Part 1: Assertions

The first major flaw in the book is the reliance on assertions rather than run-time validation. If you're unfamiliar with an assertion, it's basically a macro that might look like this:

#ifdef DEBUG
void _Assert(char *, unsigned); /* prototype */
#define ASSERT(f)
if (f)
_Assert(__FILE__, __LINE__)
#define ASSERT(f) NULL

and your code, using the Microsoft approach, is supported to assert that various arguments are valid, that certain conditions are present, and so forth. For example, inside the body of a memcpy, we might assert that the source and destination arguments are non-NULL:

ASSERT(pvTo != NULL && pvFrom != NULL);

So why is this approach so bad... no, horrible... no insanely ridiculous? I'll give you four reasons:

1) What happens to our error-checking when we build a release version? Bingo! Our "error-checking" goes away. So unless we've tested every possible pathway through the code, we're running naked while in release mode... with zero error-checking!

2) What happens to the signature and footprint of our code when we build a release version? Bada bing! We get a completely different signature and footprint, depending upon release type. Code comes and goes based upon our build. And, yes, this is a maintenance nightmare... no, disaster... no, horrible, catastrophic f**kup.

3) What happens to cleanup of resources when things don't work as intended? Splort! That's called "leakage" and if you've ever wondered why many Microsoft products seem to get bulkier and slower and thrashier the longer you run them, I think we've just discovered why.

4) And what happens if we're working on a thick-client GUI and trying to diagnose painting problems? Spack! You get caught in a lovely continuum of assertions while your painting logic kicks in... and the assertion dialog box keeps defeating the paint, causing repaints, causing assertions... aeiieeeeeiieee!

But in a nutshell, the most egregious issue is the first item: the code will work when conditions are reasonable, but it will fail horribly when things go bad. And, like a house of cards, when one piece fails, the entire structure will come crashing down, killing the little card people inside, squashing their tiny bodies, their bloody entrails hanging out, their tiny card mothers crying out for them... perhaps I've gone too far with this whole "house of cards" analogy...

Stay tuned for Part Two...

1 comment:

Unknown said...

I recall having this debate with you when we read that book. I liked asserts and still do. I believe fast fail assert like mechanisms have their place. I do agree that the debug/no debug nature of the MS default assert was wrong headed. You were correct on that point. The behavior contract of the method should always be validated.
Not to change subjects, but an assert mechanism is a perfect place for an exception to be thrown. ;-)