Some people might argue that design isn't really a construction activity, but on
small projects, many activities are thought of as construction, often including design.
On some larger projects, a formal architecture might address only the system-level
issues and much design work might intentionally be left for construction. On other large
projects, the design might be intended to be detailed enough for coding to be fairly
mechanical, but design is rarely that complete—the programmer usually designs part
of the program, officially or otherwise.

On small, informal projects, a lot of design is done while the programmer sits at the
keyboard. "Design" might be just writing a class interface in pseudocode before writing
the details. It might be drawing diagrams of a few class relationships before coding
them. It might be asking another programmer which design pattern seems like a better
choice. Regardless of how it's done, small projects benefit from careful design just as
larger projects do, and recognizing design as an explicit activity maximizes the benefit
you will receive from it.

Cross-Reference

For details on the different levels of formality required on large and small projects, see Chapter 27.

Design is a huge topic, so only a few aspects of it are considered in this chapter. A
large part of good class or routine design is determined by the system architecture, so
be sure that the architecture prerequisite discussed in
Architecture Prerequisite has been satisfied. Even more design work
is done at the level of individual classes and routines, described in Chapter 6, and Chapter 7.

If you're already familiar with software design topics, you might want to just hit
the highlights in the sections about design challenges in Design Challenges and key heuristics in Design Building Blocks: Heuristics.

5.1 Design Challenges

The phrase "software design" means the conception, invention, or contrivance of a
scheme for turning a specification for computer software into operational software.
Design is the activity that links requirements to coding and debugging. A good
top-level design provides a structure that can safely contain multiple lower-level
designs. Good design is useful on small projects and indispensable on large
projects.

Cross-Reference

The difference between heuristic and deterministic processes is described in
Chapter 2.

Design is also marked by numerous challenges, which are outlined in this
section.

Design Is a Wicked Problem

Horst Rittel and Melvin Webber defined a "wicked" problem as one that could be
clearly defined only by solving it, or by solving part of it (1973). This paradox
implies, essentially, that you have to "solve" the problem once in order to
clearly define it and then solve it again to create a solution that works. This
process has been motherhood and apple pie in software development for decades
(Peters and Tripp 1976).

The picture of the software designer deriving his design in a rational,
error-free way from a statement of requirements is quite unrealistic. No system
has ever been developed in that way, and probably none ever will. Even the
small program developments shown in textbooks and papers are unreal. They have
been revised and polished until the author has shown us what he wishes he had
done, not what actually did happen.

—David Parnas Paul Clements

In my part of the world, a dramatic example of such a wicked problem was the
design of the original Tacoma Narrows bridge. At the time the bridge was built,
the main consideration in designing a bridge was that it be strong enough to
support its planned load. In the case of the Tacoma Narrows bridge, wind created
an unexpected, side-to-side harmonic ripple. One blustery day in 1940, the ripple
grew uncontrollably until the bridge collapsed, as shown in Figure 5-1.

Figure 5-1. The Tacoma Narrows bridge—an example of a wicked problem

This is a good example of a wicked problem because, until the bridge collapsed,
its engineers didn't know that aerodynamics needed to be considered to such an
extent. Only by building the bridge (solving the problem) could they learn about
the additional consideration in the problem that allowed them to build another
bridge that still stands.

One of the main differences between programs you
develop in school and those you develop as a professional is that the design
problems solved by school programs are rarely, if ever, wicked. Programming
assignments in school are devised to move you in a beeline from beginning to end.
You'd probably want to tar and feather a teacher who gave you a programming
assignment, then changed the assignment as soon as you finished the design, and
then changed it again just as you were about to turn in the completed program. But
that very process is an everyday reality in professional programming.

Design Is a Sloppy Process (Even If it Produces a Tidy Result)

The finished software design should look well organized and clean, but the
process used to develop the design isn't nearly as tidy as the end result.

Design is sloppy because you take many false steps and go down many blind
alleys—you make a lot of mistakes. Indeed, making mistakes is the point of
design—it's cheaper to make mistakes and correct designs than it would be to
make the same mistakes, recognize them after coding, and have to correct
full-blown code. Design is sloppy because a good solution is often only subtly
different from a poor one.

Further Reading

For a fuller exploration of this viewpoint, see "A Rational Design Process:
How and Why to Fake It" (Parnas and Clements 1986).

Design is also sloppy because it's hard to know when
your design is "good enough." How much detail is enough? How much design should be
done with a formal design notation, and how much should be left to be done at the
keyboard? When are you done? Since design is open-ended, the most common answer to
that question is "When you're out of time."

Cross-Reference

For a better answer to this question, see "How Much Design Is Enough?" in Design Practices later in this chapter.

Design Is About Tradeoffs and Priorities

In an ideal world, every system could run instantly, consume zero storage
space, use zero network bandwidth, never contain any errors, and cost nothing to
build. In the real world, a key part of the designer's job is to weigh competing
design characteristics and strike a balance among those characteristics. If a fast
response rate is more important than minimizing development time, a designer will
choose one design. If minimizing development time is more important, a good
designer will craft a different design.

Design Involves Restrictions

The point of design is partly to create possibilities and partly to
restrict possibilities. If people had infinite time,
resources, and space to build physical structures, you would see incredible
sprawling buildings with one room for each shoe and hundreds of rooms. This is how
software can turn out without deliberately imposed restrictions. The constraints
of limited resources for constructing buildings force simplifications of the
solution that ultimately improve the solution. The goal in software design is the
same.

Design Is Nondeterministic

If you send three people away to design the same program, they can easily
return with three vastly different designs, each of which could be perfectly
acceptable. There might be more than one way to skin a cat, but there are usually
dozens of ways to design a computer program.

Design Is a Heuristic Process

Because design is nondeterministic, design techniques tend to be
heuristics—"rules of thumb" or "things to try that sometimes
work"—rather than repeatable processes that are guaranteed to produce
predictable results. Design involves trial and error. A design tool or technique
that worked well on one job or on one aspect of a job might not work as well on
the next project. No tool is right for everything.

Design Is Emergent

A tidy way of summarizing these attributes of design is to say that design is
"emergent." Designs don't spring fully formed directly from someone's brain. They
evolve and improve through design reviews, informal discussions, experience
writing the code itself, and experience revising the code.

Virtually all systems undergo some degree of
design changes during their initial development, and then they typically change to
a greater extent as they're extended into later versions. The degree to which
change is beneficial or acceptable depends on the nature of the software being
built.

Further Reading

Software isn't the only kind of structure that changes over time. Physical
structures evolve, too—see How Buildings Learn
(Brand 1995).