Byte Jar - Software Lessons Learned the Hard Way
A public service catalog of solutions to annoying software development problems, or
a sporadically updated rant chamber hosted by the code grunts of a tiny software company. Thanks for tuning in.
Ad-hoc software design isn't necessary or helpful; four well-defined stages will give you all the details you need to implement your software with generous backing documentation.
When you begin a software application, you'll probably start out on a white board (or wikiment), sketching out ideas. As the idea progresses, you'll meet with various players (other developers, financial backers, sales people, etc.) to flush out the need. These meetings will be very high level, talking about problems, concepts, and goals. This is the "idea generation" stage, and it's a very nebulous, informal stage.
Eventually, you'll reach a point where all the stakeholders seem to agree on what you're to be writing, and you'll find that you've taken some fairly comprehensive lists of what the software will do.
Sometimes, your work's done for you: a client will approach you with an idea, which they'll ask you to execute in software. The idea may be flushed out with lists of "requirements", but don't be fooled by these: they are not requirements in the sense you'll want them to be. Treat them like a Christmas list for a child: they're wishes and desires. You'll use them to jump-start your own documentation.
With the idea in hand, as well as a list of what the software should do, you can begin more formal design development. There are only three levels of design you need to create for any software application:
These all address different audiences and have different reasons for being. They all express information about what the software will do, but they do so from different "angles" of the solution. So what's the difference you ask?
Well, a requirements document states the problem needing to be solved, an overview of the solution, a list of bullet point items about what will be done and what won't be done, and additional information describing the "goals" of the software. The requirements document acts as a "contract", clearly stating to all stakeholders what a software application will and will not do. Let me be very clear and repeat that: a requirements document says what a software application will and will not do. Ignore the negative side at your own peril.
A functional document takes the requirements and turns them into a description of what the user sees and how the software behaves to the user. The functional specification includes an overview of the application (think 50,000 feet) that briefly captures the intent of the application and its organization. The functional specification also includes scenarios and activities, describing stereotypical users who'll use the application and how they'll interact with the software.
The functional specification also describes, in mind-numbing detail, every aspect of the application. Include descriptions of how the software acts in nominal cases as well as all the bizarre exception cases (what exactly happens when the Internet connection dies half-way through the upload?). Include the exact text to be used on screens and in messages. You don't have to have the final UI organization, but you should have the content nailed down. Every detail you write must reinforce one of the requirements given in the requirements document. Don't write in a function that is not directly supported by a requirement (or non-requirement).
The technical specification documents how the functional specification will be implemented in software (and hardware). Algorithms, data structures, architectures, processors, languages, coding standards, etc. will all be discussed in exceptional detail. The technical specification should specifically address each functional component of the application, as well as make general sweeping statements to cover your posterior. For example, your technical specification might say that assertions will be used at function entry and exit to check in and out parameters, respectively. You might then go on to say that for a particular functional error checking case, you will use assertions and error returns to detect and respond to the error.
If you were building a house, you might see these documents manifesting themselves like this:
Before you even think about changing code, go back to the requirements document. Insert your new functionality into the requirements design first, then think about how the new requirement impacts the functional design, and then finally how the functional change impacts the technical design.
Think of it like pushing a glass off a table: entropy is in your favor moving from requirements (glass on the table), to functional (glass at edge), to technical (glass falling), to code (shattered on the floor). Going the other way -- shattered glass to reassembled glass -- is very hard, and you'll pay for trying that arrogance eventually.
You might (in fact, will probably) generate other documents from these, including:
These documents will betray their heritage, to some degree, possibly all the way back to the requirements document. In other words, you'll see vestiges of the requirements even in the user manuals. This is a good thing -- these documents should all complement each other, amplifying and augmenting their predecessors.