Basics of the Unix Philosophy
Prev Chapter 1. Philosophy Next
------------------------------------------------------------------------
Basics of the Unix Philosophy
The ?Unix philosophy? originated with Ken Thompson's early meditations
on how to design a small but capable operating system with a clean
service interface. It grew as the Unix culture learned things about how
to get maximum leverage out of Thompson's design. It absorbed lessons
from many sources along the way.
The Unix philosophy is not a formal design method. It wasn't handed down
from the high fastnesses of theoretical computer science as a way to
produce theoretically perfect software. Nor is it that perennial
executive's mirage, some way to magically extract innovative but
reliable software on too short a deadline from unmotivated, badly
managed, and underpaid programmers.
The Unix philosophy (like successful folk traditions in other
engineering disciplines) is bottom-up, not top-down. It is pragmatic and
grounded in experience. It is not to be found in official methods and
standards, but rather in the implicit half-reflexive knowledge, the
/expertise/ that the Unix culture transmits. It encourages a sense of
proportion and skepticism ? and shows both by having a sense of (often
subversive) humor.
Doug McIlroy, the inventor of Unix pipes and one of the founders of the
Unix tradition, had this to say at the time [McIlroy78
]:
(i) Make each program do one thing well. To do a new job, build
afresh rather than complicate old programs by adding new features.
(ii) Expect the output of every program to become the input to
another, as yet unknown, program. Don't clutter output with
extraneous information. Avoid stringently columnar or binary input
formats. Don't insist on interactive input.
(iii) Design and build software, even operating systems, to be tried
early, ideally within weeks. Don't hesitate to throw away the clumsy
parts and rebuild them.
(iv) Use tools in preference to unskilled help to lighten a
programming task, even if you have to detour to build the tools and
expect to throw some of them out after you've finished using them.
He later summarized it this way (quoted in /A Quarter Century of Unix/
[Salus ]):
This is the Unix philosophy: Write programs that do one thing and do
it well. Write programs to work together. Write programs to handle
text streams, because that is a universal interface.
Rob Pike, who became one of the great masters of C, offers a slightly
different angle in /Notes on C Programming/ [Pike ]:
Rule 1. You can't tell where a program is going to spend its time.
Bottlenecks occur in surprising places, so don't try to second guess
and put in a speed hack until you've proven that's where the
bottleneck is.
Rule 2. Measure. Don't tune for speed until you've measured, and
even then don't unless one part of the code overwhelms the rest.
Rule 3. Fancy algorithms are slow when n is small, and n is usually
small. Fancy algorithms have big constants. Until you know that n is
frequently going to be big, don't get fancy. (Even if n does get
big, use Rule 2 first.)
Rule 4. Fancy algorithms are buggier than simple ones, and they're
much harder to implement. Use simple algorithms as well as simple
data structures.
Rule 5. Data dominates. If you've chosen the right data structures
and organized things well, the algorithms will almost always be
self-evident. Data structures, not algorithms, are central to
programming.^[ ^9 ]
Rule 6. There is no Rule 6.
Ken Thompson, the man who designed and implemented the first Unix,
reinforced Pike's rule 4 with a gnomic maxim worthy of a Zen patriarch:
When in doubt, use brute force.
More of the Unix philosophy was implied not by what these elders said
but by what they did and the example Unix itself set. Looking at the
whole, we can abstract the following ideas:
1.
Rule of Modularity: Write simple parts connected by clean interfaces.
2.
Rule of Clarity: Clarity is better than cleverness.
3.
Rule of Composition: Design programs to be connected to other
programs.
4.
Rule of Separation: Separate policy from mechanism; separate
interfaces from engines.
5.
Rule of Simplicity: Design for simplicity; add complexity only
where you must.
6.
Rule of Parsimony: Write a big program only when it is clear by
demonstration that nothing else will do.
7.
Rule of Transparency: Design for visibility to make inspection and
debugging easier.
8.
Rule of Robustness: Robustness is the child of transparency and
simplicity.
9.
Rule of Representation: Fold knowledge into data so program logic
can be stupid and robust.
10.
Rule of Least Surprise: In interface design, always do the least
surprising thing.
11.
Rule of Silence: When a program has nothing surprising to say, it
should say nothing.
12.
Rule of Repair: When you must fail, fail noisily and as soon as
possible.
13.
Rule of Economy: Programmer time is expensive; conserve it in
preference to machine time.
14.
Rule of Generation: Avoid hand-hacking; write programs to write
programs when you can.
15.
Rule of Optimization: Prototype before polishing. Get it working
before you optimize it.
16.
Rule of Diversity: Distrust all claims for ?one true way?.
17.
Rule of Extensibility: Design for the future, because it will be
here sooner than you think.
If you're new to Unix, these principles are worth some meditation.
Software-engineering texts recommend most of them; but most other
operating systems lack the right tools and traditions to turn them into
practice, so most programmers can't apply them with any consistency.
They come to accept blunt tools, bad designs, overwork, and bloated code
as normal ? and then wonder what Unix fans are so annoyed about.
Rule of Modularity: Write simple parts connected by clean interfaces.
As Brian Kernighan once observed, ?Controlling complexity is the essence
of computer programming? [Kernighan-Plauger
]. Debugging dominates development time, and
getting a working system out the door is usually less a result of
brilliant design than it is of managing not to trip over your own feet
too many times.
Assemblers, compilers, flowcharting, procedural programming, structured
programming, ?artificial intelligence?, fourth-generation languages,
object orientation, and software-development methodologies without
number have been touted and sold as a cure for this problem. All have
failed as cures, if only because they ?succeeded? by escalating the
normal level of program complexity to the point where (once again) human
brains could barely cope. As Fred Brooks famously observed [Brooks
], there is no silver bullet.
The only way to write complex software that won't fall on its face is to
hold its global complexity down ? to build it out of simple parts
connected by well-defined interfaces, so that most problems are local
and you can have some hope of upgrading a part without breaking the whole.
Rule of Clarity: Clarity is better than cleverness.
Because maintenance is so important and so expensive, write programs as
if the most important communication they do is not to the computer that
executes them but to the human beings who will read and maintain the
source code in the future (including yourself).
In the Unix tradition, the implications of this advice go beyond just
commenting your code. Good Unix practice also embraces choosing your
algorithms and implementations for future maintainability. Buying a
small increase in performance with a large increase in the complexity
and obscurity of your technique is a bad trade ? not merely because
complex code is more likely to harbor bugs, but also because complex
code will be harder to read for future maintainers.
Code that is graceful and clear, on the other hand, is less likely to
break ? and more likely to be instantly comprehended by the next person
to have to change it. This is important, especially when that next
person might be yourself some years down the road.
Never struggle to decipher subtle code three times. Once might be a
one-shot fluke, but if you find yourself having to figure it out a
second time ? because the first was too long ago and you've forgotten
details ? it is time to comment the code so that the third time will be
relatively painless.
-- Henry Spencer
Rule of Composition: Design programs to be connected with other
programs.
It's hard to avoid programming overcomplicated monoliths if none of your
programs can talk to each other.
Unix tradition strongly encourages writing programs that read and write
simple, textual, stream-oriented, device-independent formats. Under
classic Unix, as many programs as possible are written as simple
/filters/, which take a simple text stream on input and process it into
another simple text stream on output.
Despite popular mythology, this practice is favored not because Unix
programmers hate graphical user interfaces. It's because if you don't
write programs that accept and emit simple text streams, it's much more
difficult to hook the programs together.
Text streams are to Unix tools as messages are to objects in an
object-oriented setting. The simplicity of the text-stream interface
enforces the encapsulation of the tools. More elaborate forms of
inter-process communication, such as remote procedure calls, show a
tendency to involve programs with each others' internals too much.
To make programs composable, make them independent. A program on one end
of a text stream should care as little as possible about the program on
the other end. It should be made easy to replace one end with a
completely different implementation without disturbing the other.
GUIs can be a very good thing. Complex binary data formats are sometimes
unavoidable by any reasonable means. But before writing a GUI, it's wise
to ask if the tricky interactive parts of your program can be segregated
into one piece and the workhorse algorithms into another, with a simple
command stream or application protocol connecting the two. Before
devising a tricky binary format to pass data around, it's worth
experimenting to see if you can make a simple textual format work and
accept a little parsing overhead in return for being able to hack the
data stream with general-purpose tools.
When a serialized, protocol-like interface is not natural for the
application, proper Unix design is to at least organize as many of the
application primitives as possible into a library with a well-defined
API. This opens up the possibility that the application can be called by
linkage, or that multiple interfaces can be glued on it for different tasks.
(We discuss these issues in detail in Chapter 7 .)
Rule of Separation: Separate policy from mechanism; separate
interfaces from engines.
In our discussion of what Unix gets wrong, we observed that the
designers of X made a basic decision to implement ?mechanism, not
policy??to make X a generic graphics engine and leave decisions about
user-interface style to toolkits and other levels of the system. We
justified this by pointing out that policy and mechanism tend to mutate
on different timescales, with policy changing much faster than
mechanism. Fashions in the look and feel of GUI toolkits may come and
go, but raster operations and compositing are forever.
Thus, hardwiring policy and mechanism together has two bad effects: It
makes policy rigid and harder to change in response to user
requirements, and it means that trying to change policy has a strong
tendency to destabilize the mechanisms.
On the other hand, by separating the two we make it possible to
experiment with new policy without breaking mechanisms. We also make it
much easier to write good tests for the mechanism (policy, because it
ages so quickly, often does not justify the investment).
This design rule has wide application outside the GUI context. In
general, it implies that we should look for ways to separate interfaces
from engines.
One way to effect that separation is, for example, to write your
application as a library of C service routines that are driven by an
embedded scripting language, with the application flow of control
written in the scripting language rather than C. A classic example of
this pattern is the /Emacs/ editor, which uses an embedded Lisp
interpreter to control editing primitives written in C. We discuss this
style of design in Chapter 11 .
Another way is to separate your application into cooperating front-end
and back-end processes communicating through a specialized application
protocol over sockets; we discuss this kind of design in Chapter 5
and Chapter 7 . The
front end implements policy; the back end, mechanism. The global
complexity of the pair will often be far lower than that of a
single-process monolith implementing the same functions, reducing your
vulnerability to bugs and lowering life-cycle costs.
Rule of Simplicity: Design for simplicity; add complexity only
where you must.
Many pressures tend to make programs more complicated (and therefore
more expensive and buggy). One such pressure is technical machismo.
Programmers are bright people who are (often justly) proud of their
ability to handle complexity and juggle abstractions. Often they compete
with their peers to see who can build the most intricate and beautiful
complexities. Just as often, their ability to design outstrips their
ability to implement and debug, and the result is expensive failure.
The notion of ?intricate and beautiful complexities? is almost an
oxymoron. Unix programmers vie with each other for ?simple and
beautiful? honors ? a point that's implicit in these rules, but is well
worth making overt.
-- Doug McIlroy
Even more often (at least in the commercial software world) excessive
complexity comes from project requirements that are based on the
marketing fad of the month rather than the reality of what customers
want or software can actually deliver. Many a good design has been
smothered under marketing's pile of ?checklist features? ? features
that, often, no customer will ever use. And a vicious circle operates;
the competition thinks it has to compete with chrome by adding more
chrome. Pretty soon, massive bloat is the industry standard and everyone
is using huge, buggy programs not even their developers can love.
Either way, everybody loses in the end.
The only way to avoid these traps is to encourage a software culture
that knows that small is beautiful, that actively resists bloat and
complexity: an engineering tradition that puts a high value on simple
solutions, that looks for ways to break program systems up into small
cooperating pieces, and that reflexively fights attempts to gussy up
programs with a lot of chrome (or, even worse, to design programs
/around/ the chrome).
That would be a culture a lot like Unix's.
Rule of Parsimony: Write a big program only when it is clear by
demonstration that nothing else will do.
?Big? here has the sense both of large in volume of code and of internal
complexity. Allowing programs to get large hurts maintainability.
Because people are reluctant to throw away the visible product of lots
of work, large programs invite overinvestment in approaches that are
failed or suboptimal.
(We'll examine the issue of the right size of software in more detail in
Chapter 13 .)
Rule of Transparency: Design for visibility to make inspection and
debugging easier.
Because debugging often occupies three-quarters or more of development
time, work done early to ease debugging can be a very good investment. A
particularly effective way to ease debugging is to design for
/transparency/ and /discoverability/.
A software system is /transparent/ when you can look at it and
immediately understand what it is doing and how. It is /discoverable/
when it has facilities for monitoring and display of internal state so
that your program not only functions well but can be /seen/ to function
well.
Designing for these qualities will have implications throughout a
project. At minimum, it implies that debugging options should not be
minimal afterthoughts. Rather, they should be designed in from the
beginning ? from the point of view that the program should be able to
both demonstrate its own correctness and communicate to future
developers the original developer's mental model of the problem it solves.
For a program to demonstrate its own correctness, it needs to be using
input and output formats sufficiently simple so that the proper
relationship between valid input and correct output is easy to check.
The objective of designing for transparency and discoverability should
also encourage simple interfaces that can easily be manipulated by other
programs ? in particular, test and monitoring harnesses and debugging
scripts.
Rule of Robustness: Robustness is the child of transparency and
simplicity.
Software is said to be /robust/ when it performs well under unexpected
conditions which stress the designer's assumptions, as well as under
normal conditions.
Most software is fragile and buggy because most programs are too
complicated for a human brain to understand all at once. When you can't
reason correctly about the guts of a program, you can't be sure it's
correct, and you can't fix it if it's broken.
It follows that the way to make robust programs is to make their
internals easy for human beings to reason about. There are two main ways
to do that: transparency and simplicity.
For robustness, designing in tolerance for unusual or extremely bulky
inputs is also important. Bearing in mind the Rule of Composition helps;
input generated by other programs is notorious for stress-testing
software (e.g., the original Unix C compiler reportedly needed small
upgrades to cope well with Yacc output). The forms involved often seem
useless to humans. For example, accepting empty lists/strings/etc., even
in places where a human would seldom or never supply an empty string,
avoids having to special-case such situations when generating the input
mechanically.
-- Henry Spencer
One very important tactic for being robust under odd inputs is to avoid
having special cases in your code. Bugs often lurk in the code for
handling special cases, and in the interactions among parts of the code
intended to handle different special cases.
We observed above that software is /transparent/ when you can look at it
and immediately see what is going on. It is /simple/ when what is going
on is uncomplicated enough for a human brain to reason about all the
potential cases without strain. The more your programs have both of
these qualities, the more robust they will be.
Modularity (simple parts, clean interfaces) is a way to organize
programs to make them simpler. There are other ways to fight for
simplicity. Here's another one.
Rule of Representation: Fold knowledge into data, so program logic
can be stupid and robust.
Even the simplest procedural logic is hard for humans to verify, but
quite complex data structures are fairly easy to model and reason about.
To see this, compare the expressiveness and explanatory power of a
diagram of (say) a fifty-node pointer tree with a flowchart of a
fifty-line program. Or, compare an array initializer expressing a
conversion table with an equivalent switch statement. The difference in
transparency and clarity is dramatic. See Rob Pike's Rule 5
.
Data is more tractable than program logic. It follows that where you see
a choice between complexity in data structures and complexity in code,
choose the former. More: in evolving a design, you should actively seek
ways to shift complexity from code to data.
The Unix community did not originate this insight, but a lot of Unix
code displays its influence. The C language's facility at manipulating
pointers, in particular, has encouraged the use of dynamically-modified
reference structures at all levels of coding from the kernel upward.
Simple pointer chases in such structures frequently do duties that
implementations in other languages would instead have to embody in more
elaborate procedures.
(We also cover these techniques in Chapter 9 .)
Rule of Least Surprise: In interface design, always do the least
surprising thing.
(This is also widely known as the Principle of Least Astonishment.)
The easiest programs to use are those that demand the least new learning
from the user ? or, to put it another way, the easiest programs to use
are those that most effectively connect to the user's pre-existing
knowledge.
Therefore, avoid gratuitous novelty and excessive cleverness in
interface design. If you're writing a calculator program, ?+? should
always mean addition! When designing an interface, model it on the
interfaces of functionally similar or analogous programs with which your
users are likely to be familiar.
Pay attention to your expected audience. They may be end users, they may
be other programmers, or they may be system administrators. What is
least surprising can differ among these groups.
Pay attention to tradition. The Unix world has rather well-developed
conventions about things like the format of configuration and
run-control files, command-line switches, and the like. These traditions
exist for a good reason: to tame the learning curve. Learn and use them.
(We'll cover many of these traditions in Chapter 5
and Chapter 10 .)
The flip side of the Rule of Least Surprise is to avoid making things
superficially similar but really a little bit different. This is
extremely treacherous because the seeming familiarity raises false
expectations. It's often better to make things distinctly different than
to make them /almost/ the same.
-- Henry Spencer
Rule of Silence: When a program has nothing surprising to say, it
should say nothing.
One of Unix's oldest and most persistent design rules is that when a
program has nothing interesting or surprising to say, it should /shut
up/. Well-behaved Unix programs do their jobs unobtrusively, with a
minimum of fuss and bother. Silence is golden.
This ?silence is golden? rule evolved originally because Unix predates
video displays. On the slow printing terminals of 1969, each line of
unnecessary output was a serious drain on the user's time. That
constraint is gone, but excellent reasons for terseness remain.
I think that the terseness of Unix programs is a central feature of the
style. When your program's output becomes another's input, it should be
easy to pick out the needed bits. And for people it is a human-factors
necessity ? important information should not be mixed in with verbosity
about internal program behavior. If all displayed information is
important, important information is easy to find.
-- Ken Arnold
Well-designed programs treat the user's attention and concentration as a
precious and limited resource, only to be claimed when necessary.
(We'll discuss the Rule of Silence and the reasons for it in more detail
at the end of Chapter 11 .)
Rule of Repair: Repair what you can ? but when you must fail, fail
noisily and as soon as possible.
Software should be transparent in the way that it fails, as well as in
normal operation. It's best when software can cope with unexpected
conditions by adapting to them, but the worst kinds of bugs are those in
which the repair doesn't succeed and the problem quietly causes
corruption that doesn't show up until much later.
Therefore, write your software to cope with incorrect inputs and its own
execution errors as gracefully as possible. But when it cannot, make it
fail in a way that makes diagnosis of the problem as easy as possible.
Consider also Postel's Prescription:^[ ^10 ]
?Be liberal in what you accept, and conservative in what you send?.
Postel was speaking of network service programs, but the underlying idea
is more general. Well-designed programs cooperate with other programs by
making as much sense as they can from ill-formed inputs; they either
fail noisily or pass strictly clean and correct data to the next program
in the chain.
However, heed also this warning:
The original html documents recommended ?be generous in what you
accept?, and it has bedeviled us ever since because each browser accepts
a different superset of the specifications. It is the /specifications/
that should be generous, not their interpretation.
-- Doug McIlroy
McIlroy adjures us to /design/ for generosity rather than compensating
for inadequate standards with permissive implementations. Otherwise, as
he rightly points out, it's all too easy to end up in tag soup.
Rule of Economy: Programmer time is expensive; conserve it in
preference to machine time.
In the early minicomputer days of Unix, this was still a fairly radical
idea (machines were a great deal slower and more expensive then).
Nowadays, with every development shop and most users (apart from the few
modeling nuclear explosions or doing 3D movie animation) awash in cheap
machine cycles, it may seem too obvious to need saying.
Somehow, though, practice doesn't seem to have quite caught up with
reality. If we took this maxim really seriously throughout software
development, most applications would be written in higher-level
languages like Perl, Tcl, Python, Java, Lisp and even shell ? languages
that ease the programmer's burden by doing their own memory management
(see [Ravenbrook ]).
And indeed this is happening within the Unix world, though outside it
most applications shops still seem stuck with the old-school Unix
strategy of coding in C (or C++). Later in this book we'll discuss this
strategy and its tradeoffs in detail.
One other obvious way to conserve programmer time is to teach machines
how to do more of the low-level work of programming. This leads to...
Rule of Generation: Avoid hand-hacking; write programs to write
programs when you can.
Human beings are notoriously bad at sweating the details. Accordingly,
any kind of hand-hacking of programs is a rich source of delays and
errors. The simpler and more abstracted your program specification can
be, the more likely it is that the human designer will have gotten it
right. Generated code (at /every/ level) is almost always cheaper and
more reliable than hand-hacked.
We all know this is true (it's why we have compilers and interpreters,
after all) but we often don't think about the implications.
High-level-language code that's repetitive and mind-numbing for humans
to write is just as productive a target for a code generator as machine
code. It pays to use code generators when they can raise the level of
abstraction ? that is, when the specification language for the generator
is simpler than the generated code, and the code doesn't have to be
hand-hacked afterwards.
In the Unix tradition, code generators are heavily used to automate
error-prone detail work. Parser/lexer generators are the classic
examples; makefile generators and GUI interface builders are newer ones.
(We cover these techniques in Chapter 9 .)
Rule of Optimization: Prototype before polishing. Get it working
before you optimize it.
The most basic argument for prototyping first is Kernighan & Plauger's;
?90% of the functionality delivered now is better than 100% of it
delivered never?. Prototyping first may help keep you from investing far
too much time for marginal gains.
For slightly different reasons, Donald Knuth (author of /The Art Of
Computer Programming/, one of the field's few true classics) popularized
the observation that ?Premature optimization is the root of all evil?.^[
^11 ] And he was right.
Rushing to optimize before the bottlenecks are known may be the only
error to have ruined more designs than feature creep. From tortured code
to incomprehensible data layouts, the results of obsessing about speed
or memory or disk usage at the expense of transparency and simplicity
are everywhere. They spawn innumerable bugs and cost millions of
man-hours ? often, just to get marginal gains in the use of some
resource much less expensive than debugging time.
Disturbingly often, premature local optimization actually hinders global
optimization (and hence reduces overall performance). A prematurely
optimized portion of a design frequently interferes with changes that
would have much higher payoffs across the whole design, so you end up
with both inferior performance and excessively complex code.
In the Unix world there is a long-established and very explicit
tradition (exemplified by Rob Pike's comments above and Ken Thompson's
maxim about brute force) that says: /Prototype, then polish. Get it
working before you optimize it/. Or: Make it work first, then make it
work fast. ?Extreme programming' guru Kent Beck, operating in a
different culture, has usefully amplified this to: ?Make it run, then
make it right, then make it fast?.
The thrust of all these quotes is the same: get your design right with
an un-optimized, slow, memory-intensive implementation before you try to
tune. Then, tune systematically, looking for the places where you can
buy big performance wins with the smallest possible increases in local
complexity.
Prototyping is important for system design as well as optimization ? it
is much easier to judge whether a prototype does what you want than it
is to read a long specification. I remember one development manager at
Bellcore who fought against the ?requirements? culture years before
anybody talked about ?rapid prototyping? or ?agile development?. He
wouldn't issue long specifications; he'd lash together some combination
of shell scripts and awk code that did roughly what was needed, tell the
customers to send him some clerks for a few days, and then have the
customers come in and look at their clerks using the prototype and tell
him whether or not they liked it. If they did, he would say ?you can
have it industrial strength so-many-months from now at such-and-such
cost?. His estimates tended to be accurate, but he lost out in the
culture to managers who believed that requirements writers should be in
control of everything.
-- Mike Lesk
Using prototyping to learn which features you don't have to implement
helps optimization for performance; you don't have to optimize what you
don't write. The most powerful optimization tool in existence may be the
delete key.
One of my most productive days was throwing away 1000 lines of code.
-- Ken Thompson
(We'll go into a bit more depth about related ideas in Chapter 12
.)
Rule of Diversity: Distrust all claims for ?one true way?.
Even the best software tools tend to be limited by the imaginations of
their designers. Nobody is smart enough to optimize for everything, nor
to anticipate all the uses to which their software might be put.
Designing rigid, closed software that won't talk to the rest of the
world is an unhealthy form of arrogance.
Therefore, the Unix tradition includes a healthy mistrust of ?one true
way? approaches to software design or implementation. It embraces
multiple languages, open extensible systems, and customization hooks
everywhere.
Rule of Extensibility: Design for the future, because it will be
here sooner than you think.
If it is unwise to trust other people's claims for ?one true way?, it's
even more foolish to believe them about your own designs. Never assume
you have the final answer. Therefore, leave room for your data formats
and code to grow; otherwise, you will often find that you are locked
into unwise early choices because you cannot change them while
maintaining backward compatibility.
When you design protocols or file formats, make them sufficiently
self-describing to be extensible. Always, /always/ either include a
version number, or compose the format from self-contained,
self-describing clauses in such a way that new clauses can be readily
added and old ones dropped without confusing format-reading code. Unix
experience tells us that the marginal extra overhead of making data
layouts self-describing is paid back a thousandfold by the ability to
evolve them forward without breaking things.
When you design code, organize it so future developers will be able to
plug new functions into the architecture without having to scrap and
rebuild the architecture. This rule is not a license to add features you
don't yet need; it's advice to write your code so that adding features
later when you /do/ need them is easy. Make the joints flexible, and put
?If you ever need to...? comments in your code. You owe this grace to
people who will use and maintain your code after you.
You'll be there in the future too, maintaining code you may have half
forgotten under the press of more recent projects. When you design for
the future, the sanity you save may be your own.
------------------------------------------------------------------------
^[ ^9 ] Pike's original adds ?(See Brooks p.
102.)? here. The reference is to an early edition of /The Mythical
Man-Month/ [Brooks ]; the quote is ?Show me your flow
charts and conceal your tables and I shall continue to be mystified,
show me your tables and I won't usually need your flow charts; they'll
be obvious?.
^[10 ] Jonathan Postel was the first editor of
the Internet RFC series of standards, and one of the principal
architects of the Internet. A tribute page
is maintained by the Postel Center
for Experimental Networking.
^[11 ] In full: ?We should forget about small
efficiencies, say about 97% of the time: premature optimization is the
root of all evil?. Knuth himself attributes the remark to C. A. R. Hoare.
------------------------------------------------------------------------
Prev Up Next
What Unix Gets Right Home The Unix Philosophy in One Lesson