Skip to content

Chapter 11: Design it Twice

Your first idea for a module or system is unlikely to be the best one. For each major design decision, sketch several radically different alternatives, list their pros and cons, and then pick one, combine them, or use their flaws to find a better design.

  • Design it twice: “consider multiple options for each major design decision.”
    • When to use: choosing a module’s interface, then again for its implementation, and at higher levels such as user-interface features or the decomposition of a system into major modules.
    • How:
      1. Sketch a few alternatives. Make them radically different, because you learn more. Pin down only the most important methods, not every feature.
      2. Even if you are sure only one approach is reasonable, sketch a second one anyway, “no matter how bad you think it will be.”
      3. List the pros and cons of each.
      4. Choose one, or combine features of several into a new design that beats them all.
      5. If none is attractive, use the problems you found to drive new alternatives.
  • Criteria for comparing interfaces:
    • The most important: “ease of use for higher level software.”
    • Is one interface simpler than another?
    • Is one more general-purpose?
    • Does one allow a more efficient implementation?
  • Criteria for comparing implementations: “simplicity and performance.”
  • Radically different alternatives: options that differ in their basic approach, not in details.
  • Gap buffer: one of the implementation options the author lists for the text class, along with a linked list of lines and fixed-size blocks of characters.
  • Cost of designing twice: for a class, often no more than an hour or two, compared with days or weeks of implementation. For larger modules, both the exploration and the payoff are larger.
  • “It isn’t that you aren’t smart; it’s that the problems are really hard!” Smart people learn in school that their first quick idea is good enough, and that habit stops working on hard problems. “no-one is good enough to get it right with their first try” for large software systems.
  • Think of design-it-twice as training. Comparing alternatives teaches you which factors make designs better or worse, so you rule out bad designs faster over time.
  • Treat extra work in the caller as a red flag in every alternative. If all your options make higher-level code do extra manipulation, the module isn’t doing its job.
  • Implementing the first idea that comes to mind: it makes even very smart people underperform, and makes them “frustrating to work with.”
  • Comparing near-identical options: small variations teach little.
  • Over-specifying every alternative: a sketch of the key methods is enough to compare.

Designing the interface of the editor’s text class.

  1. Three alternatives.
    • Line-oriented: insert, modify, and delete whole lines.
    • Character-oriented: insert and delete single characters.
    • String-oriented: operate on arbitrary ranges of characters, which may cross line boundaries.
  2. Pros and cons. All three interfaces are fairly simple. The line-oriented one makes higher-level code split and join lines for partial-line and multi-line operations such as cutting and pasting the selection. The character-oriented one makes higher-level code loop over characters, and it is likely to be much slower because each character is a separate call.
  3. Spot the shared problem. If you had considered only the line and character designs, you’d notice that both force higher-level code to do extra text manipulation. That’s a red flag: “if there’s going to be a text class, it should handle all of the text manipulation.”
  4. Derive a better design. The interface should match the operations higher-level software actually performs, which are neither single lines nor single characters. That reasoning leads to a range-oriented API.
  5. Design the implementation twice too. Compare a linked list of lines, fixed-size blocks of characters, and a gap buffer, judged on simplicity and performance.
  1. For every major decision, sketch at least two very different designs before choosing.
  2. Judge interfaces mainly by how easy they make life for the code that uses them.
  3. Use the weaknesses of each alternative to invent a better one.
  4. Repeat the process for the implementation, with simplicity and performance as the goals.
  5. Budget an hour or two for a class; the time is small next to implementation.
  • Ch 3: designing twice is a proactive strategic investment.
  • Ch 6: generality is one of the comparison criteria.
  • Ch 7–8: the range-oriented text interface pulls line handling down into the class.
  • Ch 15: writing interface comments first makes alternatives easier to compare.