There are many well-known modeling and design techniques that can be used to bring about a "good design". An incremental process may limit the applicability of these techniques, which are most powerful when applied and committed to "up front". Test everything; eliminate duplication; express all ideas; minimize entities:These few simple rules, applied locally, can help a high quality global design to emerge.

There are many design steps in XP: Release Planning contains elements of design, as does Iteration Planning. In addition, we also recommend the Quick Design Session. (See Extreme Programming Installed for more on this.) Moreover, XP practitioners in fact design “all the time”, using the practice of Refactoring. (See Martin Fowler’s excellent book, Refactoring, for comprehensive coverage of this topic.) Our purpose here is to discuss how some simple rules, applied locally, can produce a design which is globally of high quality.

My thesis rests on two assertions, which I’ll offer without much justification. A program will be recognized as having “good design” insofar as it exhibits these characteristics:

  1. The system is highly modular, consisting of separate components (typically objects) which are highly cohesive and loosely coupled.
  2. The modular components are given expressive names, which enable us to grasp quickly what the modules mean, and why they are there.

This view may be somewhat controversial: I am in fact asserting that any highly modular expressive implementation will be perceived as reflecting good design. At some later time, perhaps I’ll try to justify this view. For now, please grant the notion, at least tentatively, and come along. The rest of the article may be even more controversial: I’m suggesting that a few very simple rules can bring about good design. I’m suggesting that good design is an emergent property, given the application of these simple rules.

Rules of Simple Design

Some time ago, Kent Beck offered the following “rules” for simple design. In priority order, the code must:

  1. Run all the tests
  2. Contain no duplicate code
  3. Express all the ideas the author wants to express
  4. Minimize classes and methods

These rules are frankly subjective, at every level. We need to have the right tests to be sure that the code still works; we need to define for ourselves what duplication is; we need to be conscientious about what ideas need to be expressed and how to express them; maybe we even need to know when classes and methods are minimized, whatever that means.

OK, they’re subjective. Let’s embrace them, and think about what the rules mean to us and what they’ll do to the code as we apply them.

  • Running all the tests makes refactoring safe. But thinking about having all the tests gives us their other benefit: they help make the code clear; they help to communicate the design.
  • Removing duplication tends to create new, and smaller, classes and methods. We find a little patch of code that appears again and again. It has some meaning: we extract it, giving life to whatever idea that code patch implements
  • Expressing ideas gives meaningful names to the classes and methods we have and to those that are created by the duplication rule. As well, we are often moved to create new classes and methods just for improved expression. For example, it is common to replace a switch statement with a few classes with a polymorphic method. When we see the essential idea that underlies the switch, the classes, and the name of the method, tend to pop right out.
  • The fourth and final rule, minimizing the number of classes and methods, helps us make sure that we don't proliferate unnecessary entities.

Applying these rules, we wind up with compact, modular code, expressing all the important ideas of the system. We wind up with “good design”.

An Alternative Formulation, due to Alan Shalloway

In a posting on the Agile Modeling mailing list, Alan Shalloway of Net Objectives, proposed some alternative rules:

  1. Runs all the tests
  2. Follows once and only once rule (which I believe means "contains no duplicate code")
  3. Has high cohesion (clarity)
  4. Has loose coupling

Alan comments:

"If you look at design patterns, you can see that most of them follow these rules. High cohesion and loose coupling can often be achieved by decomposing your problem domain with the distinctions of commonality / variability analysis. That is, find how things are similar and then find out how they are different. "The other thing I like about this set of rules is that they are easy to teach and understand. Cohesion has to do with clarity. Method cohesion means a method is focused on one purpose. Class cohesion means all of the methods in a class are focused on one larger purpose."

Since, as I asserted at the beginning of this piece, I believe that high cohesion and low coupling are the essence of design quality, I have every confidence that applying Alan’s rules will surely also result in good design.

My suggestion is that both these rule sets, and the underlying premise that modularity leads to good design, are worthy of reflection. Whether we use one rule set or the other, I’m confident that we’ll find that these simple rules do cover a wide range of what we ourselves mean by “good design”.