Design
Seth Nickell posted a fantastic piece (part 1,
part 2)
on why designers have a hard time doing good work for
GNOME. Read both parts. I'll wait.
One thing that Seth says is that sometimes designers
come up with a design, and then programmers dissect
every pixel of it asking for an explanation. Why did
you put this here? Why is that a 3-pixel border instead
of 4? This is extremely unnerving to designers, as when
you are skilled at something, you don't consciously
think about all the decisions you make while
doing something. Their skill told them that that
place is where the thing would look more balanced,
or give you the most economy of mouse movement. Their
skill told them that 3 pixels looked just right, while 4
pixels would look too fat. I mean, just look
— can't you see the difference? (Programmer: no,
I can't; that's why I asked you, and 4 is an easier
number for me to deal with).
Programmers also tend to burden designers with many
questions because it is them, the programmers, who will
actually have to go through implementing everything that
the designer said. Also, it is very hard to restrain
yourself from posting your pet peeves when someone is
doing a design — I know, I've
done it myself.
For designers, having to justify every decision keeps
them from doing good work (or from doing anything at
all). Just see what happens with the OpenOffice.org
usability train-werck — nobody wants to change the
user interface because they would have to go through a
bureaucratic process of justifying every single thing
they did, write a spec, get it approved, etc.
However, Seth's tongue-in-cheek recommendation of "just
trust designers" is not 100% wise. See what happens out
there in the physical world when we trust starchitects
like Daniel Libeskind
and I. M. Pei.
They will just jerk off on your city and they will spooge a
monstrosity which no one dares to demolish because it was
so expensive to build.
Fortunately, human
interface designers are not starchitects. They are
very reasonable people, trying to build tools that are
rewarding to use.
Programmers get annoyed at designers who don't show a
hint of knowing how software is implemented. This is
like builders who get annoyed at starchitects —
yes, mister Gehry,
your fucking roof leaks because you designed a
surface with a local minimum and no drain. Water
will pool in local minima. 2000-year-old fix
which you didn't do just to be interesting: cascade
of roofs.
A designer who is not involved in the implementation
cannot be a good designer, and I will explain why.
Designers should not deliver gold-framed drawings drawn
with the finest inks on parchment, expecting that the
lowlife builders will follow the designs to the letter.
Designers need to be involved with the construction.
That will teach them what is necessary and what is
superfluous, and it will make them better designers.
Seth mentions that he and Marco worked very well
together when Marco questioned him, because there
was an interaction between designer and builder.
You may not be the best programmer or the best designer,
but if you work to acquire programming and
design skills that are above average, then you have
already won.
Design of something as complex as software is best done
iteratively. You use the scientific method: design a
bit, code a bit, test the outcome. How do you avoid
testing mostly screw-ups until you hit the right design?
You follow a few rules.
Which rules? Allow me to introduce you to the
monumental work of Christopher Alexander.
Introduction: A Pattern Language, or architecture for humans
In the 1970s, Christopher Alexander
and other architects at Berkeley went to various places
around the world, trying to see if there were reasons
for why some places and buildings are comfortable,
livable, and nice, and some are not. They
distilled their findings into a list of good
architectural patterns, and published three books
— the first two being
The Timeless Way of Building
and A Pattern Language.
A Pattern Language is remarkable for several reasons.
First, it is immensely practical. If you are building
something, you can grab the book and instantly get
hundreds of ideas for your project. Second, it is not
about a particular style or about superfluous
decoration. The book doesn't tell you, "make this shape
of flourishes in the handrails"; instead it tells you,
"a house should have its rooms placed such that sunlight
enters them according to the right time of the day -
East for the bedrooms in the morning, West for the
living room in the afternoon". Third, you can see a
whole philosophy of good living emerge from the book.
It advocates a process that allows you to construct an
environment that respects your needs as an individual,
as a social human being, and as a living being that
should not damage its environment.
For us programmers, this book is also interesting
because it was the inspiration for Gamma et al's Design
Patterns, and a whole slew of pattern-minded
thinking that we are used to.
Let's look at five little examples of patterns from the book.
Positive
outdoor space. You can feel it in the
small, cozy public squares in medieval towns. You can
most definitely not feel it in the huge parking
lagoons in shopping malls along the highway. Positive
space is more or less convex, it is partially enclosed,
and it has a definite shape. It is not just leftover
space. Here, negative and positive space between
buildings (note how old versions of the GIMP were a pain
in the ass to use because the program's windows looked like the version
on the left):
Light
on two sides of every room. This diffuses
the light, thus making a more comfortable environment.
There is no glare from the window. There is no time of
the day when you don't get good sunlight. Here, light
on two sides, and light on only one side:
Intimacy
gradient. The entrance to your home is
semi-public space. The living room is communal space.
Your bedroom is private. If you must cross the bedroom
to reach the bathroom from the living room, then
having visitors will be uncomfortable. So, you design
your home so that the connections between rooms form a
sequence from public to private.
Structure
follows social spaces. Communal areas need
to be ample and have high ceilings, and they need to be
"in the center of things". Intimate areas like reading
alcoves and your study desk need low ceilings and
separation from the rest (through doors or half-height
walls) to make them private.
Open
shelves. Deep cupboards make you put things
behind other things, so you can't see them nor reach
them. They also have a big footprint. Cupboards that
are one-item-deep automatically stay tidy, and you
always know where everything is. Things that you use
frequently should not be behind doors.
So you can see the essence of design patterns: good,
tested recipes that don't constrain your implementation
in unnecessary ways.
But where did the patterns come from?
Although the list of patterns is tremendously useful,
and universally applicable to urbanism and construction,
Alexander was not satisfied. Where did the patterns
come from? Can we make new patterns from scratch, or
must be we content with what traditional architecture
has produced so far? Are patterns necessary at all?
Alexander spent the next twenty years researching these
questions. He arrived at the following conclusions:
-
Nature creates things that all have about 15
properties in common (I'll show you later). This
happens solely through natural processes, although it is
not quite clear why very different processes produce
similar results.
-
Traditional architectures, or towns which just
evolved over time, also have those
properties. You can derive all the patterns in
A Pattern Language from those
properties.
-
Each property can also describe a
transformation to the existing space.
-
The only way to achieve good design is by using
those transformations, one at a time.
This was published in 2003-2004 in four volumes titled
The Nature of Order.
The fifteen properties
The first book in The Nature of Order deals
with fifteen properties that appear in all natural systems.
You should really look
at photos and drawings of the fifteen properties to
understand them. I'll just summarize them super-briefly here.
Levels of scale. There is a
balanced range of sizes. You don't have abrupt
changes in the sizes of adjacent things.
|
Strong centers. You can clearly
identify parts of the space or structure.
|
Thick boundaries. Lines delimit
things. In living systems, edges are the most
productive environments (e.g. all the critters
that live at the edge of the water).
|
Alternating repetition.
High/low, thick/thin, shape A and
shape B. Things oscillate.
|
Positive space. Space is
beautifully shaped, convex, enclosed. It is not
leftover space.
|
Good shape. The sails of a ship,
the shell of a snail, the beak of a bird. They
attain the optimal shape, which is beautiful.
|
Local symmetries. The world is
not symmetrical at large. But small things tend
to be symmetrical, because it is easier that way.
Your house is not symmetrical, but each window is.
|
Deep interlock and ambiguity.
The crooked streets of old towns. Axons in
neurons. Two strong centers are made stronger if
a third center is placed between them, so that it
belongs to both.
|
Contrast. You can distinguish
where one thing ends and the next one begins,
because they don't fade into each other.
|
Gradients. Things fade into each
other where they need to. Concentrations in
solutions, snow or earth banks, the wires that
support a bridge.
|
Roughness. The world is not
frictionless and smooth. Irregularities are good.
|
Echoes. Things repeat and echo
each other. Things are unique in their exact
shape, but those shapes repeat over and over.
|
The void. Sometimes you get a
big blank area for quietness of form. A lake, a
courtyard, a picture window.
|
Simplicity and inner calm.
Things are as simple as possible, but no simpler.
|
Non-separateness. Everything
depends on everything else. You can't separate a
fish from the pond and the aquatic plants. You
can't separate a column from the base of the
building.
|
Structure-preserving transformations
The second book in The Nature of Order
describes how each of those properties also defines a
transformation. For example:
Thick boundaries. You can sometimes
transform something beneficially by adding a
boundary to it. You plant a hedge around a garden,
which then serves as beauty, as a wind-break, and as a
productive system on its own. Scrollable boxes without
a GTK_SHADOW_IN frame are hard to distinguish from the
window's background. You put a cornice at the top of a
building, so you don't get an abrupt transition between
the building and the sky.
Local symmetries. Small parts of built
things are easier to build symmetrically — because
they are turned on a lathe, because they need access
from both sides, because they fold like a book. Making
things asymmetrical "just to be interesting" takes extra
work and it is harder to make them work well.
Positive space. Feeling too exposed
when in your desk? Add a waist-high bookshelf beside
you to delimit your space, but not to completely close
you off.
Each of these is a
structure-preserving transformation. You
make a change in the existing structure not by tearing
it down and remaking it, but by tweaking one thing at a
time according to those properties and transformations.
The fundamental process
Over a long argument, Alexander explains why following
this process of applying structure-preserving
transformations is the only way to achieve a
good, functional design. This is not just for
buildings, but for everything we construct. We mimic
nature, but we do it faster.
So, how do you do one step in your design/implementation
process?
1. Start with what you have — an empty lot, or an
already-built building, or a program that looks ugly and
is hard to use.
2. Identify the centers that exist in that space. Find
the weakest center or the least coherent.
3. See how to apply one or more of the fifteen
structure-preserving transformations to strengthen that
weak center. Does it need to be delimited? Does it
need to be blended with its surroundings? Does it need
more detail? Does it need to be de-cluttered?
4. Find the new centers that are born when you apply the
transformation to the old center. Does the new
combination make things stronger? Prettier? More functional?
5. Ensure that you did the simplest possible thing.
6. Go back to the beginning for the next step.
A super-short summary could be, "find the bad parts;
make them better in the simplest way possible; repeat
until it is all good".
What the hell does this have to do with software design?
Let me give you some examples.
Refactoring
is just applying structure-preserving transformations.
You don't change what the program does; you just change
how it is built internally, piece by piece.
You
do not rewrite software; you fix the software that
exists. Otherwise you lose all that knowledge, even if
it looks ugly in its curent state.
You don't design the whole user interface for a big
program in a single step. You go from big to small or
small to big (levels of scale); you
test each part individually until it is good
(strong centers); you make sure the
parts are not too disconnected from each other
(non-separateness). You move a few
widgets where they are easier to reach, or where they
are closer to the data to which they refer. You remove
some frames and separators to reduce clutter.
Free software and cities
We software developers are building cities. I include
code hackers, designers, translators, documenters,
etc. in the term "developers" — building cities of
software programs interconnected with each other through
IPC. Each program is a building, large or small, simple
or complex, each with a different purpose and layout.
Designing a whole system (kernel, libraries, tools,
shell, programs) would be like trying to design a whole city.
A
good city cannot be designed; it has to emerge from
the interactions between its inhabitants, over many
years. Good cities are emergent, not designed.
Ecosystems emerge from the interactions of living and
non-living beings; they are not designed and they always
evolve.
Remember that no one can work on all of free
software. You cannot design a whole system by yourself, and you
cannot make everyone agree on everything for a perfectly
consistent design. But if you follow the
structure-preserving transformations and the fundamental
process, you can build your part of a big system and it
will automatically be harmonious with everything else
— and you will have fixed the bad parts first,
making the overall goodness much better.
So, go forth and design and build and iterate.