Dependability
Dependability
Dependability
ABSTRACT
Achieving software reliability takes many complementary techniques, directed
at the process or at the products. This survey summarizes some of the most
fruitful ideas.
1 OVERVIEW
Everyone who uses software or relies on devices or processes that use software
in other words, everyone has a natural interest in guarantees that
programs will perform properly. The following pages provide a review of
techniques to improve software quality.
There are many subcultures of software quality research, often seemingly
sealed off from each other; mentioning process-based approaches such as
CMMI to programming language technologists, or tests to people working on
proofs, can be as incongruous as bringing up Balanchine among baseball fans.
This survey disregards such established cultural fences and instead attempts to
include as many as possible of the relevant areas, on the assumption that
producing good software is hard enough that every little bit counts [60]. As
a result we will encounter techniques of very diverse kinds.
A note of warning to the reader seeking objectivity: I have not shied away
from including references easy to spot to my own work, with the
expectation (if a justification is needed) that it makes the result more lively
than a cold inspection limited to other peoples products and publications.
DEPENDABLE SOFTWARE
Defining reliability
The term software reliability itself lacks a universally accepted definition.
One could argue for taking it to cover all external quality factors such as ease
of use, efficiency and extendibility, and even internal quality factors such as
modularity. (The distinction, detailed in [57], is that external factors are the
properties, immediate or long-term, that affect companies and people
purchasing and using the software, whereas internal factors are perceptible
only to software developers although in the end they determine the attainment
of external factors.)
It is reasonable to retain a more restricted view in which reliability only
covers three external factors: correctness, robustness and security. This
doesnt imply that others are irrelevant; for example even the most correct,
robust and secure system can hardly be considered dependable if in practice it
takes ages to react to inputs, an efficiency problem. The same goes for ease of
use: many software disasters on record happened with systems that
implemented the right functions but made them available through error-prone
user interfaces. The reasons for limiting ourselves to the three factors listed
are, first, that including all others would turn this discussion into a survey of
essentially the whole of software engineering (see [33]); second, that the
techniques to achieve these three factors, although already very diverse, have
a certain kindred spirit, not shared by those for enhancing efficiency (like
performance optimization techniques), ease of use (like ergonomic design)
and other external and internal factors.
DEPENDABLE SOFTWARE
Deficiencies
In trying to ascertain the reliability of a software product or process we must
often like a detective or a fire prevention engineer adopt a negative
mindset and look for sources of violation of reliability properties. The
accepted terminology here distinguishes three levels:
A failure is a malfunction of the software. Note that this term does not
directly apply to products other than executable code.
A program that does not initialize one of its variables along a particular
path is suspicious, independently of any of its properties vis--vis the
fulfillment of its specification.
A poorly written user manual may not explicitly violate the prescriptions
of another project document, but is problematic all the same.
This observation leads to distinguishing two complementary kinds of
reliability assessment, verification and validation, often combined in the
abbreviation V&V:
DEPENDABLE SOFTWARE
3 CLASSIFYING APPROACHES
One of the reasons for the diversity of approaches to software quality is the
multiplicity of problems they address. The following table shows a list of
criteria, essentially orthogonal, for classifying them.
Process
Product
Manual
Tool-supported
Technology-neutral
Technology-specific
vs
Product- or phase-specific
Dynamic (requires execution)
Informal
Mathematical
Complete (guarantee)
Free
Commercial
3 CLASSIFYING APPROACHES
DEPENDABLE SOFTWARE
4 PROCESS-BASED APPROACHES
We start with the least technical approaches, emphasizing management
procedures and organizational techniques.
Lifecycle models
One of the defining acts of software engineering was the recognition of the
separate activities involved, in the form of lifecycle models that prescribe a
certain order of tasks (see the figure on the adjacent page). The initial model
is the so-called waterfall [11], still used as a reference for discussions of the
software process although no longer recommended for literal application.
Variants include:
The V model which retains the sequential approach of the waterfall but
divides the process into two parts, the branches of the V; activities
along the first branch are for development, those in the second branch are
for verification and validation, each applied to the results of one of the
steps along the first branch.
4 PROCESS-BASED APPROACHES
Waterfall
Cluster
V-shaped
10
DEPENDABLE SOFTWARE
In the Cluster model, the presence, for each cluster, of the generalization
task to prepare for reuse.
Also in the Cluster model, the use of a seamless and reversible approach
which unifies the methods, tools, techniques and notations that help
throughout the software process, rather than exaggerate them. (The textbook
counter-example here is the use of UML for analysis and design [56].)
Organizational standards
Another process-related set of developments has had a major effect, largely
beneficial, on some segments of the industry. In the early 1990s the US
Department of Defense, concerned with the need to assess its suppliers
software capabilities and to establish consistent standards, entrusted the
Software Engineering Institute with the task of developing a Capability
Maturity Model, whose current incarnation, CMMI [74] (the I is for
Integration) provides a collection of standards applicable to various
disciplines, rather than a single model for software. Largely independently, the
International Standard Organization has produced a set of software-oriented
variants of its 9000-series quality standards, which share a number of
properties with CMMI. The present discussion is based on CMMI.
4 PROCESS-BASED APPROACHES
Beyond its original target community, CMM and CMMI have been the
catalyst for one of the major phenomena of the IT industry starting in the midnineties: the development of offshore software production, especially in India
[63]. CMMI qualification provides suppliers of outsourcing development
services with quality standards and the associated possibility of independent
certification, without which customers would not be have known how to trust
distant, initially unknown contractors.
CMMI is (in the earlier classification) product-neutral, phase-neutral and
technology-neutral. In its application to software it is intended only to
determine how well an organization controls its development process by
defining and documenting it, recording and assessing how it is applied in
practice, and working to improve it. It doesnt prescribe what the process
should be, only how much you are on top of it. You could presumably be
developing in PL/I on IBM 370 and get CMMI qualification.
CMMI assesses both the capability level of individual process areas in
(such as software) in an organization, and the maturity of an organization as a
whole. It distinguishes five levels of increasing maturity:
Performed: projects happen and results get produced, but there is little
control and no reproducibility; the process is essentially reactive.
Managed: processes are clearly defined for individual projects, but not
for the organization as a whole. They remain largely reactive.
11
12
DEPENDABLE SOFTWARE
Extreme programming
The Extreme Programming movement [6] is a reaction against precisely the
kinds of lifecycle models and process-oriented approaches just reviewed. XP
(as it is also called) emphasizes instead the primacy of code. Some of the
principal ideas include:
Test-driven development.
Some of these practices are clearly beneficial to quality but were developed
prior to XP, in particular short release cycles (Microsofts daily build as
described in 1995 by Cusumano and Shelby [19], see also [54]) and the use of
frequent testing as part of development (see e.g. quality first [55]). Those
really specific to XP are of limited interest (while sometimes a good practice,
pair programming cannot be imposed indiscriminately, both because it doesnt
work for some people and because those who find it useful may not find it
useful all the time) or, in the case of tests viewed as a replacement for
specifications, downright detrimental. See [75] and [64] for critiques of
the approach.
Code inspections
A long-established quality practice is the inspection, also known as review: a
session designed to examine a certain software element with the aim of finding
flaws. The most common form is code inspection, but the process can be
applied to any kind of software engineering product. Rules include:
4 PROCESS-BASED APPROACHES
The sole goal is to identify deficiencies and confirm that they are indeed
deficiencies; correction is not part of the process and should not be
attempted during the meeting.
Code inspections can help avoid errors, but to assess their usefulness one must
compare the costs with those of running automated tools that can catch some
of the same problems without human intervention; static analyzers, discussed
below, are an example.
Some companies have institutionalized the rule that no developer may
check in code (integrate it into the repository for a current or future product)
without approval by one other developer, a limited form of code inspection
that has a clearly beneficial effect by forcing the original developer to convince
at least one other team member of the suitability of the contribution.
Open-source processes
A generalization of the idea of code inspection is the frequent assertion, by
members of the open-source community, that the open-source process
dramatically improves quality by enabling many people to take a critical look
at the software text; some have gone so far as to state that given enough eyes,
all bugs are shallow [73].
As with many of the other techniques reviewed, we may see in this idea
a beneficial contribution, but not a panacea. John Viega gives [78] the example
of a widely used security program in which in the past two years, several very
subtle buffer overflow problems have been found Almost all had been in the
code for years, even though it had been examined many times by both hackers
and security auditors One tool was able to identify one of the problems as
potentially exploitable, but researchers examined the code thoroughly and
came to the conclusion that there was no way the problem could be exploited.
(The last observation is anecdotal evidence for the above observation that tools
such as static analyzers are potentially superior to human analysis.)
While is no evidence that open-source software as a whole is better (or
worse) than commercial software, and no absolute rule should be expected if
only because of the wide variety of products and processes on both sides, it is
clear that more eyes potentially see more bugs.
13
14
DEPENDABLE SOFTWARE
Requirements engineering
In areas such as embedded systems, many serious software failures have been
traced [45] to inadequate requirements rather than to deficiencies introduced
in later phases. Systematic techniques for requirements analysis are available
[76] [40] to improve this critical task of collecting customer wishes and
translating them into a form that can serve as a basis for a software project.
Design patterns
A process-related advance that has had a strong beneficial effect on software
development is the emergence of design patterns [32]. A pattern is an
architectural scheme that has been recognized as fruitful through frequent use
in applications, and for which a precise description exists according to a
standard format. Patterns provide a common vocabulary to developers, hence
simplifying design discussions, and enable them to benefit from the collective
wisdom of their predecessors.
A (minority) view of patterns [62] [65] understands them as a first step
towards the technique discussed next, reusable components. Patterns, in this
interpretation, suffer from the limitation that each developer must manually
insert the corresponding solutions into the architecture of every applicable
system. If instead it is possible to turn the pattern into a reusable component,
developers can directly reuse the corresponding solution through an API
(Abstract Program Interface). The observation here is that it is better to reuse
than to redo. Investigations [65] suggest that with the help of appropriate
programming language constructs up to two thirds of common design patterns
can be thus componentized.
Trusted components
Quality improvement techniques, whether they emphasize the process or the
product, are only as good as their actual application by programmers. The
magnitude of the necessary education effort is enough to temper any hope of
major short-term improvements, especially given that many programmers
have not had the benefit of a formal computer science education to start with.
4 PROCESS-BASED APPROACHES
15
16
DEPENDABLE SOFTWARE
Configuration management
Configuration management is a both practice (for the software developer) and
a service (from the supporting tools), so it could in principle be classified
under process as well as under product. It belongs more properly to the
latter category since its tools that make configuration management realistic;
applied as a pure organizational practice without good tool support, it quickly
becomes tedious and ceases being applied.
Configuration management may be defined as the systematic collecting
and registering of project elements, including in particular the ability to:
17
18
DEPENDABLE SOFTWARE
8), and to compare them with values on record. While not necessarily meaningful
in isolation, such measures elements are a useful control tool for the manager;
they are in line with the CMMIs insistence that an organization can only reach
the higher levels of process maturity (4 and 5) by moving from the qualitative to
the quantitative, and should be part of the data collected for such an effort.
Static analyzers
Static analyzers are another important category of tools, increasingly
integrated in development environments, whose purpose is to examine the
software text for deficiencies. They lie somewhere between type checkers
(themselves integrated in compilers) and full program provers, and will be
studied below (page 26) after the discussion of proofs.
6 PROGRAMMING LANGUAGES
6 PROGRAMMING LANGUAGES
The evolution of programming languages plays its part in the search for more
reliable software. High-level languages contribute both positively, by
providing higher levels of expression through advanced constructs freeing the
programmer (in the same spirit as modern IDEs) from mundane, repetitive or
irrelevant tasks, and negatively, by ruling out certain potentially unsafe
constructs and, as a result, eradicate entire classes of bugs at the source.
The realization that programming language constructs could exert a
major influence on software quality both through what they offer and what
they forbid dates back to structured programming [22] [20] which, in the early
seventies, led to rejecting the goto as a control structure in favor of more
expressive constructs sequence, conditional, loop, recursion. The next
major step was object-oriented programming, introducing a full new set of
abstractions, in particular the notion of class, providing decomposition based
on object types rather than individual operations, and techniques of
inheritance and genericity.
In both cases the benefit comes largely from being able to reason less
operationally about software. A software text represents many possible
executions, so many in fact that it is hard to understand the program and
hence to get it right by thinking in terms of what happens at execution [22].
Both structured and object-oriented techniques make it possible to limit such
operational thinking and instead understand the abstract properties of future
run-time behaviors by applying the usual rules of logical reasoning.
In drawing the list of programming languages most important
contributions to quality, we must indeed put at the top all the mechanisms that
have to do with structure. With ever larger programs addressing ever more
ambitious goals, the production and maintenance of reliable software requires
safe and powerful modular decomposition facilities. Particularly noteworthy are:
As pointed out, the class mechanism, which provides a general basis for
stable modules with a clear role in the overall architecture.
19
20
DEPENDABLE SOFTWARE
Proofs
Perhaps the principal difference between mathematics and engineering is that
only mathematics allows providing absolute guarantees. Given the proper
axioms, I can assert with total confidence that two plus two equals four. But if
I want to drive to Berne the best assurance I can get that my car will not break
down is a probability. I know its higher than if I just drive it to the suburbs,
and lower than if my goal were Prague, Alma-Ata, Peking or Bombay; I can
make it higher by buying a new, better car; but it will never be one. Even with
the highest attention to quality and maintenance, physical products will
occasionally fail.
Under appropriate assumptions, a program is like a mathematical
proposition rather than a material device: any general property of the program
stating that all executions of the program will achieve a certain goal, or that
at least one possible execution will is either true or false, and whether it is
true or not is entirely determined by the text of the program, at least if we
assume correct functioning of the hardware and of other software elements
needed to carry out program execution (compiler, run-time system, operating
system). Another way of expressing this observation is that a programming
language is similar to a mathematical theory, in which certain propositions are
true and others false, as determined by the axioms and inference rules.
In principle, then, it should be possible to prove or disprove properties of
programs, in particular correctness, robustness and security properties, using
the same rigorous techniques as in the proofs of any mathematical theorem.
This assumes overcoming a number of technical difficulties:
21
22
DEPENDABLE SOFTWARE
Even if the language, the context and the properties of interest are fully
specified semantically and the properties relevant, the proof process
remains a challenge. It cannot in any case be performed manually, since
even the proof of a few properties of a moderately sized programs quickly
reaches into the thousands of proof steps. Fully automated proofs are, on
the other hand, generally not possible. Despite considerable advances in
computer-assisted proof technology (for programs as well as other
applications) significant proofs still require considerable user interaction
and expert knowledge.
Of course the effort may well be worthwhile, especially in two cases: lifecritical systems in transportation and defense to which, indeed, much proof
work has been directed; and reusable components, for which the effort is
justified as explained in the discussion of Trusted Components above by
the scaling-up effect of reuse.
Here are some of the basic ideas about how proofs work. A typical
program element to prove would be, in Eiffel notation
decrement
-- Decrease counter by one.
require
counter > 0
do
counter := counter 1
ensure
counter = old counter 1
counter >= 0
end
This has a program body, the do clause, and two assertions, a precondition
introduced by require and a postcondition introduced by ensure and
consisting of two subclauses implicitly connected by an and. Assertions are
essentially boolean expressions of the language with the possibility, in a
postcondition, of using the old notation to refer to values on entry: here the
first subclause of the postcondition states that the value of counter will have
been decreased by one after execution of the do clause.
Program proofs deal with such annotated programs, also called
contracted programs (see section 8 below). The annotations remind us that
proofs and other software quality assurance technique can never give us
absolute guarantees of quality: we can never say that a program is correct,
only assess it whether through rigorous techniques like proofs or using
more partial ones such as those reviewed next relatively to explicitly stated
properties, expressed here through assertions integrated in the program text.
From a programmers viewpoint the above extract is simply the text of a
routine to be executed, with some extra annotations, the precondition and
postcondition, expressing properties to be satisfied before and after. But for
proof purposes this text is a theorem, asserting that whenever the body (the do
clause with its assignment instruction) is executed with the precondition
satisfied it will terminate in such a way that the postcondition is satisfied.
This theorem appears to hold trivially but even before addressing the
concern noted above that computer integers are not quite the same as
mathematical integers proving it requires the proper mathematical
framework. The basic rule of axiomatic semantics (or Hoare semantics [37])
covering such cases is the assignment axiom, which for any variable x and
expression e states that the following holds
23
24
DEPENDABLE SOFTWARE
you need, in this general approach, to introduce a new assertion called the loop
invariant and an integer expression called the loop variant. The invariant is
a weakened form of the desired postcondition, which serves as approximation
of the final goal; for example if the goal is to compute the maximum of a set
of values, the invariant will be Result is the maximum of the values processed
so far. The advantage of the invariant is that it is possible both to:
Ensure the invariant through initialization (the from clause in the above
notation); in the example the invariant will be trivially true if we start with
just one value and set Result to that value.
Preserve the invariant through one iteration of the loop body (the loop
clause); in the example it suffices to extend the set of processed values by
one element v and execute if v > Result then Result := v end.
If indeed a loop possesses such an invariant and its execution terminates, then
on exit the invariant will still hold (since it was ensured by the initialization
and preserved by all the loop iterations), together with the Exit condition. The
combination of these two assertions gives the postcondition of the loop. Seen
the other way around, if we started from a desired postcondition and weakened
it to get an invariant, we will obtain a correct program. In the example, if the
exit condition states that we have processed all values of interest, combining
this property with the invariant Result is the maximum of the values
processed so far tells us that Result is the maximum of all values.
Such reasoning is only interesting if the loop execution actually
terminates; this is where the loop variant comes in. It is an integer expression
which must have a non-negative value after the Initialization and decrease,
while remaining non-negative, whenever the Body is executed with the Exit
condition not satisfied. The existence of such an expression is enough to
guarantee termination since a non-negative integer value cannot decrease
forever. In the example a variant is N i where N is the total number of values
being considered for the maximum (the proof assumes a finite set) and i the
number of values processed.
Axioms and inference rules similarly exist for other constructs of
programming languages, becoming, as noted, more intricate as one moves on
to more advanced mechanisms.
25
26
DEPENDABLE SOFTWARE
P until Q: Q will hold at some point in the future, and until then P will hold.
Regardless of the kind of programs and properties being targeted, there are
two approaches to producing program proofs. The analytic method takes
programs as they exist, then after equipping them with assertions, either
manually or with some automated aid as noted above, attempts the proof. The
constructive method [24] [2] [68] integrates the proof process in the software
construction process, often using successive refinements to go from
specification to implementation through a sequence of transformations, each
proved to preserve correctness, and integrating more practical constraints at
every step.
Proof technology has had some notable successes, including in industrial
systems (and in hardware design), but until recently has remained beyond the
reach of most software projects.
Static analysis
If hoping for a proof covering all the correctness, reliability and security
properties of potential interest is often too ambitious, the problem becomes
more approachable if we settle for a subset of these properties a subset that
may be very partial but very interesting. For example being able to determine
that no buffer overflow can ever arise in a certain program in other words,
to provide a firm guarantee, through analysis of the program text, that every
index used at run time to access an item in an array or a character in a string
will be within the defined bounds is of great practical value since this rules
out a whole class of security attacks.
Static analysis is the tool-supported analysis of software texts for the
purpose of assessing specific quality properties. Being static, it requires no
execution and hence can in principle be applied to software products other
than code. Proofs are a special case, the most far-reaching, but other static
analysis techniques are available.
Static analysis tools such as PREfix [72] have been regularly applied for
several years to new versions of the Windows code base and have avoided
many potential errors.
One of the issues of static analysis is the occurrence of false alarms:
inconsistency reports that, on inspection, do not reveal any actual error. This
was the weak point of older static analyzers, such as the widely known Lint tool
which complements the type checking of C compilers: for a large program they
can easily swamp their users under thousand of messages, most of them
spurious, but requiring a manual walkthrough to sort out the good from the bad.
(In the search for errors, of course, the good is what otherwise would be
considered the bad: evidence of wrongdoing.) Progress in static analysis has
been successful in considerably reducing the occurrence of false alarms.
The popularity of static analysis is growing; the current trend is to extend
the reach of static analysis tools ever further towards program proofs. Two
examples are:
27
28
DEPENDABLE SOFTWARE
ESC-Java [21] and, more recently, the Boogie analyzer [4] make program
proving less obtrusive by incrementally extending the kind of diagnostics
with which programmers are familiar, for example type errors, to more
advanced checks such as the impossibility to guarantee that an invariant
is preserved.
Model checking
The model checking approach to verification [36] [17] [3] is static, like proofs
and static analysis, but provides a natural link to the dynamic techniques
(testing) studied below. The inherent limitation of tests is that they can never
be exhaustive; for any significant system in fact, even for toy examples
the number of possible cases skyrockets into the combinatorial stratosphere,
where the orders of magnitude invite lyrical comparisons with the number of
particles in the universe.
The useful measure is the number of possible states of a program. The
notion of state was implicit in the earlier discussion of assertions. A state is
simply a snapshot of the program execution, as could be observed, if we stop
that execution, by looking up the contents of the programs memory, or more
realistically by using the debugger to examine the values of the programs
variables. Indeed it is the combination of all the variables values that
determines the state. With every 64-bit integer variable potentially having 264
values, it is not surprising that the estimates quickly go galactic.
Model checking attempts exhaustive analysis of program states anyway
by performing predicate abstraction. The idea is to simplify the program by
replacing all expressions by boolean expressions (predicates), with only two
possible values, so that the size of the state space decreases dramatically; it
will still be large, but the power of modern computers, together with smart
algorithms, can make its exploration tractable. Then to determine that a
desired property holds for example, a security property such as the absence
of buffer overflows, or a timing property such as the absence of deadlock it
suffices to evaluate the corresponding assertion in all of the abstract states and,
if a violation of that assertion (or counter-example) is found, to check that it
also arises in the original program.
Not find any violations, in which case it proves there was none in the
original program.
29
30
DEPENDABLE SOFTWARE
8 DESIGN BY CONTRACT
The goal of developing software to support full proofs of correctness
properties is, as noted, desirable but still unrealistic for most projects. Even a
short brush with program proving methods suggests, however, that more rigor
can be highly beneficial to software quality. The techniques of Design by
Contract go in this direction and deliver part of the corresponding benefits
without requiring the full formality of proof-directed development.
The discussion of proofs introduced Eiffel notations such as
require assertion
-- A routine precondition
ensure assertion
-- A routine postcondition
The discipline of Design by Contract [53] [57] [67] gives a central role to these
mechanisms in software development. It views the overall process of building
a system as defining a multitude of relationships between client and
supplier modules, each specified through a contract in the same manner as
relationships between companies in the commercial world.
The benefits of such a method, if carried systematically, extend
throughout the lifecycle, supporting the goal of seamlessness discussed earlier:
8 DESIGN BY CONTRACT
31
32
DEPENDABLE SOFTWARE
9 TESTING
Testing [70] [8] is the most widely used form of program verification, and still
for many teams essentially the only one. In academic circles testing has long
suffered from a famous comment [23] that (because of the astronomical
number of possible states) testing can only show the presence of bugs, but
never to show their absence. In retrospect its hard to find a rational
explanation for why this comment ever detracted anyone from the importance
of tests, since it in no way disproves the usefulness of testing: finding bugs is
a very important task of software development. All it indicates is that we
should understand that finding bugs is indeed the sole purpose of testing, and
not delude ourselves that test results directly reflect the level of quality of a
product under development.
Components of a test
Successful testing relies on a test plan: a strategy, expressed in a document,
describing choices for the tasks of the testing process. These tasks include:
Instrumenting the software to run the tests (rather than perform its normal
operation, or in addition to it); this is known as building a test harness,
which may involve test drivers to solicit specific parts to be tested, and
stubs to stand for parts of the system that will not be tested but need a
placeholder when other parts call them.
Recording the test data (test cases, oracles, outputs) for future re-testing
of the system, in particular regression testing, the task of verifying that
previously corrected errors have not reappeared.
9 TESTING
Kinds of test
One may classify tests with respect to their scope (this was used in the earlier
description of the V model of the lifecycle):
Observing the state of the art in software testing suggests that four issues are
critical: managing the test process; estimating the quality of test suites;
devising oracles; and the toughest generating test cases automatically.
33
34
DEPENDABLE SOFTWARE
9 TESTING
Defining oracles
An oracle, allowing interpretation of testing results, provides a decision criterion
for accepting or rejecting the result of a test. The preparation of oracles can be as
much work as the rest of the test plan. The best solution that can be recommended
is to rely on contracts: any functional property of a software system (with the
possible exception of some user-interface properties for which human assessment
may be required) can be expressed as a routine postcondition or a class invariant.
These assertions can be included in the test harness, but it is of course
best, as noted in the discussion of Design by Contract, to make them an
integral part of the software to be tested as it is developed; they will then
provide the other benefits cited, such as aid to design and built-in
documentation, and will facilitate regression testing.
35
36
DEPENDABLE SOFTWARE
10 CONCLUSION
This survey has taken a broad sweep across many techniques that all have
something to contribute to the aim of software reliability. While it has stayed
away from the gloomy picture of the state of the industry which seems to be
de rigueur in discussions of this topic, and is not justified given the
considerable amount of quality-enhancing ideas, techniques and tools that are
available today and the considerable amount of good work currently in
progress, it cannot fail to note as a conclusion that the industry could do much
more to take advantage of all these efforts and results.
There is not enough of a reliability culture in the software world; too
often, the order of concerns is cost, then deadlines, then quality. It is time to
reassess priorities.
Acknowledgments
The material in this chapter derives in part from the slides for an ETH industry
course on Testing and Software Quality Assurance prepared with the help of
Ilinca Ciupa, Andreas Leitner and Bernd Schoeller. The discussion of CMMI
benefited from the work of Peter Kolb in the preparation of another ETH
course, Software Engineering for Outsourced and Offshored Development.
Bernd Schoeller and Ilinca Ciupa provided important comments on the draft.
Design by Contract is a trademark of Eiffel Software.
The context for this survey was provided by the Hasler Foundations
grant for our SCOOP work in the DICS project. We are very grateful for the
opportunities that the grant and the project have provided, in particular for the
experience gained in the two DICS workshops in 2004 and 2005.
REFERENCES
Note: All URLs listed were active in April 2006.
[1] Algirdas Avizienis, Jean-Claude Laprie and Brian Randell: Fundamental
Concepts of Dependability, in Proceedings of Third Information Survivability
Report, October 2000, pages 7-12, available among other places at
citeseer.ist.psu.edu/article/avizienis01fundamental.html.
[2] Ralph Back: A Calculus of Refinements for Program Derivations, in Acta
Informatica, vol. 25, 1988, pages 593-624, available at crest.cs.abo.fi/publications/
public/1988/ACalculusOfRefinementsForProgramDerivationsA.pdf.
37
10 REFERENCES
documentation
at
38
DEPENDABLE SOFTWARE
[14] T.Y. Chen, H. Leung and I.K. Mak: Adaptive random testing, in Advances
in Science - ASIAN 2004: Higher-Level Decision Making, 9th Asian
Computing Science Conference, ed. Michael J. Maher, Lecture Notes in
Computer Science 3321, Springer-Verlag, 2004, available at
tinyurl.com/lpxn5.
[15] Ilinca Ciupa and Andreas Leitner: Automated Testing Based on Design by
Contract, in Proceedings of Net.ObjectsDays 2005, 6th Annual Conference on
Object-Oriented and Internet-Based Technologies, Concepts and
Applications for a Networked World, 2005, pages 545-557, available at
se.ethz.ch/people/ciupa/papers/soqua05.pdf. See also AutoTest page at
se.ethz.ch/research/autotest.
[16] Ilinca Ciupa, Andreas Leitner, Manuel Oriol and Bertrand Meyer: Object
Distance and its Application to Adaptive Random testing of Object-Oriented
Programs, submitted for publication, 2006, available at se.ethz.ch/~meyer/
publications/testing/object_distance.pdf.
[17] Edmund M. Clarke Jr., Orna Grumberg and Doron A. Peled: Model
Checking, MIT Press, 1999.
[18] Patrick Cousot: Verification by Abstract Interpretation, in International
Symposium on Verification Theory & Practice Honoring Zohar Mannas 64th
Birthday, ed. Nachum Dershowitz, Lecture Notes in Computer Science 2772,
Springer-Verlag, 2003, pages 243-268.
[19] Michael Cusumano and Richard Selby: Microsoft Secrets, The Free
Press, 1995.
[20] Ole-Johan Dahl, Edsger W. Dijkstra and C.A.R. Hoare: Structured
Programming, Academic Press, 1971.
[21] David L. Detlefs, K. Rustan M. Leino, Greg Nelson, and James B. Saxe:
Extended Static Checking, Research Report 159, Compaq Systems Research
Center, December 1998, available at ftp://gatekeeper.research.compaq.com/
pub/DEC/SRC/research-reports/SRC-159.pdf.
[22] Edsger W. Dijkstra: Go To Statement Considered Harmful, in
Communications of the ACM, Vol. 11, No. 3, March 1968, pages 147-148,
available at www.acm.org/classics/oct95/.
[23] Edsger W. Dijkstra: Notes on Structured Programming, in [20]; original
typescript available at www.cs.utexas.edu/users/EWD/ewd02xx/EWD249.PDF.
[24] Edsger W. Dijkstra: A Discipline of Programming, Prentice Hall, 1978.
[25] Brian J. Dreger: Function Point Analysis, Prentice Hall, 1989.
10 REFERENCES
[26] Paul Dubois, Mark Howard, Bertrand Meyer, Michael Schweitzer and
Emmanuel Stapf: From Calls to Agents, in Journal of Object-Oriented
Programming (JOOP), vol. 12, no. 6, September 1999, available at
se.ethz.ch/~meyer/publications/joop/agent.pdf.
[27] Eclipse pages at www.eclipse.org.
[28] ECMA/ISO: Eiffel: Analysis, Design and Programming Language,
standard ECMA 367, accepted in April 2006 as ISO standard, available at
www.ecma-international.org/publications/standards/Ecma-367.htm.
[29] Eiffel open-source development site at eiffelsoftware.origo.ethz.ch/
index.php/Main_Page.
[30] Eiffel Software: EiffelStudio documentation, online at eiffel.com.
[31] Michael D. Ernst, J. Cockrell, William G. Griswold and David Notkin:
Dynamically Discovering Likely Program Invariants to Support Program
Evolution, in IEEE Transactions on Software Engineering, vol. 27, no. 2,
February 2001, pages 1-25, available at pag.csail.mit.edu/~mernst/pubs/
invariants-tse2001.pdf.
[32] Erich Gamma, Richard Helms, Ralph Johnson and John Vlissides:
Design Patterns, Addison-Wesley, 1994.
[33] Carlo Ghezzi, Mehdi Jazayeri, Dino Mandrioli, Software Engineering,
2nd edition, Prentice Hall, 2003.
[34] Richard Hamlet: Random Testing, in Encyclopedia of Software
Engineering, ed. J. J. Marciniak, 1994, available at tinyurl.com/rcjxg.
[35] Brian Henderson-Sellers: Object-Oriented Metrics: Measures of
Complexity, Prentice Hall, 1995.
[36] Thomas A. Henzinger, Xavier Nicollin, Joseph Sifakis and Sergio
Yovine: Symbolic Model Checking for Real-Time Systems, in Logic in
Computer Science, Proceedings of 7th Symposium in Logics for Computer
Science, Santa Cruz, California, 1992, pages 394-406, available at
tinyurl.com/lb5fm.
[37] C.A.R. Hoare: An axiomatic basis for computer programming, in
Communications of the ACM, Vol. 12, no. 10, October 1969, pages 576 - 580,
available at tinyurl.com/ory2s.
[38] C.A.R. Hoare and Jayadev Misra: Verified Software: Theories, Tools,
Experiments, Vision of a Grand Challenge Project, October 2005, foundation
paper for the VSTTE conference [77], available at vstte.ethz.ch/pdfs/vsttehoare-misra.pdf.
[39] IFIP Working Group 10.4 on dependable computing and fault tolerance:
home page at www.dependability.org.
39
40
DEPENDABLE SOFTWARE
10 REFERENCES
[56] Bertrand Meyer: UML: The Positive Spin, in American Programmer, 1997,
available at archive.eiffel.com/doc/manuals/technology/bmarticles/uml/page.html.
[57] Bertrand Meyer: Object-Oriented Software Construction, 2nd edition,
Prentice Hall, 1997.
[58] Bertrand Meyer, Christine Mingins and Heinz Schmidt: Providing
Trusted Components to the Industry, in Computer (IEEE), vol. 31, no. 5, May
1998, pages 104-105, available at se.ethz.ch/~meyer/publications/computer/
trusted.pdf.
[59] Bertrand Meyer: The Role of Object-Oriented Metrics, in Computer
(IEEE), vol. 31, no. 11, November 1998, pages 123-125, available at
se.ethz.ch/~meyer/publications/computer/metrics.
[60] Bertrand Meyer, Every Little Bit Counts: Towards Reliable Software, in
Computer (IEEE_, vol. 32, no. 11, November 1999, pages 131-133, available
at se.ethz.ch/~meyer/publications/computer/reliable.pdf.
[61] Bertrand Meyer: The Grand Challenge of Trusted Components, in ICSE
25 (International Conference on Software Engineering, Portland, Oregon,
May 2003), IEEE Computer Press, 2003.
[62] Bertrand Meyer: The Power of Abstraction, Reuse and Simplicity: An
Object-Oriented Library for Event-Driven Design, in From ObjectOrientation to Formal Methods: Essays in Memory of Ole-Johan Dahl, eds.
Olaf Owe, Stein Krogdahl, Tom Lyche, Lecture Notes in Computer Science
2635, Springer-Verlag, 2004, pages 236-271, available at se.ethz.ch/~meyer/
publications/lncs/events.pdf.
[63] Bertrand Meyer: Offshore Development: The Unspoken Revolution in
Software Engineering, in Computer (IEEE), January 2006, pages 122-124,
available at se.ethz.ch/~meyer/publications/computer/outsourcing.pdf.
[64] Bertrand Meyer: What will remain of Extreme Programming?,
in EiffelWorld, Vol. 5, no. 2, February 2006, available at www.eiffel.com/
general/monthly_column/2006/February.html.
[65] Bertrand Meyer and Karine Arnout: Componentization: the Visitor
Example, to appear in Computer (IEEE), 2006, draft available at
se.ethz.ch/~meyer/publications/computer/visitor.pdf.
[66] Microsoft: Visual Studio pages at msdn.microsoft.com/vstudio.
[67] Richard Mitchell and Jim McKim: Design by Contract by Example,
Addison-Wesley, 2001.
[68] Carroll Morgan: Programming from Specifications, 2nd edition, Prentice
Hall, 1994, available at web.comlab.ox.ac.uk/oucl/publications/books/PfS/.
[69] John Musa: Software Reliability Engineering, 2nd edition, McGraw-Hill, 1998.
41
42
DEPENDABLE SOFTWARE
[70] Glenford J. Myers, Corey Sandler, Tom Badgett and Todd M. Thomas:
The Art of Software Testing, 2nd edition, Wiley, 2004.
[71] Jeff Offutt: Mutation testing papers at www.ise.gmu.edu/~ofut/rsrch/
mut.html.
[72] John Pincus: presentations (mostly PowerPoint slides) on PREfix and
PREfast at research.microsoft.com/users/jpincus/.
[73] Eric Raymond: The Cathedral and the Bazaar: Musings on Linux and
Open Source by an Accidental Revolutionary, O Reilly, 1999; earlier version
available at www.firstmonday.org/issues/issue3_3/raymond/.
[74] Software Engineering Institute, CMMI site, available at
www.sei.cmu.edu/cmmi.
[75] Matt Stephens and Doug Rosenberg: Extreme Programming Refactored:
The Case Against XP, aPress, 2003.
[76] Axel van Lamsweerde: Goal-Oriented Requirements Engineering: A
Guided Tour, in Proceedings of the 5th IEEE International Symposium on
Requirements Engineering, August 2001, available at tinyurl.com/mscpj.
[77] Verified Software: Theories, Tools, Experiments: International IFIP
conference, ETH Zurich, October 2005, see VSTTE conference site at
vstte.ethz.ch.
[78] John Viega: The Myth of Open-Source Security, 2000, available at
www.developer.com/tech/article.php/626641; follow-up article, Open-Source
Security:
Still
at
Myth,
September
2004,
available
at
www.onlamp.com/pub/a/security/2004/09/16/open_source_security_myths.html.
[79] Jeffrey M. Voas and Gary McGraw: Software Fault Injection: Inoculating
Programs Against Errors, Wiley, 1998.
[80] Jos Warmer and Anneke Kleppe: The Object Constraint Language:
Getting Your Models Ready for MDA, 2nd edition, Addison-Wesley, 2003.
[81] Elaine J. Weyuker and Bingchiang Jeng: Analyzing Partition Testing
Strategies, in IEEE Transactions on Software Engineering, vol. 17, no. 9, July
1991, pp. 97-108.
[82] Wikipedia: entry Mars Climate Orbiter, available at
en.wikipedia.org/wiki/Mars_Climate_Orbiter.
[83] Edward Yourdon: When Good Enough Software Is Best, in Software
(IEEE), vol. 12, no. 3, May 1995, pages 79-81.