John Robinson's pages on
Engineering

INTRODUCTION
What engineers do

ARTICLES ON ENGINEERING
Engineering Thinking
Understanding Problems
Design Methodology

ARTICLES ON SOFTWARE
Software visions
The software artisan

ONLINE RESOURCES
Links

RETURN TO:
Userport homepage
York homepage

Software Visions

John Robinson, 1993 (with later revisions)

Software is different enough from other things that it's worth trying to describe carefully what makes it so unusual. Some things we know just from programming experience: software is invisible, abstract, infinitely malleable, and complex. It's also difficult, inelegant, fragile and often wrong. We can see already that it's going to be hard to decide which properties are essential to software, and which are just accidents of practical programming. Software turns a general purpose machine to a particular purpose, for as long as a particular program runs. That purpose may be so far removed from electronic interactions happening inside the computer, that there is no comprehensible way of describing one in terms of the other. Software has independent existence, but it is an artifact reflective of human thinking (Fred Brookes has called it "pure thought stuff") and of physical constraints. So software is strange, and there is plenty of opportunity for deep thinking on its essence.

Unfortunately there is no subdiscipline of software ontology in philosophy, which seems like a missed chance to me. But there is a history of the subject, and by reviewing that we can see how software has been understood by its practitioners. In this article I put different ideas about what software is into seven categories, created from definitions of the word "program", and I label the categories as though they described schools of thought or distinct intellectual perspectives. To some extent they do, but the point is to compare and contrast ideas, not to invite you to choose your favorite label. Indeed, I hope you'll choose all the labels, because from each perspective I draw out some consequences for software design that I think are worthwhile enough to be called principles. The perspectives and definitions reflect psychology (how it is most useful to think about software), as much as ontology (software's nature of being), because the ultimate purpose is to derive design principles for thinking humans to use.

Here, then, are seven ideas about the nature of software, each expressed as a definition of "program", each from a particular point of view.

The Mathematical View

A program is a formal description of the sequence of primitive operations required to reach some result. It is a mathematical statement, formally equivalent to a proof.

The Hardware View

A program is the top layer of a programmable control system.

The Literature View

A program is a document, expressing thoughts verbally. Software is written, read, and indexed; it is a literature.

The Organic Growth View

A program is a large, complicated, invisible organism, whose development and growth is determined by "programming".

The Artisan View

A program is a hand-made artifact, fashioned by a craftsperson, who applies creative judgement and experience to achieve beauty and utility simultaneously.

The User-Centered View

A program is a machine that makes a computer usable for a particular task.

The Engineering View

A program is an artifact, designed systematically, applying science and mathematics.

In exploring the seven perspectives, I will review some of the history of programming, bringing in ideas from prominent practitioners of Computer Science and Engineering. Discussion of each definition will lead to design principles. These, I believe, are enough to support sound software design.

1.1 Software as Mathematics

The Mathematical View

A program is a formal description of the sequence of primitive operations required to reach some result. It is a mathematical statement, formally equivalent to a proof.

In the early days of computers, there was no doubt that they belonged to the realm of mathematics. Not only were the original applications of computers mathematical, but the people who theorized about them, designed and built them were mathematicians.

In the early 20th century, Russell and Whitehead pioneered the formal application of logic to the process of mathematical proof. They saw that a "procedure" (how something is to be done) could be talked about in the same kind of abstract formalism as the objects on which the procedure operates. Gödel's famous theorem about the limitations of mathematical systems was developed by thinking of proof procedures in just this way --- as objects that could be manipulated algebraically. The other side of this coin was that the essence of a mechanical process lies not in the machine that does it, but in the abstract control structure for that machine. Church, Post, Turing, and others saw this abstract control structure as a "program", or sequence of simple operations. In showing that the logical form of a machine can be separated from its physical realization, they postulated that equivalent machines could be created (or "run") on different hardware, and that some hardware could be general-purpose (universal) enough to run many different programs.

The first computers were truly virtual machines, existing only in the imaginations of thinkers like Turing. They were conceived as general purpose automata that could be "programmed" to behave as though they were particular specific machines. That is, given a formal specification of a particular machine (in other words, a program), the computer would "become" that machine. In an exciting and technologically unprecedented way, a single general purpose computer could become any one of many abstract machines, according to the program it was given. And that program was invisible, an abstraction, "just" a formalism. This strange stuff we now call software rarely holds for us the same mystique. But at the birth of computer science, the conception of such a universal mechanism, that changes its behavior according to a mathematical abstraction, was an intellectual wonder.

Most of the early physically-realized computers were programmable calculating machines. They were used for numerical calculations, such as those required for the development of war technology in World War II. Mathematicians, like Turing, who were directly involved in the war effort, were able to participate in the building of the first machines that approached their conceptual structures. It was not until the late 1940s however, that truly general-purpose computers began to appear. It had been shown that there are many possible structures that could support programming. It was not long before the structure, or architecture, proposed by the Hungarian mathematician, John von Neumann, was widely adopted. This structure is familiar in the computers of today. It involves an arithmetic unit for combining data by addition, subtraction, etc., an area of storage for both instructions and data (here again is the idea of the equivalence of primitive operations and data), and a controller that successively fetches and executes instructions. There are facilities for testing conditions and for jumping out of the normal sequence to an instruction elsewhere in the program.

During the fifties, new types of theoretical computer science grew up. The analysis of algorithms using combinatorial mathematics provided tools for discovering how best to do common jobs like sorting and searching. Numerical analysis developed, as long and complicated procedures became practicable when run on computers. And the early emphasis on abstraction motivated the design of the first high-level languages. BASIC, FORTRAN and LISP were all invented in the fifties, the last of these being particularly interesting. The syntax of LISP was originally obscure and hard to learn. But then John McCarthy, LISP's inventor, wanted to use it to do some mathematics where the procedures and data would have the same syntactic form. The new syntax he developed (used today) reflects the same mathematical mindset as that of Russell and Whitehead --- procedures can be manipulated just like other objects. McCarthy has written at length on the use of LISP as a vehicle for reasoning about mathematics and computers. LISP is still the most-used language in artificial intelligence, though its links to the theory of machines have not been as significant as its flexibility in symbol manipulation.

Right from the start, programmers' thinking diverged from the idea of manipulating mathematical abstractions. Once computing machinery was available, they began to develop programs that grew in size and complexity, such that thinking of them as abstract machines was no longer natural (or even possible). More generally, during the fifties, practical programming came to be an art, learned chiefly through observation and imitation, rather decoupled from the theory being developed at the same time. The beginning of Computer Science teaching as a separate discipline, in the early sixties, was in part a response to this gap between theory and practice.

At the birth of Computer Science, there was already confusion about what it was. Some University CS departments were grown out of engineering or mathematics departments, some were created full-formed from nowhere. Those who were mathematicians wanted to return programming to its roots. Their main argument was that in seeing the equivalence between a program and a mathematical proof, they could not only teach people to think properly about programming, but they could also write programs that would be guaranteed correct.

It is interesting that some of the most respected computer scientists of the sixties were (and are) proponents of programming as mathematics, yet they are usually cited for their practical contributions to the art. For example, in several papers Edsger Dijkstra explored methods for proving program correctness. But he is probably better known for seminal work in real-time systems (semaphores, cooperating sequential processes) and for his leadership in promoting structured programming (though exactly what that means has never been agreed upon), and programming without GOTOs. C A R Hoare, whose paper "An axiomatic basis for computer programming" epitomizes the mathematical view, is better known as a contributor to real-time systems theory (monitors) and particularly as the inventor of Quicksort. These researchers would probably insist that their mathematical perspective underlay all their work, and the search for correctness-proving mechanisms inspired their practical discoveries.

Hoare’s "axiomatic basis" was the first practical system for proving program correctness. He was able to demonstrate it on the non-trivial program, "Find", which solves a subproblem of Quicksort, namely to find the element of an array A[1..N] whose value is nth in order of magnitude, and to put it at location A[n]. Hoare’s success inspired much work throughout the seventies, culminating in comprehensive correctness-proving schemes, such as that described in David Gries’ 1981 book "The Science of Programming". This is an inspiring book to read, though from a distance of over a decade, it now looks a bit overconfident. The reader would probably smell a rat as early as Dijkstra’s Forward, which makes much of the impact that correctness proving will have on programming practice. Dijkstra had said similar things a decade earlier, and the sea-change in programming practice he looked for still has not arrived.

What does the mathematical perspective on programming give to the student? Not the theory of algorithms, nor complexity theory, nor numerical analysis --- these are engineering perspectives really: the application of math to programming, rather than interpreting programming as math. What the mathematical perspective gives is the recognition that programming is about tautology -- saying the same thing different ways. Once the requirements for the program are properly specified, the creation of the final product is a sequence of conversions between equivalent representations. This does not belittle programming -- mathematicians are all too aware that working out different ways of saying things (like proving theorems) can be fiendishly difficult. Rather, it recognizes that how things are represented is more fundamental than what is represented. Programming is about making the right choices in representation -- how to structure data, how to group items into modules, how to express the complex twists and turns of processing.

The Mathematical Perspective is thus the inspiration for a principle of design:

The principle of representation (1)

The central question in software design is representation: How should this information, this procedure, this situation, be represented?

This principle pervades the whole software design process. At different stages -- problem statement, specification, modularization, programming, testing -- the same information will be represented in different ways. But always, the first concern is the structures within which things (data, actions) are expressed. How those structures are then "filled-in" is secondary. The formalisms discussed in specification, documentation, algorithms and data structures courses are all intended to make the conversion between the stages of design as exact (and as easy) as possible.

Arguably, the Mathematical Perspective could have provided a second principle of design:

Apply any available formal technique to ensure program correctness.

But it's not really honest to elevate this wish to a principle, because hardly anyone lives up to it. Today, application of formal correctness proving techniques to real-life programs is rare. This is changing, as more usuable, powerful techniques are developed and applied. But it will be a long time before formal methods are applied consistently across large applications. Even when that they are, adopting a proof procedure during the creation of programs does not guarantee against mistakes. There are plenty of examples of published but flawed mathematics.

1.2 Flexible Control of Flexible Hardware

The Hardware View

A program is the top layer of a programmable control system.

The second perspective, the one labelled The Hardware View in Table 1, is closer to the Mathematical perspective than any other, though nowadays having a hardware-oriented perspective usually shows an electronics background rather than a math background. Engineers concerned with embedded controllers, for example, bring a kind of bottom-up view to software: They know how semiconductor devices work, then how they are used to make logic gates, then the principles of combinatorial logic design, then sequential digital systems. They then see programming as the top layer of control of a particular flexible architecture. The diagram below illustrates the layered model that an electronics designer has of a computer.

 

How relevant is this perspective outside the relatively small group of programmers who are working "close to the hardware", perhaps designing control circuits, probably programming in assembly language? It is relevant in two ways.

First, the good designer will know enough about the capacity, performance and costs of the hardware platform to be able to include it in quantitative analysis of possible designs. Too many software projects get too far before someone realizes that an assumption about the platform's capabilities was incorrect -- and could have been shown incorrect by a simple calculation at the start of the project.

The hardware performance principle (2)

Know (quantitatively) the capabilities of the hardware.

But the hardware is often not the layer directly below the program. The operating system and the programming environment both separate the application from the hardware, and interpose a set of facilities. So a designer must know about the software architecture of the particular platform too. More specialized programming requires detailed knowledge about the services being used. In general then:

The little-picture principle (3)

Understand the mechanisms working at levels of abstraction below your program (but don't mess with them).

 

Much more could be said about the virtues of knowing your platform, understanding how the hardware and operating system work and being able to assess the implications of change. These are common sense issues that belong also to the engineering perspective.

1.3 Software as Literature

The Literature View

A program is a document, expressing thoughts verbally. Software is written, read, and indexed; it is a literature.

The idea of "writing" a program is much older than "building" a program. It is still the most common description of the process, and it invites consideration of parallels between programming and other forms of writing. While some experts, such as Nicklaus Wirth, the inventor of Pascal and Modula 2, dislike even the term programming language, others see programming as true literary expression.

As an illustration of this perspective, Kernighan and Plauger's definitive book, "The Elements of Programming Style" opens with these words:

"Good programming cannot be taught by preaching generalities. The way to learn to program well is by seeing, over and over, how real programs can be improved by the application of a few principles of good practice and a little common sense. Practice in critical reading leads to skill in rewriting, which in turn leads to better writing."

This point of view is founded on the literary view of software, and sees well-written code as the essence of a good software product.

Perhaps the boldest attempt to take the parallels between program writing and prose writing to their natural conclusions was that of Carolyn Van Dyke, in her article "Taking 'Computer Literacy' Literally" (Communications of the ACM, May 1987). She suggests five ways in which students (and teachers) of programming can learn from students (and teachers) of writing.

• Beware of rigid human standards beyond the explicit constraints of the language. Instead, learn from literate practitioners. Van Dyke cites Donald Knuth's attitude to the GOTO statement. Knuth is an acknowledged master of programming, yet he swims against the tide of wisdom that says GOTOs should be avoided.

• Learn style and syntax from texts that show the language in use. That is, read programs to benefit your own writing.

• Plan, write and rewrite in a recursive (sic) way. Don't expect to compose either linearly or in a top-down fashion, rather keep honing the program as you write.

• In teaching programming, emphasize the arts of invention; allow students some latitude in choosing topics, and freedom in their expression.

• Recognize that computer programs are a medium of human communication.

Because Van Dyke has taken the parallels further than anyone else, it may be that her advice needs tempering. Certainly, that is my view, but the comments below should not detract from the inspiration that Van Dyke's work is to a high view of program source code.

Van Dyke's first point, learning from the literate, echoes Kernighan and Plauger. Unfortunately, the advice to beware of standards is questionable. Generally speaking, coding standards are a boon to the programmer's art, directing the imagination away from the periphery and towards the real issues. There will be occasions when to break the rules is right, but knowing this does not help decide when those occasions are. The apprentice programmer is probably better off using the rules habitually, until the skill develops to recognize their limitations.

Reading programs to enlarge understanding is good advice. Good style and syntax might not always brush off, but it cannot do any harm to see how more experienced programmers tackle their craft.

Van Dyke's third point, about revising your program as you go along, does describe something programmers often do. No-one programs in a strictly linear or top-down fashion. There is nothing really wrong in changing something over there to make something over here work better, provided the total program is not too big. But there is a danger in Design by Successive Approximation when it makes the designer lazy about the first attempt.

So far as freedom of choice in student work is concerned, there certainly are advantages. This sort of freedom may increase motivation as students focus on their own interests. However, it makes the programmer free to do their own thing with the technology, without necessarily learning about how to attack problems. Dealing with problems is probably as hard to learn as good literate programming.

Van Dyke's fifth statement epitomizes the literary view, and is echoed by many other writers. Horowitz, in "Fundamentals of Programming Languages" speaks of a programming language as "a systematic notation by which we describe computational processes to others". This is something beyond a mere recognition that maintainability should be considered when a program is written. It suggests that a program should be addressed to a human audience first, and only secondly to the machine.

It is worth considering this aspect of programming as human communication a little further. Well before Van Dyke and Horowitz, Donald Knuth was advocating writing "Literate Programs". He and his students developed a system called "web" that allowed the programmer to compose code and commentary simultaneously, then automatically "tangled" the program into object code for the computer, and "weaved" it into a structured, typeset document for the human. Knuth himself wrote big programs using "web", and two, TeX and METAFONT, have been published as books.

In a "web" program, the code is introduced in "natural stages in the order its parts might have been written". Each section begins with commentary and then presents modified pascal code that implements something the size of a subroutine. Everything is cross-referenced via the typesetting facilities of TeX.

At the outset, Knuth hoped his new paradigm would be widely adopted and lead programmers to better design, just as it had in his own experience. To give literate programming a boost, Knuth produced two programs for the "Programming Pearls" column in Communications of the ACM (appearing in the May and June 1986 issues). An irregular column, "Literate Programming", appeared in CACM for the next couple of years. Each column included a "literate program" and a critique.

Knuth’s article, followed by the five Literate Programming columns through to March 1990, provide a potted, rather depressing, history. The only people submitting "literate programs" were those who had designed their own literate programming systems (of which "web" was but the first). Most of the published programs contained bugs. Some were extremely hard to understand. Knuth himself was guilty in this, in that he chose to introduce a new data structure, the hash trie, within one of his programs. It is all very elegant, and if you struggle hard, you can appreciate the artistry, but many people surely preferred the more conventional solution to the same problem published later by D Hanson. Hanson’s program did have a gigantic bug however, whereas Knuth’s only had a little one. The "Literate Programming" editor's tone in his final column is resigned: Literate programming is worthwhile, but perhaps its time is not quite come.

What, then, are we to make of Literate Programming? Granted that good style and comments are necessary, is it worth going further and writing programs as literature?

Yes! The Literature View emphasizes a crucial insight, so obvious that it is easy to miss: The embodiment of software is program text; while the essence may be illusive, the final word on what a program does is given in words, in the source code. A plot outline or a scenario does not capture the essence of a book - it is not literature. In the same way, project documentation is not the software - only the program is.

This leads to a principle:

The textual principle (4)

The text of the program itself - the source code - is the final authority.

The principle uses the word "authority" to emphasize the central nature of the code, not only for being the most reliable place to find out why a program works as it does, but also to stress that all design decisions are contingent on what can be said in the text. It is important to remember when coding is characterized as just part of the design of software (and sometimes a relatively small part), the code remains the central artifact and embodiment of the program.

1.4 Organic Software

The Organic Growth View

A program is a large, complicated, invisible organism, whose development and growth is determined by "programming".

Seeing a program as a mathematical statement puts software in the realm of pure mathematics. As far as it goes, this approach seems to be closest to what a program really is. But it does not go far enough. It cannot cope efficiently enough (or reliably enough) with the large programs that are of most practical importance. Seeing a program as a piece of literature encourages careful writing, and emphasizes the value of the code itself.

What about the view that software is something different from all other human creations? Later in the article, the engineering view is outlined, drawing parallels between programs and other artifacts. But what of the possibility that engineering is an inadequate context for programs --- that they are so different from other artifacts, that engineering models and methods must be completely revised, if not discarded, to make sense. Fred Brooks has made recent contributions to this debate, which point to the limitations of drawing from engineering when dealing with software.

Fred Brooks is Kenan Professor of Computer Science at the University of North Carolina, and the central figure in the development of the IBM System/360 mainframe computers in the 1960s. He wrote the collection of essays "The Mythical Man Month", a classic work on Software Engineering. In the late 1980's Brooks chaired a committee of the Department of Defense in the USA, looking into prospects for improving software productivity. The results of that committee's work were distilled in Brooks' article "No Silver Bullet" for IEEE Computer magazine (April 1987). The silver bullet in question is the one that will slay the werewolf of monstrous software, and Brooks' conclusion is that there is no such thing.

In the course of the article Brooks gives his definition of software. He sees three irreducible qualities:

• Software's complexity

• Its changeability

• Its invisibility

Brooks says that software is complex in a way that no other artifact is --- no two parts are alike. More important though, is that software elements interact in a non-linear fashion, so that as a program grows, its complexity increases much more than linearly. Unlike physical science, where simplified models of phenomena have been developed to abstract away the complexity, software complexity is inherent. Part of the complexity comes from the need to conform to standards, which, from the point of view of the program as a whole, are arbitrary --- they detract from the unity of the program, because different aspects --- the user interface, the file format, etc., all have different kinds of complexity imposed on them by conformity.

Brooks' second property, the changeability of software, is not a statement that other artifacts are immutable, but rather that the pressures for software change are so much stronger than for other objects. Successful software is pressured into new variants, "small" adaptations are proposed which extend the reach of an existing product. At the same time, computer hardware so quickly becomes obsolete, that programs must adapt to survive.

Brooks did not pursue the changeability property as far as he could. Perhaps this was because most of his readers would already be very familiar with its implications. As much as 75% of a project's costs can be absorbed by "Maintenance". This is not just work to fix bugs, but is exactly the kind of changes Brooks was talking about --- changes to meet new technology, addition of features, and so on. Many programmers spend their careers maintaining software. Design for Maintainability is a good maxim.

To some degree, the changeability of software is simply a reflection of its nature as "infinitely malleable thought-stuff", as Brooks calls it. This leads to the third of his properties - Invisibility.

Software has no spatial existence. Where there are geometrical abstractions to describe software (for example in visual programming environments), they are really just metaphors. The program itself is unvisualizable. The essence of software is that as data structures and algorithms, it represents conceptual, not physical, objects.

Brooks goes on, in his paper, to insist that many of Software Engineering's hot topics --- object-oriented programming, artificial intelligence, environments and tools, etc. --- attack only the accidents of software, not its essentials. He then gives four proposed attacks on the essence:

• Buy, don't build

• Use rapid prototyping to refine requirements

• Grow, don't build

• Nurture great designers

The first of these is very much the conventional engineering mindset --- no reinvention. Brooks is simply emphasizing a traditional value that has perhaps been too often forgotten in software. The second attack also appears in the other engineering disciplines, though it does not have the same importance as in programming. Skipping the third point for a moment, Brooks' final attack is really a management issue; recognizing that the best programmers are far better than the average, he recommends rewarding them appropriately.

Brooks’ third attack on software essence is the most interesting. In a way, it is nothing new. He is talking about incremental development, top-down design, stepwise refinement --- all buzzwords since the early seventies. Yet this is something quite different from what can be done in other engineering disciplines. Brooks and his predecessors argue that a system should first be made to run, even if all it does is call dummy "stubs" (empty subroutines). Then it should be fleshed out gradually, with stubs being replaced with useful code and perhaps a lower level of stubs. As each function is added, the program grows organically out of what is already there.

Now there can be arguments about this kind of top-down design. Sometimes stepwise refinement might be in conflict with another design principle --- for example, Information Hiding. But the crucial point that Brooks makes is that of morale. He observes that once a program is running, enthusiasm jumps and efforts redouble. If there is always a working system, no matter how limited, then the development team is far more motivated.

Brooks identified software’s invisibility as part of its essence. In a sense, it is one of software’s essential problems. Software cannot be seen, nor can it be touched. In these circumstances, the program engineer has a much more difficult job than the designer of bridges or engines. Anything that can provide psychological compensation is valuable. And this is what "growing" software does. To see a working though part-formed program gives the designer a sense of accomplishment and confidence. This is extremely important. Perhaps program engineering has, more than any other discipline, taught us the importance of the engineer’s psychology. Good morale should be the software project manager’s high aim.

So all this can be summed up:

The principle of incremental growth (5)

Though it do little more than nothing, make the first prototype a framework for the final program, and get it running early.

Going a little further with the picture of software as a growing, organic entity, note that this perspective explains why many programs become sprawling monsters - they just keep on growing. What can be done about this? Assuming that a program is going to sprawl, the best that can be done is to limit the lengths of its tentacles. In other words:

The principle of containment (6)

Localize information

There is one more lesson from the model of organic software. Related to Brooks' identification of the changeability of software as part of its essence, it might be said that a changing specification is a fact of most software projects. That is, customers, users, and programmers often do not know exactly what they want at the start of a project, and their requirements change while the work is in progress. This means that a good development methodology should be able to accommodate changes in the direction of program growth in response to changes in the specification. Although a specification change is often seen as the thing to be avoided at all costs, it is more realistic to assume that it will happen, many times. Therefore, software designers should assume, as a fact of life, that "The specification will change", and as a principle,

The principle of the changing specification (7)

Software design methodology should minimize the damage caused by changes to the specification.

 

 

1.5 The Craft of Program Construction

The Artisan View

A program is a hand-made artifact, fashioned by a craftsperson, who applies creative judgement and experience to achieve beauty and utility simultaneously.

Programming has often been seen as an art or a craft. This is a valuable perspective because it helps in understanding programming and programmers.

The craftsperson embraces all the artistic virtues appropriate to their medium that do not conflict with utility. Clarity, style and beauty are all important in creating good artifacts. They are sought not for their own sakes, but because things work better if they are well crafted. Through formal learning, apprenticeship and practice, a craftsperson develops skills in choosing and handling materials and tools, adapting to - indeed exploiting - the peculiarities of each design situation, and consistently achieving a polished finish to their products.

Good programs are carefully shaped, elegant and economical. They demonstrate craftsmanship.

The artisan principle (8)

See yourself as a craftsperson.

As a "design principle", this has more to do with attitude than process. Another article looks at the implications of seeing oneself as a craftsperson in the practice of software design.

This principle has implications for teaching and management of programmers. In the context of university teaching (which happens to be my context), it puts a heavy responsibility of the teacher: it is not the English blackboard notes that will teach most, but the program examples... so they had better be good. As well, because the practice of craft is fundamental to it, the teacher or mentor must convey and encourage a few "small" habits. The rationale is that habituated good practice is more valuable than wider-ranging knowledge that rarely gets applied. Procedural knowledge - skill - that is acquired by practicing design, needs to be guarded and exploited.

The skill-building principle (9)

Manage knowledge effectively

Treating programming as a craft has another implication for software management. This is so important psychologically that it is worth stating as a principle.

The principle of ownership (10)

In assigning programmers to work on a team project, aim to maximize each programmer's sense of ownership of their own code.

This is the one principle that has to do primarily with programming in a team. It has implications in modularization, testing and code inspection.

 

 

1.6 User-Centered Design

The User-Centered View

A program is a machine that makes a computer usable for a particular task.

The last twenty years have been characterized by the introduction of a huge number of unusable consumer products. Almost everyone has experience of difficult-to-use watches, clocks, VCRs, microwave ovens. Yet the problems of bad design in user interfaces have been around much longer. The psychologist Donald Norman includes stoves, doors, light switches in his list of poor interfaces. Nonetheless, people's everyday exposure to frustrating, inefficient and just plain bad interfaces has led to increased interest in "User-Centered Design" (Norman's phrase).

The user-centered designer would look at the other definitions in this chapter as missing the whole point - that programs are things that enable people to do things. Unless users are in the picture, then all this talk of essence is really accident - the program will not fulfill the practical purpose that people want to use it for. The software designer therefore needs to have a user-oriented perspective too.

There is another reason for being concerned with user interface. In typical modern personal-computer applications, the user interface is responsible for more than half of the code! With this degree of importance, it's worth codifying the perspective into a principle, even at the risk of tautology:

The User-Centered Design principle (11)

Design for your users

In any particular project the designer will be concerned about how the user currently does the job, what the user's needs are, what the work context is. Every software designer should also develop a general knowledge of human cognition, perhaps through a psychology course or book on human-machine interaction. Understanding something of human perception, pattern recognition, and mental models not only leads to better designs, but also fosters humility on the part of the designer. Inefficient, error-prone, unhappy use of a program by a user is a fault of the design, and it is arrogant to bludgeon the user with commands like "Do not attempt to use this product before reading the manual", when they just paid for it.

The context of User-Centered Design is an appropriate place to introduce another principle - one that has wider applicability than just the user interface.

The principle of consistency (12)

Embrace Standards

This principle should be applied in many different contexts. Perhaps, because of its generality it should have been introduced in the next section - engineers certainly embrace standards. But it is included here because consistency in the user interface is vital, yet designers are notorious for inventing their own modifications to standard interfaces. Almost always modifications should be avoided and imposed or commonly agreed standards used instead. A good example of this is programs written for the Macintosh. Right from the start, Apple has provided developers with User Interface guidelines. It is a mistake to diverge from these for any but the strongest reasons.

 

1.7 Software Design as Engineering

The Engineering View

A program is an artifact, designed systematically, applying science and mathematics.

Engineering has been defined many ways. Probably the following five points sum it up:

• Engineering is applying scientific knowledge and mathematical analysis to the solution of practical problems.

• It usually involves designing and building artifacts.

• It seeks good, and if possible, optimum, solutions, according to well-defined criteria.

• It uses reliable components and tools; it invents only where necessary, by synthesis from existing "building-blocks".

• Engineering is practiced according to well-defined principles and methods.

Notice that the size of the project is not mentioned. One can talk of engineering a coupling as legitimately as engineering an aircraft. Also, methodology, which is certainly part of the total picture, does not on its own define engineering.

The five-point list above immediately suggests an equivalent summary for Software Design:

• Software Design is applying scientific knowledge and mathematical analysis to the solution of practical problems.

• It usually involves designing and building programs.

• It seeks good, and if possible, optimum, solutions, according to well-defined criteria.

• It uses reliable components and tools; it invents only where necessary, by synthesis from existing "building-blocks".

• Software Design is practiced according to well-defined principles and methods.

 

Unfortunately, this "definition" of software design does not describe very well the experience that many programmers (including the author) have had in practice. But it does give a good starting place for talking about what Software Design can and should be.

 

The first part of the definition introduces two fundamental ideas. The first is reiterated in the following principle:

The principle of analysis (13)

Analysis is essential to design.

Analysis is not just important in software design; it is part of its essence. The programmer must understand and use the mathematical tools available for analyzing potential solutions.

The second fundamental idea is that of solving problems. The engineer stands, metaphorically, with one foot in the technology and one foot in the problem. (In civil engineering this can also be literally true!) The technology is the components, tools and processes for the job in hand; it is also the engineer's scientific and mathematical skill set. The problem is the real-world situation, the environment, the resources, the need that has to be addressed. The engineer must handle the technology competently, but equally must have a deep understanding of the problem domain. In software design, the practitioner must stand on the same two feet. Yes, understanding of theory and skillful use of tools are essential. But a good program can only be written if the problem domain is properly understood.

The problem domain principle (14)

Good design depends on understanding the problem and its context.

Part of problem definition is deciding on evaluation criteria -- asking the question, "How will I know I have succeeded?" Criteria may change over the course of a project, as the problem is better understood. But at any stage, they give quantitative ways of deciding how good a solution is. This leads to the next principle derived from the above definition.

The principle of the good solution (15)

Seek an optimal solution according to well-defined criteria.

A working solution, an existence proof, is not good enough. Here again the importance of analysis is clear.

The next part of the definition directly yields another principle:

The building-block principle (16)

Exploit existing solutions.

Use existing code; existing techniques; existing methodologies. Where possible design by synthesis from reliable existing building blocks. Principle 12: Embrace standards, could have been introduced in this context too.

There is a corollary to Principle 16, so important that it is almost a principle in its own right:

 

 

The software corollary (16a)

The algorithm is in a book.

Practicing software designers need to know how important algorithms work, where to find information on particular types of algorithms, and how to understand algorithm descriptions and performance analyses. It is very unusual for a programming job to require the invention of a substantial algorithm.

What about the last part of the definition? It mentions principles and methods. Methods and methodology are frameworks for design. The use of a methodology can be summed up:

The principle of methodology (17)

Design Systematically

Design according to a system. It was very tempting to make this principle broader: rather than "Design Systematically", to say "Act Systematically". Good engineers keep logbooks, they embrace Standards, they measure things very carefully, they test rigorously, they use checklists. These points may seem prosaic, perhaps peripheral to the real issues, but they are the habits of good engineers, and they reflect overall systematic action. In the end "Design Systematically" was chosen -- after all, it keeps the focus on design.

 

1.8 The Engineering Perspective and "Software Engineering"

Software Engineering was originally a response to the "Software Crisis" of the late 1960s, when it became clear that conventional programming techniques could not scale up well to large projects. It was (and is) concerned with the search for techniques to address this problem. Structured programming, high level languages and timesharing were originally seen as Software Engineering issues. The collection of articles entitled Programming Methodology, edited by David Gries, is a good illustration of this early perspective. But in the corporate consciousness of computing, Software Engineering has now become identified with higher-level methodology and process. Its application to the detail of programming practice has been lost.

Consider the higher-level perspective for a moment. A reasonable estimate of the world cost of software development in 1993 is $300,000,000,000. Of course, world software revenues are much greater than this --- software manufacturers have overheads, and they try to make a profit. Revenues are not related to costs in a simple relationship; for many middle-sized programs like personal computer word processors and spreadsheets, revenues depend critically on marketing, placement and compatibility strategies. The development cost for the initial release of such a program (which may have, say, 100,000 lines of code) might be as little as 20 person years. This could have no relationship to the eventual revenues. What is certain, though, is that the larger the program, the higher the cost per line of code. Development cost increases much faster than program size. Very large programs, such as those for electronic telephone switches, are longer than 20 million lines. They never stop growing and changing, and keep hundreds of programmers perpetually busy.

Several studies have shown that, as well as cost per line, two other factors change as program size increases: the difference in productivity between the best and the worst programmers decreases, and the proportion of development time spent in defect removal increases. The details of one study (Capers Jones, for IBM, 1977) are summarized in the table below. For these results, programming was in assembler language; a line of code corresponded to every non-commentary source statement. The lengths of time implied by this table seem staggering at first. One example Capers Jones gives in detail is of a 750kline program consisting of 75 components, each with a team of 5 programmers (375 technical programmers in all). There were 30 user reference documents maintained (6000 pages) plus 75 component and maintenance documents (7200 pages).

 

Size of job in klines of code.

Best programmer (months/kline)

Worst programmer (months/kline)

% time spent in defect removal

1

1

6

40

8

2.5

7

60

64

6.5

11

60

512

17.5

21

70

2048

30

32

75

Table 1. Capers Jones' results for IBM programmer productivity.

This information suggests a simple management strategy: For small programs, invest in the best programmers because their productivity is substantially greater than average programmers; for large programs invest in organizational solutions, aimed particularly at reducing the defect removal time.

What are some of these organizational solutions? Certainly methods are needed for keeping track of the huge amount of information embodied in a program. Documentation, version and change control are Software Engineering issues that apply here. More fundamentally, all developers want to produce programs that are correct, robust, efficient, maintainable. Software Engineering techniques for specification, testing, inspection are aimed at this.

Again, particularly for large systems, it is important that the cost and schedule be kept under control - Software Engineering is concerned with planning, estimating and tracking a project. Furthermore, there is increasing emphasis on speeding up development time. The need for new products quickly (and frequently) has driven the introduction of design methodologies geared to fast productization. For medium-sized software products, where cost is less of a concern than making the right product for the market, companies are prepared to spend more to achieve the same results faster. As a result, the emphases in software engineering are changing. The "Mythical Man Month" is around -- software development cannot be speeded up by adding people to a lagging project. But starting with a larger team might make sense, if there is confidence that it will speed up development. Subcontracting software development, using toolkits and object libraries, overlapping phases of the development process - all of these are things a company might try to reduce time to product. So the old emphases of Software Engineering - getting the specification right and rigorous, maintainability, proper testing, remain; but they are joined by a new urgency that development should be fast.

The term "Software Engineering" raises many problems, not least of which is how traditional professional engineering relates and reacts to computer scientists using the term. But, in any case, the distance between a reasonable definition of Engineering and "Software Engineering" as currently understood is too great. Software Engineering has embraced methodological and management ideas, but it is the principles and attitudes of engineering that are most worth applying to day-to-day software design.

1.9 Summary

I have looked at software design from seven different perspectives, each offering a different definition of program. From these, I have drawn seventeen principles that can be used to guide the practice of software design. For convenience the principles are repeated here.

THE MATHEMATICAL VIEW

Principle 1: The central question in software design is representation: How should this information, this procedure, this situation, be represented?

 

THE HARDWARE VIEW

Principle 2: Know (quantitatively) the capabilities of the hardware.

 

Principle 3: Understand the mechanisms working at levels of abstraction below your program (but don't mess with them).

 

THE LITERATURE VIEW

Principle 4. The text of the program itself - the source code - is the final authority.

 

THE ORGANIC GROWTH VIEW

Principle 5: Though it do little more than nothing, make the first prototype a framework for the final program, and get it running early.

 

 

Principle 6: Localize information

Principle 7: Software design methodology should minimize the damage caused by changes to the specification.

 

THE ARTISAN VIEW

Principle 8: See yourself as a craftsperson.

Principle 9: Manage knowledge effectively

Principle 10: In assigning programmers to work on a team project, aim to maximize each programmer's sense of ownership of their own code.

 

THE USER-CENTRED VIEW

Principle 11: Design for your users

Principle 12: Embrace standards

 

THE ENGINEERING VIEW

Principle 13: Analysis is essential to design.

Principle 14: Good design depends on understanding the problem and its context.

Principle 15: Seek an optimal solution according to well-defined criteria.

Principle 16: Exploit existing solutions.

Principle 17: Design Systematically

 

1.10 References and Further Reading

The references tell where to find the things explicitly refered to in the article, plus some other introductory references that are directly relevant. Although there may be many significant papers and books in a particular area, in general only one or two are given. These almost always have good lists of references themselves.

The Mathematical View

Hofstadter, D R, "Gödel, Escher, Bach: An Eternal Golden Braid", New York: Basic Books Inc., 1979. A readable, and very popular, introduction to ideas about mathematical systems, theorem proving, autonoma, and much else.

Gries, D, "The Science of Programming", New York: Springer-Verlag, 1981. One perspective on the mathematical view (scientific view?) of programming, that references much of the important work in the field. Highly recommended.

The Hardware View

Tanenbaum, A S, "Structured Computer Organization", Englewood Cliffs: Prentice-Hall Inc., 1976. Illustrative of the hardware-oriented perspective on computers.

The Literature View

B W Kernighan, P J Plauger, "The elements of programming style" 2nd ed., McGraw-Hill 1978

B W Kernighan, P J Plauger, "Software tools in Pascal", Addison-Wesley, 1981.

C Van Dyke (May 1987) "Taking 'Computer Literacy' Literally", Communications of the ACM, Vol 20, No 5, pp 366-374.

D E Knuth (May 1984), "Literature Programming", Computer Journal, Vol 27, No 2, pp 97-111.

J Bentley, (May 1986) "Programming Pearls: Literate Programming", Communications of the ACM, Vol 29 No 5, pp 364-369. (Also see the followups in June 1986 pp 471-483 and April 1987, pp 284-290.)

E Horowitz, "Fundamentals of Programming Languages", Rochville, ML: Computer Science Press, 1983.

B R Schneider (May 15, 1985), "Programs as essays", Datamation, Vol 30 No 10, pp 162-168.

The announcement for the Literate Programming column in Communications of the ACM was on page 593 of the July 1987 issue. The first article in the series (pp 594-599 of that issue) has D Hanson's solution to the problem that Knuth' solved in the Bentley reference above. Hanson's error was pointed out in the December 1987 issue, page 1000.

The Organic Growth View

F P Brooks (April 1987), "No silver bullet: Essences and Accidents of Software Engineering", IEEE Computer, Vol 20, No 4, pp 10-19.

The User-Centred View

D A Norman, "The Psychology of Everyday Things", Basic Books, 1988.

Apple Computer Inc (1985), "Inside Macintosh", Reading, MA: Addison-Wesley. Volume 1 has the original User Interface Guidelines. Later volumes supplement.

The Engineering View

Because of the subtle distinction this book makes between Engineering Software and Software Engineering, it is perhaps best here to include the best "conventional" Software Engineering textbooks I know of. At least, these are my favourites:

R Pressman, "Software Engineering: A Practitioner's Approach", McGraw-Hill, 2nd. ed. 1987.

B T Mynatt, "Software Engineering with Student Project Guidance", Englewood Cliffs, NJ: Prentice-Hall, 1990.

I Sommerville, "Software Engineering", Wokingham UK: Addison-Wesley, 4th ed., 1992.

J.A. McDermid (ed), "Software Engineer's Reference Book", Butterworth-Heinemann Ltd., 1991. Has pointers to many other sources.

The other references mentioned in this section are:

T. Capers Jones (January 1977), "Program Quality and Programmer Productivity", IBM Technical Report TR02.764.

T. Capers Jones (1986), "Programming Productivity", New York: McGraw-Hill, New York.