“Quality is designed in, not added on.”

I’ve been using that quote as my e-mail sig for about a year now. Actually, I think I made that one up myself. It started out as something more like “Quality is something you design in from the start, not tack on at the end.” But I like to keep my sigs short and sweet when I can.

Think about it. First, make a cheap piece of crap. Then QA it. Run it through tests, tighten loose screws, replace the broken buttons. When you’re done you have a cheap piece of crap that worked just before you put it into the box.

QA certainly didn’t add any quality, no matter how heroic their efforts. All they did was make sure that one item worked once. As soon as it shows on the doorstep of a real person all bets are off. If it’s a product you’re trying to support, your support costs are going to go through the roof.

Software is no different from any other product. If you toss it together without thought or planning, what you end up with may work today, but tomorrow somebody is going to enter “32q” into a numeric field, or slide the mechanism just a little too quickly, or try to open a jpeg file instead of ASCII text, and suddenly your program will crash and take the customer’s next month’s payroll with it.

Of course, with enough effort you can bash it into good enough shape that the bugs will be minor annoyances, and people will work around them. Then next week somebody will ask for a new feature and you’ll say “Sure!” or somebody will report a bug you can’t ignore. Now you have to go back into the code and improve it. When you fix one thing, do three others break? Do you have Heisenbugs that inexplicably vanish one day and show up the next? Does the very idea of working on your code make your hands go all clammy and make you think of long vacations in Tahiti? What you have there, my friend, is code that is both fragile and unmaintainable.

Maintenance and support are probably going to be your biggest expenses. The only way you’re going to keep them tolerable is by designing quality in from the start. Make the product more robust and the customers won’t be calling as often, for as long. Fewer RMAs. Make the product more maintainable and it will cost less to add those important features and fix those important bugs.

Here are a few ways to help assure quality in your software.

Proper Documentation

Document how it’s supposed to look to the customer, how it’s supposed to work, and what it does when things go wrong. Everybody signs off on it. If anybody wants a change it has to go into the document first, and the schedule gets adjusted accordingly. Too much rush, too many direction changes, too much pressure will be reflected in the quality of the code.

Proper scheduling

Scheduling is one of the worst problems in software. Part of it is pressure; bosses like optimistic estimates and will likely apply pressure to that end. Technically-oriented bosses may be more enlightened, but they may not — a mediocre programmer, perhaps one who came from a culture of hack-and-slash programming, may reject estimates that he “knows” are too long because he knows how long it would take him to write it. He’s actually ignoring the time it takes to get it working, and not thinking at all about maintainability.

My favorite method is: Break it down into tasks. Pick a minimum time for a task: I usually use one day. Estimate the time for each task; double it for debugging and other chores. If it’s less than your minimum, make it your minimum. Don’t be afraid to add some padding to any task you’re unsure about.

It works surprisingly well. What happens is most of those one-hour tasks actually only take an hour or two, and suddenly I’m three days ahead. Then I run smack dab into a problem on one of the two-day tasks that takes three days to sort out. In the end I find that I usually break even, plus or minus a little.

You don’t have to use my method. Use what works for you, but I emphasize, what works for you. If a method keeps coming up short (seldom will you regularly over-estimate!) then either adjust the method or try another one. Try mine if you like, or go online and find the other ten thousand three hundred ways of doing it. People write books about it.
Some of this folds back into what I said earlier.  Any time there’s a change in the design it needs to be added to the estimate. Don’t be tempted to write small changes off; every one will cost you, and other people will be tempted to ask for a lot of them. Small changes have a habit of becoming big pains in the neck, so consider carefully what the side-effects will be.
Keep in practice
“But it’s only a quick utility for my own use. It’s not worth spending a lot of time on.” Quite often that’s true; it may not be worth spending a lot of time polishing a one-time-use utility or something only used occasionally or as part of the actual development. But watch out: if people find it useful it may get out into the wild. It’s happened to me. If you spend a bit of time on that quality, it’ll be something to be proud of instead of something you’re constantly apologizing for.
And it will help keep you in the habit of building in quality. The great thing about a habit is that you don’t have to think about doing; you just do it.
Break down; simplify; separate
I’m an assembly language programmer, mainly. I also work in C and C++.  (I know other languages and can learn others, but those are what I happen to work in at the moment.) I’m not a great C++ guru by any means. I have mixed feelings about many of its features.
But I love C++ for some things. Encapsulation. Data hiding. Accessors. Abstraction. These are things either directly part of the language or strongly supported by it. These are things that you can do in C, in a sense, in a crippled sort of way. (My other favorite feature of C++ over C is the more rigid typing.)
Assembly doesn’t give you that sort of structure. But you can impose much of it yourself, and I do. I isolate functionality into single modules (one source file, usually with one associated include file). Is it a display function? Create a module called “display” and move all the functionality for the display into that module. All the variables should be local if possible. If you need external access, create accessor functions. Compromise only where performance is a serious issue, and make “gentleman’s agreement” rules to limit access. Macros are good for that, too.
In my current job I inherited a piece of code that I eventually turned into our current crop of products. During the process I noticed that the USB code had some flakiness that was hard to define and not obvious in a quick code review. One day I decided it was time to track it down. What was the first thing I did? Not the emulator. Not a formal code review. The first thing I did was pull in all the related variables that were defined in other modules, made public variables local and write accessor functions, pull inline code from other modules and turn it into accessor functions, and generally encapsulated the code. I compiled it, swept away a few bugs, and tried it.
The flakiness was gone. Poof, like magic. I never followed up on the original symptoms and in nearly a year it has never shown up again. This same code is being used in every single one of our (new USB version) major products. All I did was make absolutely sure everything that happened to the USB code and its variables was under strict control of that module, and the robustness of that module jumped by several orders of magnitude. The other effect of doing that was, of course, that it made the module more maintainable — everything pertaining to USB is in that module. If I have to make a change I don’t need to poke around all over the place trying to find the bits and pieces; it’s all there.
And I didn’t even use classes.

Tags:

Leave a Reply

You must be logged in to post a comment.