It is really fun to stumble upon old classics like Reeves’ “What is Software Design?” and think about them in a modern context.
This post begins a series of responses to Reeves’ article. I will examine his basic assertions here, and comment on how they relate to agile modeling, test-driven development (TDD), and agile in general in future posts.
The article revolves around the idea that if we want, as an industry, to draw parallels between software engineering and hardware/mechanical/structural engineering, we have a bunch of the parallels wrong:
- The building or manufacturing activity in creating software really is “the build” as we know it in development terms – compilation and linking. This activity is basically free and almost instantaneous for software (it certainly is when comparing it to something like building a bridge, or sending a product through a manufacturing assembly line). When a developer is finished writing code in a higher-level language (Java, C#, VB, C++) the product is not yet manufactured, because the product cannot be used as-is. It is only manufactured when it can run and perform tasks on the hardware natively, which would *have* to be post-compilation. We’ve written in higher-level languages for so long, and we so rarely write machine code (or even assembler) anymore, so the statement seems crazy – but it is correct for an engineering analogy.
- The primary output of any true engineering/design activity is documentation – documentation to be sent to the manufacturers that is complete and specific enough to allow for manufacturing product (this seems to ring true for building architecture too, if we want to start drawing analogies to software architecture). Therefore, in software engineering the design is in the source code itself, because that is the next step back from the manufacturing (compilation), and it is the only artifact complete enough to allow for manufacturing. This has many implications:
- Software designs are relatively easy to complete, especially when comparing against mechanical designs of similar complexity. Therefore, it’s been easier to produce very complex software designs, and we usually do – to the extent that managing complexity is now the primary activity of software engineering.
- The prototyping and simulations seen in the mechanical engineering world are largely unnecessary in software – when you can build the real thing for free in very little time, why bother with a prototype? But the validations and the adjustments of a mechanical design live on in the software world – as testing and debugging. Therefore, testing and debugging are first-class citizens in software design/engineering (cue up TDD zealotry).
- Everything that we currently understand as software development is design or engineering: coding, debugging, testing, and the stuff we already call “design” too. This means software is cheap to build, but it is very expensive to engineer.
- High-level designs – the stuff we already call “design” – are not complete, but only structural frameworks for detailed designs. Translation between the high-level design and the detailed design can have problems, as most high-level design languages (UML) have no direct translation to detailed design languages (Java or C#). If the detailed design influences the high-level design, so be it – as they are both design activities. This is essentially testing and debugging of the high-level design by way of the low-level design (code). A corollary of this idea is: don’t be afraid to start coding earlier, well before high-level designs are complete. A good, realistic process will embrace coding as a design process; it will not try to stick its head in the sand when faced with reality.
Allowing the detailed design to influence the high-level design, if necessary, is akin to something I’ve said all along. Guys like Ken Schwaber (or anyone with even a small amount of experience in software development) agree with me: a design or architecture is meaningless until vetted in some form of an implementation.
Reeves wrote the article in 1992, and it shows its age in places. When he casts pre-agile iterative/incremental advantages (“risk abatement” and “shortened product delivery times”) as buzzword trafficking and “excuses to start coding earlier in the life cycle,” I think he sells those advantages and his own ideas short. Why would a good approach need excuses?
Other ideas and statements are amazingly prescient. As one example, I found his comments on documenting late, so as to have more accurate documentation, to line up exactly with some things that Ambler says about agile-style documentation.
Jack W. Reeves:
…all programmers know that writing the software design documents after the code instead of before, produces much more accurate documents. The reason is now obvious. Only the final design, as reflected in code, is…refined during the build/test cycle. The probability of the initial design being unchanged during this cycle is inversely related to the number of modules and number of programmers on a project. It rapidly becomes indistinguishable from zero.
Write deliverable documentation as late as possible, avoiding speculative ideas that are likely to change in favor of stable information.
Some ideas and ways I’ll be analyzing this article in the future include:
- How do these statements relate to agile and agile techniques – specifically TDD and emergent architecture?
- These sorts of articles and statements will rally Keyboard Cowboys, who have always twisted any methodology they could get their hands on to justify their behavior. How does Reeves’ proposal differ from that?
- Does code generation from models and reverse engineering of code into models solve the “high-level design languages (UML) have no direct translation to detailed design languages (C#, Java)” problem? Does it ease the ways we allow the low-level design (code) influence the high-level design?
- Does our industry represent “engineering” in any form, anyway? Are we merely trafficking in buzzwords – design, engineering, architecture, scaffolding – to make ourselves feel better? Would we be better off avoiding these sorts of metaphors?
One thought on “if (Code_as_Design != Keyboard_Cowboys) return Agile_Modeling + TDD; else return Same_Stuff_Different_Day;”