SB has a good , short and to the point, where he points out that the problem with BDUF(“Big Design Up Front”) is with the ‘Big’ aspect, not the ‘Up Front’ aspect.
For instance:
I’m sure every developer with even the slightest amount of experience has run into a situation where a proposed design for a domain concept is initiated, and then someone (perhaps even the developer in question) has decided something along the lines of: “We need to generalize this so that it covers future requirements.”
If this doesn’t cause you some concern, it should. The problem with trying to design against future requirements is that, except in rare instances, you don’t really know what those requirements are. So, you sort of guess at what they might be, and come up with something that sort of, maybe, handles these vague future requirements. And, except in rare instances, you will get this wrong.
In a recent example that I dealt with, an internal logging system was being designed to handle some automatically scheduled batch processes, when they ran, who triggered them, what the result was, what exceptions occurred, etc. etc. etc. Now, given that there are many stock/standard logging systems (from Microsoft’s Enterprise Library to many open-source tools), this should already raise an alarm. But, there are occasionally times when you do need to roll your own, so let’s leave that aside.
The batch processes in question had to do with file transfers, an area with a fairly fixed and standard set of requirements. However, the designers decided that the log system should be designed to handle future batch processes, like SQL jobs. I asked one of the designers, “Have you ever heard of YAGNI (You Aren’t Going to Need It)?” He then asked, “What’s YAGNI?” I replied, “You aren’t going to need it.” He answered, “Oh, yes of course.” And then continued to talk about the need to make the design handle future batch processes, like SQL jobs. And so the design began to exhibit generic and nearly incomprehensible aspects. I don’t remember all the specifics, but it wasn’t enough to have a concept like FileSource, no, you needed something like PrecursorCondition (I made that up, but it was something equally bad, IIRC).
All of which made it that much harder to understand and implement the current requirements that actually mattered.
Now, it is always fun to make fun of others, but, to be fair, another instance:
When designing an eCom Order Management system a few years back, we (myself and a business partner) knew from our previous dot com experience that there were many different order status types, often depending on many inventory status types, and that you needed to manage the flow pretty carefully. We knew this. We were domain experts.
Now, the actual system we were building didn’t need that sophistication at the time. But, we knew we might need it later. So, we designed our happy little designer selves away for quite a long period of time, building in a whole bunch of functionality into the design, functionality which that system never needed, and which prevented us from actually implementing the functionality we did need for quite a long period of time.
As Scott points out, the obvious remedy is to do no design, but this is just reactionary, and causes its own set of problems.
There is no magic formula to determine how much design is needed. The correct answer is “Some, but not too much.” Are you generalizing so much that it is hard to determine exactly what the current design is doing for the specific functionality being designed for? Back it down. Think of a potential next requirement, one that is actually reasonable. Will you need to re-write every single class module involved to handle that requirement? Maybe you need to give the design a little bit more thought.
As always, use your discretion wisely.