[ Home | Resume | Programming | Engineering Philosophy | Family ]

A Minimal Methodology for C++

This document describes the methodology that I like to use for C++ programming. I like it because it includes all of the necessary steps, but otherwise it's streamlined, simple to understand (you'll know how to do it by the time you're done reading this, although you might spend a lifetime trying to master it) and requires no proprietary tools. It's also certifiably devoid of any original ideas whatsoever.

In real life, people tend to skip steps. Skipping unimportant steps is fine; I'm all for that. But skipping essential steps (and all the steps in this methodology are alleged to be essential for a project of any significant size) usually results in those same steps being revisited later in the project at 10 to 100 times the cost.1 There's really no excuse for that, no matter what your business situation.

Requirements Gathering

The first step is to find out what the requirements are. Part of this step is to negotiate the requirements such that the greatest possible value is achieved with the least possible effort. The "customer" with whom you negotiate the requirements may or may not be your own company's marketing department, but either way it helps to have contact with end users.

Requirements can be documented in just about any format you like, but they really do need to be documented. Even if you have a perfect memory, you'll need documentation in order to prove to others that the requirements have changed, and to avoid having the project's schedule based on a smaller set of requirements than what the customer expects.

We're not too worried about the requirements changing yet, although that's guaranteed to happen. Everything in this methodology is implicitly iterative (to account for feedback) and concurrent (to account for changes), although it does have a definite order in the sense that requirements flow through it as do drops of water through a river.

Analysis

Once we have the requirements, it's important to then understand them completely. That's what analysis is all about.

We need to go further than envisioning a system that satisfies the requirements, making sure that the requirements are not self-contradictory, and so on. We also need to understand why the requirements are there in terms of the value that the customer derives from them. This is how you verify that the requirements describe what the customer actually wants. It's also how you anticipate changes.

The products of analysis are fuzzy, generally consisting of what's in the head of the analyst. During analysis, I find it useful to keep a running list of notes regarding things like un-obvious consequences of the requirements, similarities and differences among requirements, and which requirements might be likely to change, crossing out the incorrect points as I rule them out.

There's no law against starting design before analysis is complete, so long as you finish analyzing the part of the system under design first. In fact, sometimes that's exactly the right thing to do. But consider that the earlier you start designing, the more likely you'll need to rework the design to account for things that you'll discover later in the analysis.

Design

Now that you understand what you need to build, the next step is to come up with a plan for building it. Before you can do that, you need to have a decent design vocabulary. To learn object-oriented design, I recommend the "Design Patterns" book.2

However, C++ is not just an object-oriented language. In particular, it's also a generic language (read: templates). There aren't too many good books about generic programming in C++, but I recommend using the following rule: If you see inheritance in a design pattern, and you can bind the subclass type at compile time, then say to yourself, "That subclass is a template argument." Try it. It works. Really.

Given that you know how to design, you then create a text file with headings for all of the classes that you know you're going to need, with a 1 or 2 paragraph explanation of what that class is supposed to do, how it participates in design patterns with other classes, and so forth. You may list a few members, but don't think you have to enumerate them all. You don't need a separate heading for each member class, because it wouldn't be a member class unless its role in relation to the parent class were obvious, right?

That's about it.

Don't do pictures. They're prettier and easier to understand, but they're also hard as hell to maintain. Save the pictures for your white board, and document them only if you find yourself having to draw the same ones over and over. Also, you need to be able to merge changes, just as you do with source code, so you'll want to stick with plain text, or maybe HTML.

Your design document is, in a word, wrong. Don't worry about that. You can't possibly foresee every single implementation issue that's going to come up and force the design to change, nor should you try. Just use your knowledge of C++ to do the best you can. The point is for your colleagues to be able to pick up the design document and get the gist. (Even if you're working alone, sometimes you need a design document to give yourself the gist.)

If the requirements are too huge for a single person to do the class design, then I recommend first breaking the system down into a hierarchy of subsystems. You should follow a similar approach for that, wherein each subsystem is described in a page or two. A project of this magnitude is a lot harder to manage, so you should be wary of taking it on unless you know what you're doing.

Implementation

I have a special name for detailed design documentation. I call it "source code."

As you code each class, the class description should move from the design document into the class header and thereafter be updated and maintained there. Leave a note such as "[see class header]" behind in the design document. That way, when the design is refined in parallel with coding (which always happens), you need to modify the design document only for the classes that haven't been implemented yet.

Prefer to implement classes from the bottom up, such that you can most easily write the test program for each class as you develop it. Follow the coding standards , integrate early, track defects, own changes instead of files, and eat your vegetables too.

Changes

There's nothing in this methodology that prevents late requirements changes from being incorporated, schedule permitting. However, there are a couple of points to keep in mind:

First, if most of the proposed changes would result in a modification to the overall design (other than possibly adding a single new subclass) or in code changes spread across a lot of different places, then you probably did a crappy job of analysis (anticipating changes) and/or design (preparing for changes). Learn from your mistakes and do better, or step aside and let someone else do it.

Second, no matter how well you analyze and design, late changes are always more expensive than early changes, everything else being equal. The first thing you need to do when a late change comes in (whether or not you think the change is a good idea) is to ask the customer if he's willing to pay even the cost of estimating the cost of implementation. The next thing you need to do is to make sure that all the implementers have signed up for the cost estimate, or else it's your fault if the estimate is wrong. If the customer is signed up for the cost as well, then by all means incorporate the change.


1 Steve McConnell, Rapid Development
2 Gamma, et al., Design Patterns

Anders Johnson, last modified $Date: 2003/01/28 $

[ Home | Resume | Programming | Engineering Philosophy | Family ]