Let's assume for a moment that the "goodness" of a design is inversely proportional to the cost of change. In other words, if a team makes a change to some software and that change is easy to make, the design is "good." If the change is hard to make, the design is "bad." (Let's also assume that the software works, is scalable, performant, etc.: we're talking about internal quality here, not external quality.)
An interesting side-effect of this definition is that the quality of a design is context sensitive. It depends on the feature you're being asked to implement and the team that's doing the implementation. A design that's good for an expert team isn't necessarily good for a novice team, and a design that's good for one kind of change isn't necessarily good for another.
Pretty straightforward stuff, perhaps even obvious. Now let's talk about what this means for design.
Most people, when they think of design, think of predictive design.
In predictive design, we...
- anticipate future needs,
- predict how the design will change,
- and invent a design that can accomodate those changes easily.
Because predictive design focuses on predicting the future, it values experience and forward thinking. To become better at predictive design, people study design patterns, which at their best are the condensed wisdom and experience of dozens of programmers. When utilizing predictive design, designers will think about what is likely to change and create abstract classes, interfaces, and plug-in points to allow programmers to easily add classes to support specific kinds of new features.
The Other Side
All I've done so far is sum up what most people already know. There's another side to design, though, one that experienced designers use every day. We just don't talk about it much. What is it? Reflective design.
In reflective design, we...
- analyze existing code,
- identify design flaws,
- and fix them using refactoring.
Reflective design focuses on analysis and modification of existing code, so it values code clarity and refactoring. To become better at reflective design, people study code smells, which at their best are concise heuristics for recognizing design flaws. When utilizing reflective design, designers will create simple code that has no unused infrastructure and eliminates duplication, allowing programmers to easily modify existing classes to support arbitrary change.
Two Sides, One Coin
Neither one of these approaches is necessarily better than the other. Both predictive and reflective design are equally valid approaches to design. Good designers use the same underlying heuristics to judge "good" design regardless of the approach they're using.
These two approaches are not only compatible, they can be used concurrently. A project can use predictive design at the high level while simultaneously using reflective design at the low level. According to Lean Software Development, Raymonde Guindon studied design in 1990 ("Designing the Design Process") and found that experienced designers constantly reviewed and modified their design as they designed an elevator control system. This matches my experience with programmers as well. I would argue that programmers have always used reflective design.
A Taxonomy of Design
If reflective design is so prevalent, why invent a new term? Thanks to Extreme Programming, we've had the concept of "evolutionary design" for years now. Why call it "reflective design" instead?
The problem with "evolutionary design" is that it's just one approach to reflective design. It's often presented as an alternative to the derided "Big Design Up-Front" (BDUF). This is a false dichotomy. As a result, I often see people dismiss the idea of evolutionary design out of hand. I'm a big fan of evolutionary design, but even I thought the idea was silly when I first heard it. (You can read more about my change of heart in my experience report, "Up-Front Design Versus Evolutionary Design in Denali's Persistence Layer," XP Universe, 2001.)
As I've tried to express XP's approach to design, I've learned that the discussion isn't as black and white as people often make it out to be. There are combinations of design approaches we can choose. In addition to reflective or predictive design, we also have the design granularity and how often we design:
- Design granularity
- High: Focus on the most important classes and packages in the entire application
- Medium: Focus on a set of related classes
- Low: Focus on a single class
- Design frequency
- Up-Front: Complete all design efforts before starting coding
- Incremental: Perform design at specific intervals before returning to coding
- Continuous: Constantly perform design simultaneously with coding efforts
- Design approach
- None: No design at all
- Both predictive and reflective
The choice of predictive design versus reflective design isn't as black and white as the "BDUF vs. Evolutionary Design" debate would have you think. There are nine combinations of design granularity and design frequency. For each of these boxes, we can choose to utilize predictive design, reflective design, both, or neither. For any project, you could make a "design map" showing the project's approach to design. For example, a project aggressively using evolutionary design might look like this:
|Across namespaces||None||Reflective, some Predictive||Reflective (minimal)|
|Across classes||None||Reflective (minimal)||Reflective|
|Within a class||None||None||Reflective, some Predictive|
"Evolutionary Design" Design Map
We can do the same for "big design up front."
|Within a class||Predictive||None||Reflective|
"Big Design Up Front" Design Map
Even for these black-and-white opposites, we see aspects of both predictive and reflective design.
Up until now, the conversation has been about up-front design versus evolutionary design. I'd like to eliminate that false dichotomy. Let's put predictive design and reflective design on the table together. They're both valid design techniques that we can choose between at will. In fact, you're almost certainly using both today. Now let's talk about when and how.
I don't have comment support in my blog, but I welcome email on this and any other subject at email@example.com. If you don't want me to post your comments to a future blog entry, please let me know in your email.