# A Plea for Lean Software

Niklaus Wirth
ETH Zurich
1995

Memory  requirements   of  today's   workstations   typically  jump
substantially--from several  to many megabytes--whenever  there's a
new software release. When demand  surpasses capacity, it's time to
buy add-on memory. When the  system has no more extensibility, it's
time  to  buy  a  new,  more  powerful  workstation.  Do  increased
performance and  functionality keep pace with  the increased demand
for resources? Mostly the answer is no.

About 25  years ago, an  interactive text editor could  be designed
with as little  as 8,000 bytes of storage.  (Modern program editors
request 100  times that  much!) An operating  system had  to manage
with 8,000 bytes, and a compiler had to fit into 32 Kbytes, whereas
their modern  descendants require megabytes. Has  all this inflated
software become  any faster?  On the  contrary. Were  it not  for a
thousand times  faster hardware,  modern software would  be utterly
unusable.

Enhanced user convenience and  functionality supposedly justify the
increased  size  of  software,  but a  closer  look  reveals  these
justifications  to  be shaky.  A  text  editor still  performs  the
reasonably simple task of inserting,  deleting, and moving parts of
text; a compiler still translates text into executable code; and an
operating system  still manages  memory, disk space,  and processor
cycles. These basic obligations have not changed with the advent of
windows, cut-and-paste  strategies, and pop-up menus,  nor with the
replacement of meaningful command words by pretty icons.

The apparent software explosion is  accepted largely because of the
staggering  progress made  by semiconductor  technology, which  has
improved the  price/performance ratio  to a degree  unparalleled by
any other  branches of technology.  For example, from 1978  to 1993
Intel's 80x86 family  of processors increased power by  a factor of
335, transistor density  by a factor of 107, and  price by a factor
of about 3.  The prospects for continuous  performance increase are
still solid, and there is no sign that software's ravenous appetite
will  be appeased  anytime  soon.[1] This  development has  spawned
numerous rules,  laws, and corollaries, which  are--as is customary
in such  cases--expressed in general  terms; thus they  are neither
provable nor  refutable. With a  touch of humor, the  following two
laws reflect the state of the art admirably well:

* Software expands  to fill  the available  memory. (Parkinson)
* Software getting  slower  more   rapidly  than  hardware  becomes
 faster. (Reiser)

Uncontrolled  software  growth  has   also  been  accepted  because
customers  have trouble  distinguishing between  essential features
and those  that are  just "nice  to have."  Examples of  the latter
class:  those  arbitrarily  overlapping windows  suggested  by  the
uncritically but  widely adopted desktop metaphor;  and fancy icons
decorating  the  screen  display,  such as  antique  mailboxes  and
garbage cans that  are further enhanced by the  visible movement of
selected items toward their ultimate destination. These details are
cute but not essential, and they have a hidden cost.

## Causes for "fat software"

Clearly, two contributing factors to the acceptance of ever-growing
software  are  (1) rapidly  growing  hardware  performance and  (2)
customers' ignorance of features  that are essential-versus-nice to
have. But perhaps more important than finding reasons for tolerance
is questioning the causes: What drives software toward complexity?

A primary cause of complexity is that software vendors uncritically
adopt  almost  any feature  that  users  want. Any  incompatibility
with  the  original system  concept  is  either ignored  or  passes
unrecognized, which renders the design more complicated and its use
more cumbersome. When a system's power is measured by the number of
its features,  quantity becomes more important  than quality. Every
new release must offer additional  features, even if some don't add
functionality.

### All features, all the time

Another important reason for software complexity lies in monolithic
design, wherein all  conceivable features are part  of the system's
design. Each customer pays for  all features but actually uses very
few. Ideally, only  a basic system with  essential facilities would
be offered, a system that  would lend itself to various extensions.
Every customer could then  select the extensions genuinely required
for a given task.

Increased hardware power has undoubtedly been the primary incentive
for  vendors to  tackle  more complex  problems,  and more  complex
problems  inevitably  require more  complex  solutions.  But it  is
not  the inherent  complexity that  should  concern us;  it is  the
self-inflicted complexity. There are many problems that were solved
long ago,  but for the same  problems we are now  offered solutions
wrapped in much bulkier software.

Increased complexity results in large part from our recent penchant
for friendly  user interaction. I've already  mentioned windows and
icons;  color, gray-scales,  shadows,  pop-ups,  pictures, and  all
kinds of gadgets can easily be added.

### To some, complexity equals power

A  system's  ease   of  use  always  should  be   a  primary  goal,
but  that  ease should  be  based  on  an underlying  concept  that
makes  the  use  almost  intuitive. Increasingly,  people  seem  to
misinterpret complexity  as sophistication, which  is baffling--the
incomprehensible should cause suspicion rather than admiration.

Possibly this  trend results  from a mistaken  belief that  using a
somewhat mysterious  device confers an  aura of power on  the user.
(What  it  does  confer  is  a  feeling  of  helplessness,  if  not
impotence.) Therefore, the lure of  complexity as a sales incentive
is easily  understood; complexity  promotes customer  dependence on
the vendor.

It's  well known,  for  example, that  major  software houses  have
heavily  invested--with  success--in  customer  service,  employing
hundreds of consultants to answer  customer calls around the clock.
Much more economical for both producer and consumer, however, would
be a product  based on a systematic concept--that  is, on generally
valid rules  of inference rather than  on tables of rules  that are
applicable  to specific  situations  only--coupled with  systematic
documentation and  a tutorial. Of  course, a customer  who pays--in
advance--for service contracts is a  more stable income source than
a customer  who has  fully mastered a  product's use.  Industry and
academia  are probably  pursuing very  different goals;  hence, the
emergence of another "law":

* Customer dependence is more profitable than customer education.

What  I   find  truly  baffling  are   manuals--hundreds  of  pages
long--that accompany software  applications, programming languages,
and operating  systems. Unmistakably, they signal  both a contorted
design that lacks clear concepts and an intent to hook customers.

This lack  of lucid concepts  can't alone account for  the software
explosion. Designing solutions for complicated problems, whether in
software or hardware, is a difficult, expensive, and time-consuming
process.  Hardware's  improved  price/performance  ratio  has  been
achieved  more  from  better technology  to  duplicate  (fabricate)
designs  than  from  better  design  technique  mastery.  Software,
however, is all  design, and its duplication costs  the vendor mere
pennies.

Initial  designs   for  sophisticated  software   applications  are
invariably complicated, even when developed by competent engineers.
Truly good  solutions emerge after iterative  improvements or after
redesigns  that  exploit  new  insights,  and  the  most  rewarding
iterations  are  those  that  result  in  program  simplifications.
Evolutions of  this kind,  however, are  extremely rare  in current
software  practice--they require  time-consuming thought  processes
that  are  rarely  rewarded.  Instead,  software  inadequacies  are
typically corrected by quickly  conceived additions that invariably
result in the well-known bulk.

### Never enough time

Time pressure is probably the  foremost reason behind the emergence
of  bulky  software.  The   time  pressure  that  designers  endure
discourages  careful  planning.  It also  dis-  couragcs  improving
acceptable  solutions;  instead,  it encourages  quickly  conceived
software  additions   and  corrections.  Time   pressure  gradually
corrupts an engineer's standard of quality and perfection. It has a
detrimental effect on people as well as products.

The fact  that the vendor whose  product is first on  the market is
generally more  successful than the competitor  who arrives second,
although with a better  design, is another detrimental contribution
to the computer industry. The tendency  to adopt the "first" as the
de facto  standard is  a deplorable phenomenon,  based on  the same
time pressure.

Good  engineering   is  characterized   by  a   gradual,  step-wise
refinement  of products  that  yields  increased performance  under
given  constraints and  with given  resources. Software's  resource
limitations  are  blithely  ignored, however:  Rapid  increases  in
processor speed and memory size are commonly believed to compensate
for sloppy  software design.  Meticulous engineering habits  do not
pay off in the short run, which  is one reason why software plays a
dubiaus role among established engineering disciplines.

## Languages and design methodology

Although software  research, which  theoretically holds the  key to
many future  technologies, has been heavily  supported, its results
are  seemingly  irrelevant  to  industry.  Methodical  design,  for
example, is  apparently undesirable  because products  so developed
take  too  much  "time  to  market."  Analytical  verification  and
correctness-proof techniques  fare even  worse; in  addition, these
methods require a higher intellectual caliber than that required by
the  customary  "try  and  fix it"  approach.  To  reduce  software
complexity by  concentrating only on  the essentials is  a proposal
swiftly  dismissed as  ridiculous in  view of  customers' love  for
bells and whistles.  When "everything goes" is  the modus operandi,
methodologies and disciplines are the first casualties.

Programming language methodologies  are particularly controversial.
In  the 1970s,  it was  widely  believed that  program design  must
be  based  on well-structured  methods  and  layers of  abstraction
with clearly  defined specifications.  The abstract data  type best
exemplified this  idea and  found expression in  then-new languages
such  as  Modula-2  and  Ada.  Today,  programmers  are  abandoning
well-structured languages and migrating mostly to C. The C language
doesn't even let  compilers perform secure type  checking, yet this
compiler  task is  by far  most helpful  to program  development in
locating  early conceptual  mistakes.  Without  type checking,  the
notion of  abstraction in programming languages  remains hollow and
academic. Abstraction  can work only with  languages that postulate
strict,  static typing  of  every variable  and  function. In  this
respect, C  fails--it resembles  assembler code,  where "everything
goes."

### Reinventing the wheel?

Remarkably enough, the  abstract data type has  reappeared 25 years
after  its  invention under  the  heading  object oriented.    This
modern term's essence, regarded by  many as a panacea, concerns the
construction  of  class  (type)  hierarchies.  Although  the  older
concept  hasn't caught  on  without the  newer description  "object
oriented,"  programmers recognize  the  intrinsic  strength of  the
abstract  data  type  and  convert  to it.  To  be  worthy  of  the
description, an object-oriented language must embody strict, static
typing that cannot be breached, whereby programmers can rely on the
compiler  to  identify  inconsistencies.  Unfortunately,  the  most
popular object-oriented language,  C++, is no help  here because it
has been  declared to be  upwardly compatible with its  ancestor C.
Its wide acceptance confirms the following "laws":

* Progress is acceptable  only if it's compatible  with the current
 state. Adhering to a standard is always safer.

Given  this   situation,  programmers  struggle  with   a  language
that  discourages  structured   thinking  and  disciplined  program
construction (and denies basic  compiler support). They also resort
to makeshift tools that chiefly add to software's bulk.

What a grim picture; what a pessimist! the reader must be thinking.
No  hint of  computing's bright  future, heretofore  regarded as  a
given.

This admittedly  somber view  is realistic; nonetheless,  given the
will, there is a way to improve the state of the art.

## Project Oberon

Between  1986  and   1989,  Jurg  Gutknecht  and   I  designed  and
implemented  a  new   software  system--called  Oberon--for  modern
workstations, based on  nothing but hardware. Our  primary goal was
to  show that  software can  be developed  with a  fraction of  the
memory  capacity  and  processor power  usually  required,  without
sacrificing flexibility, functionality, or user convenience.

The  Oberon system  has been  in use  since 1989,  serving purposes
that  include  document   preparation,  software  development,  and
computer-aided design  of electronic  circuits, among  many others.
The system includes

* storage management,
* a file system,
* a window display manager,
* a network with servers,
* a compiler, and
* text, graphics, and document editors.

Designed and implemented--from scratch--by  two people within three
years,  Oberon  has  since  been  ported  to  several  commercially
available  workstations  and  has found  many  enthusiastic  users,
particularly since it is freely available.[2]

Our secondary goal was to design a system that could be studied and
explained in  detail, a system  suitable as a  software-design case
study that could be penetrated  top-down and whose design decisions
could be stated  explicitly. (Indeed, there is a  lack of published
case studies in  software construction, which becomes  all the more
evident when one  is faced with the task of  teaching courses.) The
result of  our efforts is a  single book that describes  the entire
system and contains the source code of all modules.

How  is it  possible  to build  a software  system  with some  five
man-years of effort and present it in a single book?[3]

### Three underlying tenets

First,  we  concentrated  on the  essentials.  We  omitted anything
that didn't fundamentally contribute  to power and flexibility. For
example,  user  interaction in  the  basic  system is  confined  to
textual information--no graphics, pictures, or icons.

Secondly,  we wanted  to use  a truly   object-oriented programming
language,  one  that was type-safe.  This, coupled with  our belief
that  the first  tenet  must  apply even  more  stringently to  the
tools  than to  the system  being built,  forced us  to design  our
own  language  and  to  construct  its compiler  as  well.  It  led
to  Oberon,[4]  a language  derived  from  Modula-2 by  eliminating
less essential  features (like  subrange and enumeration  types) in
addition  to  features  known  to be  unsafe  (like  type  transfer
functions and variant records).

Lastly, to be simple, efficient, and  useful, we wanted a system to
be  flexibly extensible.   This meant  that  new  modules could  be
added  that incorporate  new procedures  based on  calling existing
ones. It  also meant that new  data types could be  defined (in new
modules), compatible  with existing  types. We call  these extended
types, and  they constitute the  only fundamental concept  that was
added to Modula-2.

### Type extension

If, for  example, a  type  Viewer  is defined  in  a  module called
Viewers, then a type TextViewer can  be  defined as an extension of
Viewer (typically, in another module  that is added to the system).
Whatever operations apply to  Viewers apply equally to TextViewers,
and whatever properties Viewers have, TextViewers have as well.

Extensibility  guarantees  that  modules  may  later  be  added  to
the  system  without  requiring either  changes  or  recompilation.
Obviously, type safety is crucial and must cross module boundaries.

Type  extension  is a  typical  object-oriented  feature. To  avoid
misleading  anthropomorphisms, we  prefer to  say "TextViewers  are
compatible  with Viewers,"  rather than  "TextViewers inherit  from
Viewers." We  also avoid  introducing an entirely  new nomenclature
for  well-known  concepts;  for  example,  we  stick  to  the  term
type, avoiding  the word  class; we retain  the terms  variable and
procedure,  avoiding the  new terms  instance and  method. Clearly,
our  first  tenet--concentrating  on  essentials--also  applies  to
terminology.

### Tale of a data type

An example of a data type  will illustrate our strategy of building
basic functionality in a core system, with features added according
to the system's extensibility.

In the  system's core, the data  type Text is defined  as character
sequences with  the attributes  of font,  offset, and  color. Basic
editing operations are provided in a module called TextFrames.

An electronic mail  module is not included in the  core, but can be
added when there is a demand. When it is added, the electronic mail
module  relies  on the  core  system  and  imports the  types  Text
and  TextFrame displaying  texts.  This means  that normal  editing
operations can be applied to received e-mail messages. The messages
can be modified,  copied, and inserted into other  texts visible on
the screen  display by using  core operations. The  only operations
that the  e-mail module  uniquely provides are  receiving, sending,
and  deleting  a  message,  plus  a command  to  list  the  mailbox
directory.

### Operation activation

Another  example that  illustrates our  strategy is  the activation
of  operations.  Programs  are  not executed  in  Oberon;  instead,
individual  procedures  are exported  from  modules.  If a  certain
module M exports a procedure P, then P can be called (activated) by
merely pointing at the string M.P  appearing in any text visible on
the display,  that is, by moving  the cursor to M.P  and clicking a
mouse  button. Such  straightforward command  activation opens  the
following possibilities:

1. Frequently  used commands  are listed in  short pieces  of text.
These are called tool-texts and resemble customized menus, although
no special menu software is  required. They are typically displayed
in small viewers (windows).

2.  By  extending   the  system  with  a   simple  graphics  editor
that  provides captions  based  on Oberon  texts,  commands can  be
highlighted and  otherwise decorated with boxes  and shadings. This
results in pop-up  and/or pull-down menus, buttons,  and icons that
are  "free"  because  the  basic command  activation  mechanism  is
reused.

3. A  message received by  e-mail can  contain commands as  well as
text. Commands  are executed by  the recipient's clicking  into the
message (without  copying into  a special  command window).  We use
this feature,  for example, when  announcing new or  updated module
releases. The message typically  contains receive commands followed
by lists  of module names  to be  downloaded from the  network. The
entire process requires only a few mouse clicks.

### Keeping it simple

The  strategy of  keeping  the core  system  simple but  extensible
rewards the  modest user. The  Oberon core occupies fewer  than 200
Kbytes, including editor  and compiler. A computer  system based on
Oberon needs to  be expanded only if  large, demanding applications
are  requested, such  as  CAD with  large  memory requirements.  If
several such  applications are  used, the  system does  not require
them to be  simultaneously loaded. This economy is  achieved by the
following system properties:

1. Modules can be loaded on  demand. Demand is signaled either when
a command  is activated--which is  defined in a module  not already
loaded--or when  a module being  loaded imports another  module not
already present. Module  loading can also result  from data access.
For example,  when a document  that contains graphical  elements is
accessed by an editor whose graphic  package is not open, then this
access inherently triggers its loading.

2. Every module is in memory  at most once. This rule prohibits the
creation of linked load files (core images). Typically, linked load
files are  introduced in operating  systems because the  process of
linking  is  complicated  and  time-consuming  (sometimes  more  so
than compilation).  With Oberon,  linking cannot be  separated from
loading.  This  is  entirely  acceptable  because  the  intertwined
activities are very fast; they  happen automatically the first time
a module is referenced.

### The price of simplicity

The experienced  engineer, realizing  that free lunches  never are,
will  now  ask,  Where  is  the  price  for  this  economy  hidden?
A  simplified  answer  is:  in  a  clear  conceptual  basis  and  a
well-conceived, appropriate system structure.

Ifthe core--or any other  module--is to be successfully extensible,
its designer must understand how it  will be used. Indeed, the most
demanding  aspect  of  system  design  is  its  decomposition  into
modules. Each module  is a part with a  precisely defined interface
that specifies imports and exports.

Each module also encapsulates implementation techniques. All of its
procedures must be consistent with respect to handling its exported
data types. Precisely defining the right decomposition is difficult
and can  rarely be achieved without  iterations. Iterative (tuning)
improvements are of  course only possible up to the  time of system
release.

It is  difficult to  generalize design rules.  If an  abstract data
type  is  defined,  carefully  deliberated  basic  operations  must
accompany it, but composite operations should be avoided. It's also
safe to  say that  the long-accepted  rule of  specification before
implementation must be  relaxed. Specifications can turn  out to be
as unsuitable as implementations can turn out to be wrong.

##

In  concluding,  here are  nine  lessons  learned from  the  Oberon
project that  might be worth  considering by anyone embarking  on a
new software design:

1. The  exclusive use  of a  strongly typed  language was  the most
influential factor in designing this  complex system in sucha short
time. (The manpower was a small fraction of what would typically be
expended for  comparably sized projects based  on other languages.)
Static typing (a) lets the compiler pinpoint inconsistencies before
program  execution; (b)  lets the  designer change  definitions and
structures  with  less danger  of  negative  consequences; and  (c)
speeds up the improvement process, which could include changes that
might not otherwise be considered feasible.

2, The most  difficult design task is to find  the most appropriate
decomposition  of the  whole  into a  module hierarchy,  minimizing
function and code duplications. Oberon is highly supportive in this
respect by carrying type checks over module boundaries.

3. Oberon's type extension construct was essential for designing an
extensible system  wherein new modules added  functionality and new
object classes  integrated compatibly with the  existing classes or
data  types.  Extensibility is  prerequisite  to  keeping a  system
streamlined  from the  outset. It  also  permits the  system to  be
customized  to  accommodate  specific  applications  at  any  time,
notably without access to the source code.

4. In  an extensible  system, the  key issue  is to  identify those
primitives that  offer the  most flexibility for  extensions, while
avoiding a proliferation of primitives.

5. The belief that complex  systems require armies of designers and
programmers  is wrong.  A  system  that is  not  understood in  its
entirety, or at least to a significant degree of detail by a single
individual, should probably not be built.

6.  Communication problems  grow as  the  size of  the design  team
grows. Whether they are obvious or not, when communication problems
predominate, the team and the project are both in deep trouble.

7. Reducing complexity and size must  be the goal in every step--in
system  specification,  design,  and  in  detailed  programming.  A
programmer's competence  should be  judged by  the ability  to find
simple solutions, certainly not by productivity measured in "number
of  lines  ejected per  day."  Prolific  programmers contribute  to
certain disaster.

8.  To  gain experience,  there  is  no  substitute for  one's  own
programming  effort. Organizing  a team  into managers,  designers,
programmers,  analysts,  and  users   is  detrimental.  All  should
participate (with differing degrees of  emphasis) in all aspects of
development.  In  particular, everyone--including  managers--should
also be  product users for  a time. This  last measure is  the best
guarantee  to  correct  mistakes  and  perhaps  also  to  eliminate
redundancies.

9.  Programs should  be  written and  polished  until they  acquire
publication quality.  It is infinitely  more demanding to  design a
publishable  program  than  one  that "runs."  Programs  should  be
written for human readers as well  as for computers. If this notion
contradicts certain  vested interests  in the commercial  world, it
should at least find no resistance in academia.

With Project Oberon we have demonstrated that flexible and powerful
systems can  be built  with substantially  fewer resources  in less
time than usual,  The plague of soft- ware explosion  is not a "law
of nature." It is avoidable, and it is the software engineer's task
to curtail it.

## References

[1.] E. Perratore et al., "Fighting Fatware," Byte, Vol. 18, No. 4,
Apr. 1993, pp. 98-108.

[2.] M. Reiser, The  Oberon System, Addison-Wesley, Reading, Mass.,
1991.

[3.] N.  Wirth and J.  Gutknecht, Project Oberon--The Design  of an
Operating  System  and  Compiler, Addison-Wesley,  Reading,  Mass.,
1992.

[4.] M.  Reiser and N.  Wirth, Programming in  Oberon--Steps Beyond
Pascal and Modula, Addison-Wesley, Reading, Mass., 1992.

-

Niklaus  Wirth  is  professor  af computer  science  at  the  Swiss
Federal Institute  of Technology (ETH)  in Zurich. He  designed the
programming  languages Pascal  (1970),  Modula  (1980), and  Oberon
(1988), and  the workstations  Lilith (1980)  and Ceres  (1986), as
well as their operating software.

Wirth received a PhD from  the University of California at Berkeley
in 1963. He  was awarded the IEEE Emmanuel Piore  Prize and the ACM
Turing Award  (1984). He was named  a Computer Pioneer by  the IEEE
Computer Society and is a Foreign Associate of the National Academy
of Engineering.

Readers can  contact the  author at Institut  fiir Computersysteme,
ETH CH-8092 Zurich, Switzerland; e-mail [email protected].