Some people are adamant that designing like this is fraught with danger or just doesn't work. However, there are many, many projects where it has worked better than anyone expected and certainly better than the Waterfall approach of Big Design Up Front (BDUF). For example, see Continuous Design by Jim Shore.
The opposition is simply due to not really understanding how design is done in Agile development. In particular, using JIT design without using other aspects of Agile methodologies such as regular feedback, collaboration, continuous integration, collective code ownership, etc, will doom it to failure. And as usual I will mention that Unit Tests are crucial (see Unit Tests).
How it works as part of an Agile methodology is hard to understand so I will use an analogy. Hopefully, this allows you to understand my point.
An Analogy
I will use the analogy of designing a house. I chose this analogy as you probably already have an idea on how that's done.
Houses are built from a plan. Often an architect has spent many weeks or months creating a design on paper. Even the worst houses are built from some sort of floor plan. (In fact, in my country, government regulations preclude building without a detailed plan that has been approved by the local council.) The point is that for any house, a lot of time is spent in design and planning before construction can even begin.
Over many years (even millenia) this has been refined to be the best way to build a house. In fact this is essentially the same way that all complex physical devices and structures are built - some sort of designer, architect, or engineer (or team thereof) creates a blueprint which is carefully perused, verified, and approved before the final product is actually built.
If you just went and bought some materials and started building a house do you think it would be anything other than a disaster? Of course not!
So why do I think that designing and building software should be any different? The simple fact is that you can't build a house or software in the conventional manner without a fairly detailed design. However, if you change the way you build the software things are different. This is why Agile software development is considered to be a methodology - it only works when you understand and embrace it, in toto.
Let's see how we might build a house in the Agile manner. Note that, in the discussion below, D stands for developer (the house creator) and C stands for client (the house user).
From this ... |
D: What is your most basic requirement for the house?
C: I want something that stops me getting wet when it rains.
D: Try this. [A piece of corrugated iron on four posts.]
C: I keep bumping my head on it.
D: How's this? [The corner posts have been extended.]
C: Better. What about something to stop the wind.
D: Try this. [Four walls attached to the posts.]
C: It's less breezy but I can't get out.
D: I could add a door. ...
C: I want something that stops me getting wet when it rains.
D: Try this. [A piece of corrugated iron on four posts.]
C: I keep bumping my head on it.
D: How's this? [The corner posts have been extended.]
C: Better. What about something to stop the wind.
D: Try this. [Four walls attached to the posts.]
C: It's less breezy but I can't get out.
D: I could add a door. ...
to this! |
This continues until C has their dream house (or they can't afford to pay for any more changes).
Software is different
I hope you realize that I am not advocating this way of building houses! This would be a particularly slow and expensive way to do so. However, when building software this can be a better way (and I would argue almost always is). Building software is different for these reasons:
1. Modifying physical objects (like extending the above-mentioned corner posts) can be time-consuming and expensive. Software is far more malleable. To make something larger may be a simple matter of changing a constant and recompiling.
2. Continually changing the design of anything of any complexity quickly becomes a nightmare of managing and controlling the interactions. With software this is a little easier because you do not have to worry about physical interactions (such as the location of pipes and wiring). However, it is still important to decouple different parts of the software whenever there are no major drawbacks to doing so (see DIRE). Crucially, Unit Tests can be used to ensure nothing is broken as the software is continuously redesigned and refactored.
3. House design is well-understood and there are many standards and conventions that are well-known, proven and followed. Software design is far more variable. Moreover, the client does not have a very good idea of what they want. Even if they did the developers do not have a very good idea of how to give it to them until they try to build it.
4. For practical reasons design constraints for houses do not change much if at all. For example, the government is not going to one day say that all electrical wiring in all houses is to be changed immediately. (Even if there were an urgent safety issue this would be logistically impossible.) However, these sort of major changes are regularly thrown at software developers. BDUF cannot easily cope with change the way continuous design does.
The Argument For BDUF
I have often read the blog of Joel Spolsky, whom I respect and admire greatly. However, many years ago I read a post which saddened and disappointed me. Part of Joel's blog is even quoted on the BDUF page on Wikipedia as an argument in favour of BDUF.
However, if you carefully read the full paragraph from which the quote is taken, Joel's example is actually an excellent argument for Agile Design. Here is the full paragraph:
"Many times, thinking things out in advance saved us serious development headaches later on. When I wrote the first draft of this spec, I had a more complicated flowchart that assumed that either party (helper or victim) could initiate the process. This became extremely complicated and convoluted for various reasons. As I worked through the screens that would be needed to allow either party to initiate the process, I realized that Aardvark would be just as useful, and radically simpler, if the helper was required to start the whole process. Making this change in the spec took an hour or two. If we had made this change in code, it would have added weeks to the schedule. I can’t tell you how strongly I believe in Big Design Up Front, which the proponents of Extreme Programming consider anathema. I have consistently saved time and made better products by using BDUF and I’m proud to use it, no matter what the XP fanatics claim. They’re just wrong on this point and I can’t be any clearer than that."
- Joel Spolsky, 2006
The first thing that hit me is the mention of the first draft of the spec with the "complicated flowchart". It is obvious that the first draft is already suffering from the problems commonly associated with BDUF. In particular, it is clearly making unnecessary assumptions.
Flowcharts
Does anyone use flowcharts anymore? Back in 1979 I was told by my lecturer to avoid flow charts (probably due to their association with the evil goto). Personally, I have found UML activity diagrams (and sequence diagrams) to be more useful.
But using Agile Design obviates the need for lots of diagrams.
Now imagine if the designer(s) of the software had less time, less experience or less insight than Joel. They may have gone with the first draft of the spec with the consequent later weeks of recoding that this entailed. This is plausible as I doubt many designers are as experienced and insightful as Joel. So rather than demonstrating why BDUF is better than Agile, Joel's example clearly shows its weakness.
Now consider the Agile approach, which is to first create a minimal working version of the software. In this initial version it might not have even been obvious who (helper or victim) could initiate the process. Users would try the software and provide feedback which is used to evolve it into what they need. In this way the conundrum of who should initiate the process would probably not even have arisen.
Another thing to note is how Joel simulated the use of the software in his own mind by "working through the screens". Not many people can do this effectively. It's always far more effective to have working software than try to imagine how the software would work. (Note that this is another advantage of Agile element 2: Favour working software over comprehensive documentation.)
Finally there is one thing I need to be absolutely clear on: thinking things out in advance is not the only way to avoid serious headaches later on. And most of the time it's not even the best way.
Problems of BDUF
The plain fact is that for more than 50 years BDUF has been tried and it consistently fails. Even for simple, well-understood projects, no matter how hard you try, it's impossible to produce anything approaching a good design without attempting to implement that design. I have seen this time and again. Trust me on this.
"What?" I hear you say. "You can't say that without an explanation." Well, I can if I want to, so there! Actually, I don't need to, since it has been discussed elsewhere. For example, see the Wikipedia page.
I have also touched on the subject many times in previous posts in this blog. For example, it is the principle cause of the Gas Factory Anti-pattern. More recently, I have talked about it in the series of posts on Unit Tests and specifically in the Cost of Rework section in JIT.
But just because you asked so nicely, here is a summary of recognized problems with creating a detailed specification up-front:
- the customer often does not really know what they want, at least till they try it
- it does not adapt to changing requirements
- it tries to anticipate future changes, adding unnecessary complexity
- it tries to document every known "fact" (of which many are usually incorrect)
- it makes unnecessary assumptions about how the design will be implemented
- it is very hard to foresee problems
- many things (often major things) are overlooked
- it takes too long to create - stakeholders become worried when there is little sign of progress
- it separates design from coding, even though they are very closely related (see DIRE)
- it results in a contorted implementation - it's often easier to change the code than fix the spec
- the document is not updated when the inevitable changes are made
However, now that I think about it there is one major drawback that I have not seen discussed. This is that developers don't really come to a full understanding of what the software is supposed to do until they actually try to implement it. It's only then that logical inconsistencies, omissions and other mistakes in the design become obvious. Further, limitations (unknown to the designer and often even to the developers) only become apparent when you attempt to write the code.
Possibly most significantly, only when the developers implement the design do better ways to do things occur to them. I have lost count of the times with BDUF when I have finally completed the code, only to then think of a much better solution (simpler, more efficient, more usable, more maintainable, etc).
Agile Design
The Agile approach is only to design what is needed for the next feature or iteration. This is usually the smallest possible addition to the system. The designer should only concentrate on the current iteration and never consider any possible future changes. This is a lot harder than it sounds. (I do concede that occasionally there are exceptions to this rule which I will discuss later.)
Since past iterations did not look to the future, a particular new feature may completely invalidate a large part of the existing design. Don't panic! This is normal. Traditionally such frequent refactoring would create chaos, since every iteration would have the distinct possibility of breaking existing functionality. Without complete regression testing after each iteration bugs would creep in and not be found till some time later
This is why Agile Design requires use of Unit Tests. Unit Tests allow continual redesign and refactoring to be performed without fear of breaking existing functionality.
The advantage of the Agile approach is that the closely related activities of design and coding are done consecutively and, to some extent, simultaneously. As I discussed previously in DIRE separating the design and coding leads to the major problems of Waterfall development.
The one huge benefit that I have found is that, after developing earlier features, the developers are very familiar with how the software works. This greatly improves the chances of a better design being developed for the next iteration. Mistakes and omissions are far less likely and a good (simple, DRY, etc) solution is much more likely.
B2DB Syndrome
Every programmer experiences the B2DB (Back To Drawing Board) syndrome when they are first learning to program. This is where one tries to write some code but in doing so obtains a better understanding of how to do it. The consequence is one has to go back to the drawing board, often many times. (I discussed this in more detail in JIT Testing).
For many this is a very painful experience and they believe there must be a better way, especially (as is often the case) they have already been trained in another field (such as a branch of engineering) where it is essential to do up-front design.
I believe this is the real reason for much opposition to Agile Design - it feels like the bad old B2DB syndrome.
It's true that there is a lot of refactoring going on in Agile Design, but this is actually an advantage. Of course, more experienced developers are going to have a better initial design, but even the best will see better ways to do things once they fully understand what they are trying to do. Also (as all the Agile experts emphasize) it makes the process much more amenable to changing requirements.
Finally, at the risk of sounding like a broken record, I will mention that Unit Tests make the refactoring safer and easier. Rather than having the feel of dread of B2DB, you can actually learn to enjoy refactoring and the process of creating the best design you can find.
Summary
- only make the simplest possible enhancement that will work
- forget the large design document with lots of UML diagrams
- use the code (especially Unit Tests) as the "documentation"
- don't design for the future, concentrate on what is needed now
- don't make the code flexible, just write for the specific task at hand
- try to forget all the extra (irrelevant) information that has been given to you
- don't be afraid to refactor existing code
- use Unit Tests to allow changes to be made without fear of breaking anything