To Spec or not to Spec
Like software patterns, there are many different software creative processes that tend to be re-invented over and over again. Similar to the way that some software patterns are actually different versions of each other, or more importantly, weaknesses in the implementing language, I believe some processes only exist due to weaknesses in the chosen technology. An example of one of these processes is the ‘spec’ or specification.
Specifically, I’m talking about the technical specifications described in the above link, not functional specifications. I believe technical specifications are a signal of weakness due to the following thought process – a technical spec can be deemed ‘good’ when it is unambiguous and has a relationship with actual implementation nearing 1-to-1. If either of those two facts is untrue, then that means there is some element of interpretation in the spec, which further means that two coders may implement the spec in two different ways, making different assumptions, thus leading to defects.
So let’s assume we have a good spec, one in which we have a close to 1-to-1 mapping between specifications and implementation, and a lack of ambiguity. There are two thoughts I have on this – one is, how did we get here? The other is, how is this any different from code itself. I’ll deal with the second first, just to spice things up.
A good spec really isn’t any different than good pseudocode, in fact you’ll find a lot of English descriptions of looping behavior, if statements, etc., primitives in most imperative languages. These can be turned into implementation rather mechanically, so the question results, why didn’t you just implement the spec in the first place rather than writing the entire thing out in English? What exactly does a spec get you that doing the exact same process except using actual implementation language doesn’t?
The other question is, how do you create a good spec? Well, most likely you write a working draft, then send it out for reviews. Various other designers look over it, read the spec, then send back some comments. Maybe you missed a corner case, maybe you didn’t think of a certain interaction. Over time, the spec is rewritten to be more and more specific and consistent.
Let me know if you disagree with any of the above logic so far, because now I’m going to add a twist. Let’s say, instead of writing a spec, we actually wrote implementation code from the get go, except we used the same processes described above. That is, we iteratively rewrote it, sought frequent feedback from humans or otherwise, and we stopped when there was a 1-to-1 mapping between the problem at hand and a complete lack of ambiguity (rather easier to do with a programming language).
We just re-invented rapid prototyping. You see, spec writing is what people are forced to do when they work with languages or technologies that don’t allow rapid prototyping? What exactly, when enumerated, would these technologies be? I’d venture a guess that things like a REPL and high level ‘scripting’ interface would be a good subset. Languages like Ruby or Python, or heuristically, anything that doesn’t have a long ‘compilation’ cycle. There are many compiled languages, for example, that turn around quickly allowing rapid prototyping, so I’m not JUST saying scripting languages. Languages with more boilerplate, more line noise, and a longer turn around cycle lend to writing specs over rapid prototyping (i.e., C/C++).
Rapid prototyping has many other benefits over spec writing. For instance, during spec writing, human reviewers are not only finding high level logic errors, but also syntax problems, type problems, and the like. These are all problems that a high level language will automatically find. The enumeration of ‘what’ could be passed into a specific function mentioned in the article linked to previously is a prime example of type checking that Haskell would let you know on the spot if you hadn’t fixed.
Moreover, spec writing and rapid prototyping aren’t competitng ideas. In other words, a spec COULD be a rapid prototyped code base! If we need to build an embedded system using a subset of C, for example, rapid prototyping would be a rather onerous process compared to our REPL and high level constructs. But no one said the spec couldn’t be itself written in a high level language to be ported to low level C once it was deemed complete. Therefore, rapid prototyping should be used even on projects where a spec is used, and the two should be practically substitutable.
Of course, all process patterns like these are just that, patterns, and there really should be no ‘make software using process x’ rules since x may not always apply. Sometimes you need some exploratory programming (very similar to rapid prototyping), or sometimes you may know how to express something in English but not in code (such as in technical fields). Patterns, even process patterns, ought to be seen more as a ‘bag of tricks’ similar to the skills mathematicians build up – tricks to simplify or restate problems in other domains. Too often we’re convinced that our problems are like arithmetic, and there’s just one simple algorithm or process that needs to be exercised again and again, but instead they’re more like problems found in the higher echelons of math. They require insight, tricks, squinting, hand waving, and eventually, a eureka.
No comments yet.