$11.6 CONTRACTING FOR SOFTWARE RELIABILITY 341 More precisely,it depends on what you want.You may decide to treat assertions purely as comments,with no effect on the software's execution;then a run-time assertion violation will remain undetected.But it is also possible to use assertions to check that everything goes according to plan;then during execution the environment will automatically monitor that all assertions hold when they should,and if one does not it will trigger an exception,usually terminating execution and printing a message indicating clearly what happened.(It is also possible to include an exception handling clause that will try to recover from the exception and continue execution;exception handling is discussed in detail in the next chapter.)To specify the policy that you want-no assertion checking. or assertion monitoring at one of various possible levels-you will use a compilation option,which you can set separately for each class. See"Monitoring The full details of run-time assertion monitoring do appear later in this chapter.But assertions at rim it would be a mistake to attach too much importance to this aspect at this stage (one of the time”,page393. reasons why you were warned earlier not to think too much about the C notion of assertion if that has been your only exposure to the concept).Other aspects of assertions demand our attention first.We have only started to see assertions as a technique to help us get our software right in the first place;we still have much to discover of their methodological role as built-in guardians ofreliability.The question of what happens if we do fail (in particular if an assertion,in spite of all our efforts,is not satisfied at some execution instant)is important too,but only after we have done all we could to prevent it from arising. So(although it is never bad to think ahead)you do not need at this point to be too preoccupied by such questions as the possible performance penalty implied by the old construct.Must the run-time system preserve values before we start a routine,just to be able to evaluate an old expression appearing in the postcondition?It depends:in some circumstances(for example testing and debugging)it will indeed be useful to evaluate assertions;in others(for example production runs of fully validated systems)you can treat them as mere annotations to the software text. All that counts for the next few sections is the methodological contribution of assertions,and of the associated method of Design by Contract:as a conceptual tool for analysis,design,implementation and documentation,helping us to build software in which reliability is built-in,rather than achieved or attempted after the fact through debugging;in Mills's terms,enabling us to build correct programs and know it. 11.6 CONTRACTING FOR SOFTWARE RELIABILITY Defining a precondition and a postcondition for a routine is a way to define a contract that binds the routine and its callers. Rights and obligations By associating clauses require pre and ensure post with a routine r,the class tells its clients: "If you promise to call r with pre satisfied then I,in return,promise to deliver a final state in which post is satisfied
§11.6 CONTRACTING FOR SOFTWARE RELIABILITY 341 More precisely, it depends on what you want. You may decide to treat assertions purely as comments, with no effect on the software’s execution; then a run-time assertion violation will remain undetected. But it is also possible to use assertions to check that everything goes according to plan; then during execution the environment will automatically monitor that all assertions hold when they should, and if one does not it will trigger an exception, usually terminating execution and printing a message indicating clearly what happened. (It is also possible to include an exception handling clause that will try to recover from the exception and continue execution; exception handling is discussed in detail in the next chapter.) To specify the policy that you want — no assertion checking, or assertion monitoring at one of various possible levels — you will use a compilation option, which you can set separately for each class. The full details of run-time assertion monitoring do appear later in this chapter. But it would be a mistake to attach too much importance to this aspect at this stage (one of the reasons why you were warned earlier not to think too much about the C notion of assertion if that has been your only exposure to the concept). Other aspects of assertions demand our attention first. We have only started to see assertions as a technique to help us get our software right in the first place; we still have much to discover of their methodological role as built-in guardians of reliability. The question of what happens if we do fail (in particular if an assertion, in spite of all our efforts, is not satisfied at some execution instant) is important too, but only after we have done all we could to prevent it from arising. So (although it is never bad to think ahead) you do not need at this point to be too preoccupied by such questions as the possible performance penalty implied by the old construct. Must the run-time system preserve values before we start a routine, just to be able to evaluate an old expression appearing in the postcondition? It depends: in some circumstances (for example testing and debugging) it will indeed be useful to evaluate assertions; in others (for example production runs of fully validated systems) you can treat them as mere annotations to the software text. All that counts for the next few sections is the methodological contribution of assertions, and of the associated method of Design by Contract: as a conceptual tool for analysis, design, implementation and documentation, helping us to build software in which reliability is built-in, rather than achieved or attempted after the fact through debugging; in Mills’s terms, enabling us to build correct programs and know it. 11.6 CONTRACTING FOR SOFTWARE RELIABILITY Defining a precondition and a postcondition for a routine is a way to define a contract that binds the routine and its callers. Rights and obligations By associating clauses require pre and ensure post with a routine r, the class tells its clients: “If you promise to call r with pre satisfied then I, in return, promise to deliver a final state in which post is satisfied.” See “Monitoring assertions at run time”, page 393
342 DESIGN BY CONTRACT:BUILDING RELIABLE SOFTWARE $11.6 In relations between people or companies,a contract is a written document that serves to clarify the terms of a relationship.It is really surprising that in software,where precision is so important and ambiguity so risky,this idea has taken so long to impose itself.A precondition-postcondition pair for a routine will describe the contract that the routine(the supplier of a certain service)defines for its callers (the clients of that service). Perhaps the most distinctive feature ofcontracts as they occur in human affairs is that any good contract entails obligations as well as benefits for both parties-with an obligation for one usually turning into a benefit for the other.This is true of contracts between classes,too: The precondition binds the client:it defines the conditions under which a call to the routine is legitimate.It is an obligation for the client and a benefit for the supplier. The postcondition binds the class:it defines the conditions that must be ensured by the routine on return.It is a benefit for the client and an obligation for the supplier. The benefits are,for the client,the guarantee that certain properties will hold after the call;for the supplier,the guarantee that certain assumptions will be satisfied whenever the routine is called.The obligations are,for the client,to satisfy the requirements as stated by the precondition;for the supplier,to do the job as stated by the postcondition. Here is the contract for one of the routines in our example: put OBLIGATIONS BENEFITS A routine contract: (Satisfy precondition:) (From postcondition:) routine put for Client Only call put (x)on a non- Get stack updated:not a stack class full stack. empty,x on top(item yields x,count increased by 1). Supplier (Satisfy postcondition:) (From precondition:) Update stack representation Simpler processing thanks to have x on top(item yields to the assumption that stack x),count increased by 1, is not full. not empty. Zen and the art of software reliability:guaranteeing more by checking less Although you may not have noticed it yet,one of the contract rules given goes against the generally accepted wisdom in software engineering;shocking at first to many,it is among the method's main contributions to software reliability and deserves emphasis. The rule reflects the above observation that the precondition is a benefit for the supplier and is expressed in the bottom-right box of the table:if the client's part of the
342 DESIGN BY CONTRACT: BUILDING RELIABLE SOFTWARE §11.6 In relations between people or companies, a contract is a written document that serves to clarify the terms of a relationship. It is really surprising that in software, where precision is so important and ambiguity so risky, this idea has taken so long to impose itself. A precondition-postcondition pair for a routine will describe the contract that the routine (the supplier of a certain service) defines for its callers (the clients of that service). Perhaps the most distinctive feature of contracts as they occur in human affairs is that any good contract entails obligations as well as benefits for both parties — with an obligation for one usually turning into a benefit for the other. This is true of contracts between classes, too: • The precondition binds the client: it defines the conditions under which a call to the routine is legitimate. It is an obligation for the client and a benefit for the supplier. • The postcondition binds the class: it defines the conditions that must be ensured by the routine on return. It is a benefit for the client and an obligation for the supplier. The benefits are, for the client, the guarantee that certain properties will hold after the call; for the supplier, the guarantee that certain assumptions will be satisfied whenever the routine is called. The obligations are, for the client, to satisfy the requirements as stated by the precondition; for the supplier, to do the job as stated by the postcondition. Here is the contract for one of the routines in our example: Zen and the art of software reliability: guaranteeing more by checking less Although you may not have noticed it yet, one of the contract rules given goes against the generally accepted wisdom in software engineering; shocking at first to many, it is among the method’s main contributions to software reliability and deserves emphasis. The rule reflects the above observation that the precondition is a benefit for the supplier and is expressed in the bottom-right box of the table: if the client’s part of the put OBLIGATIONS BENEFITS Client (Satisfy precondition:) Only call put (x) on a nonfull stack. (From postcondition:) Get stack updated: not empty, x on top (item yields x, count increased by 1). Supplier (Satisfy postcondition:) Update stack representation to have x on top (item yields x), count increased by 1, not empty. (From precondition:) Simpler processing thanks to the assumption that stack is not full. A routine contract: routine put for a stack class
$11.6 CONTRACTING FOR SOFTWARE RELIABILITY 343 contract is not fulfilled,that is to say if the call does not satisfy the precondition,then the class is not bound by the postcondition.In this case the routine may do what it pleases: return any value;loop indefinitely without returning a value;or even crash the execution in some wild way.This is the case in which(in reference to the discussion at the beginning of this chapter)“the customer is wrong”. The first advantage of this convention is that it considerably simplifies the programming style.Having specified as a precondition the constraints which calls to a routine must observe,you,the class developer,may assume when writing the routine body that the constraints are satisfied;you do not need to test for them in the body.So if a square root function,meant to produce a real number as a result,is of the form sgrt (x:REAL):REAL is --Square root ofx require x>=0 do...end you may write the algorithm for computing the square root without any concern for the case in which x is negative;this is taken care of by the precondition and becomes the responsibility of your clients.(At first sight this may appear dangerous;but read on.) Actually the method of Design by Contract goes further.Writing the do clause of the routine under the form ifx<0 then "Handle the error,somehow" else "Proceed with normal square root computation" end is not just unnecessary but unacceptable.This may be expressed as a methodological rule: Non-Redundancy principle Under no circumstances shall the body of a routine ever test for the routine's precondition. This rule is the reverse of what many software engineering or programming methodology textbooks advocate,often under the name defensive programming-the idea that to obtain reliable software you should design every component of a system so that it protects itself as much as possible.Better check too much,this approach holds,than not enough;one is never too careful when dealing with strangers.A redundant check might not help,but at least it will not hurt. Design by Contract follows from the opposite observation:redundant checks can and indeed will hurt.Of course this will at first seem strange;the natural reaction is to think that an extra check-for example routine sort containing the above conditional instruction testing for x<0 even though callers have been instructed to ensurex>=0-
§11.6 CONTRACTING FOR SOFTWARE RELIABILITY 343 contract is not fulfilled, that is to say if the call does not satisfy the precondition, then the class is not bound by the postcondition. In this case the routine may do what it pleases: return any value; loop indefinitely without returning a value; or even crash the execution in some wild way. This is the case in which (in reference to the discussion at the beginning of this chapter) “the customer is wrong”. The first advantage of this convention is that it considerably simplifies the programming style. Having specified as a precondition the constraints which calls to a routine must observe, you, the class developer, may assume when writing the routine body that the constraints are satisfied; you do not need to test for them in the body. So if a square root function, meant to produce a real number as a result, is of the form sqrt (x: REAL): REAL is -- Square root of x require x >= 0 do … end you may write the algorithm for computing the square root without any concern for the case in which x is negative; this is taken care of by the precondition and becomes the responsibility of your clients. (At first sight this may appear dangerous; but read on.) Actually the method of Design by Contract goes further. Writing the do clause of the routine under the form if x < 0 then “Handle the error, somehow” else “Proceed with normal square root computation” end is not just unnecessary but unacceptable. This may be expressed as a methodological rule: This rule is the reverse of what many software engineering or programming methodology textbooks advocate, often under the name defensive programming — the idea that to obtain reliable software you should design every component of a system so that it protects itself as much as possible. Better check too much, this approach holds, than not enough; one is never too careful when dealing with strangers. A redundant check might not help, but at least it will not hurt. Design by Contract follows from the opposite observation: redundant checks can and indeed will hurt. Of course this will at first seem strange; the natural reaction is to think that an extra check — for example routine sqrt containing the above conditional instruction testing for x < 0 even though callers have been instructed to ensure x >= 0 — Non-Redundancy principle Under no circumstances shall the body of a routine ever test for the routine’s precondition
344 DESIGN BY CONTRACT:BUILDING RELIABLE SOFTWARE $11.6 may at worst be useless,but cannot possibly cause any damage.Such a comment, however,comes from a microscopic understanding of reliability,focused on individual software elements such as the sgrt routine.If we restrict our view to the narrow world of sqrt,then the routine seems more robust with the extra test than without it.But the world of a system is not restricted to a routine;it contains a multitude of routines in a multitude of classes.To obtain reliable systems we must go from the microscopic view to a macroscopic view encompassing the entire architecture. If we take this global view,simplicity becomes a crucial criterion.As was noted at the beginning of this chapter,complexity is the major enemy of quality.When we bring in this concern,possibly redundant checks do not appear so harmless any more! Extrapolated to the thousands of routines of a medium-size system (or the tens or hundreds of thousands of routines of a larger one),the ifx<0 then...of sqrt,innocuous at first sight,begins to look like a monster of useless complexity.By adding possibly redundant checks,you add more software;more software means more complexity,and in particular more sources of conditions that could go wrong;hence the need for more checks,meaning more software;and so on ad infinitum.If we start on this road only one thing is certain: we will never obtain reliability.The more we write,the more we will have to write. To avoid this infinite chase we should never start it.With Design by Contract you are invited to identify the consistency conditions that are necessary to the proper functioning of each client-supplier cooperation (each contract);and to specify,for each one of these conditions,whose responsibility it is to enforce it:the client's,or the supplier's.The answer may vary,and is partly a matter of design style;advice will be given below on how best to choose it.But once you have made the decision,you should stick to it:if a correctness requirement appears in the precondition,indicating that the requirement is part of the client's responsibility,there must not be a corresponding test in the routine;and if it is not in the precondition,then the routine must check for the requirement. Defensive programming appears in contrast to cover up for the lack of a systematic approach by blindly putting in as many checks as possible,furthering the problem of reliability rather than addressing it seriously. Redundant checking,it should be noted,is a standard technique in hardware.The difference is that in a hardware system some object that was found to be in a correct state at some point may later have its integrity destroyed because of reasons bey ond the control of the system itself,such as interference from another system,harmful external event,or simply wear and tear.For that reason it is normal practice,for example,to have both the sender and the receiver of an electronic signal check its integrity. But no such phenomenon occurs in software:if I can prove or check in some way that a is non-negative whenever sort (a)is called,I do not need to insert a check for xS0, where x is the corresponding formal argument,in the body of sqrt.Nothing will happen to a between the time it is"sent"by the caller and the time it is"received"(under the name x)by the routine.Software does not wear out when used for too long;it is not subject to line loss,to interference or to noise. Also note that in most cases what is called redundant checking in hardware is not really redundant:one actually applies different and complementary verifications,such as a parity check and some other test.Even when the checks are the same they are often
344 DESIGN BY CONTRACT: BUILDING RELIABLE SOFTWARE §11.6 may at worst be useless, but cannot possibly cause any damage. Such a comment, however, comes from a microscopic understanding of reliability, focused on individual software elements such as the sqrt routine. If we restrict our view to the narrow world of sqrt, then the routine seems more robust with the extra test than without it. But the world of a system is not restricted to a routine; it contains a multitude of routines in a multitude of classes. To obtain reliable systems we must go from the microscopic view to a macroscopic view encompassing the entire architecture. If we take this global view, simplicity becomes a crucial criterion. As was noted at the beginning of this chapter, complexity is the major enemy of quality. When we bring in this concern, possibly redundant checks do not appear so harmless any more! Extrapolated to the thousands of routines of a medium-size system (or the tens or hundreds of thousands of routines of a larger one), the if x < 0 then … of sqrt, innocuous at first sight, begins to look like a monster of useless complexity. By adding possibly redundant checks, you add more software; more software means more complexity, and in particular more sources of conditions that could go wrong; hence the need for more checks, meaning more software; and so on ad infinitum. If we start on this road only one thing is certain: we will never obtain reliability. The more we write, the more we will have to write. To avoid this infinite chase we should never start it. With Design by Contract you are invited to identify the consistency conditions that are necessary to the proper functioning of each client-supplier cooperation (each contract); and to specify, for each one of these conditions, whose responsibility it is to enforce it: the client’s, or the supplier’s. The answer may vary, and is partly a matter of design style; advice will be given below on how best to choose it. But once you have made the decision, you should stick to it: if a correctness requirement appears in the precondition, indicating that the requirement is part of the client’s responsibility, there must not be a corresponding test in the routine; and if it is not in the precondition, then the routine must check for the requirement. Defensive programming appears in contrast to cover up for the lack of a systematic approach by blindly putting in as many checks as possible, furthering the problem of reliability rather than addressing it seriously. Redundant checking, it should be noted, is a standard technique in hardware. The difference is that in a hardware system some object that was found to be in a correct state at some point may later have its integrity destroyed because of reasons beyond the control of the system itself, such as interference from another system, harmful external event, or simply wear and tear. For that reason it is normal practice, for example, to have both the sender and the receiver of an electronic signal check its integrity. But no such phenomenon occurs in software: if I can prove or check in some way that a is non-negative whenever sqrt (a) is called, I do not need to insert a check for x Š≥ 0, where x is the corresponding formal argument, in the body of sqrt. Nothing will happen to a between the time it is “sent” by the caller and the time it is “received” (under the name x) by the routine. Software does not wear out when used for too long; it is not subject to line loss, to interference or to noise. Also note that in most cases what is called redundant checking in hardware is not really redundant: one actually applies different and complementary verifications, such as a parity check and some other test. Even when the checks are the same they are often
$11.6 CONTRACTING FOR SOFTWARE RELIABILITY 345 applied by different devices,as in the just mentioned case of a sender and receiver that both check a signal,or in a redundant computer system where several computers perform the same computation,with a voting mechanism to resolve discrepancies. Another drawback of defensive programming is its costs.Redundant checks imply a performance penalty-often enough in practice to make developers wary of defensive programming regardless of what the textbooks say.If they do make the effort to include these checks,removing some of them later to improve performance will be tedious.The techniques of this chapter will also leave room for extra checks,but if you choose to enable them you will rely on the development environment to carry them out for you.To remove them,once the software has been debugged,it suffices to change a compilation option (details soon).The software itself does not contain any redundant elements. Aside from performance considerations,however,the principal reason to distrust defensive programming is simply our goal of getting the best possible reliability.For a system of any significant size the individual quality of the various elements involved is not enough;what will count most is the guarantee that for every interaction between two elements there is an explicit roster of mutual obligations and benefits-the contract. Hence the Zen-style paradox of our conclusion:that to get more reliability the best policy is often to check less. Assertions are not an input checking mechanism It is useful here to emphasize a few properties of the approach which,although implicit in the preceding discussion,have been shown by experience to require further explanations The following comments should help address some of the questions that may have been forming in your mind as you were reading about the basic ideas of Design by Contract. To avoid a common misunderstanding,make sure to note that each of the contracts discussed holds between a routine(the supplier)and another routine (its caller):we are concerned about software-to-software communication,not software-to-human or software-to-outside-world.A precondition will not take care of correcting user input,for example in a read positive integer routine that expects the interactive user to enter a positive number.Including in the routine a precondition of the form require input >0 would be wishful thinking,not a reliability technique.Here there is no substitute for the usual condition-checking constructs,including the venerable if...then...;the exception handling mechanism studied in the next chapter may also be helpful. “Modular protec- Assertions do have a role to play in a solution to this problem of input validation.In tion”,page45. line with the criterion of Modular Protection,the method encourages validating any objects obtained from the outside world-from sensors,from user input,from a network...-as close to the source of the objects as possible,using "filter"modules if necessary:
§11.6 CONTRACTING FOR SOFTWARE RELIABILITY 345 applied by different devices, as in the just mentioned case of a sender and receiver that both check a signal, or in a redundant computer system where several computers perform the same computation, with a voting mechanism to resolve discrepancies. Another drawback of defensive programming is its costs. Redundant checks imply a performance penalty — often enough in practice to make developers wary of defensive programming regardless of what the textbooks say. If they do make the effort to include these checks, removing some of them later to improve performance will be tedious. The techniques of this chapter will also leave room for extra checks, but if you choose to enable them you will rely on the development environment to carry them out for you. To remove them, once the software has been debugged, it suffices to change a compilation option (details soon). The software itself does not contain any redundant elements. Aside from performance considerations, however, the principal reason to distrust defensive programming is simply our goal of getting the best possible reliability. For a system of any significant size the individual quality of the various elements involved is not enough; what will count most is the guarantee that for every interaction between two elements there is an explicit roster of mutual obligations and benefits — the contract. Hence the Zen-style paradox of our conclusion: that to get more reliability the best policy is often to check less. Assertions are not an input checking mechanism It is useful here to emphasize a few properties of the approach which, although implicit in the preceding discussion, have been shown by experience to require further explanations. The following comments should help address some of the questions that may have been forming in your mind as you were reading about the basic ideas of Design by Contract. To avoid a common misunderstanding, make sure to note that each of the contracts discussed holds between a routine (the supplier) and another routine (its caller): we are concerned about software-to-software communication, not software-to-human or software-to-outside-world. A precondition will not take care of correcting user input, for example in a read_positive_integer routine that expects the interactive user to enter a positive number. Including in the routine a precondition of the form require input > 0 would be wishful thinking, not a reliability technique. Here there is no substitute for the usual condition-checking constructs, including the venerable if … then …; the exception handling mechanism studied in the next chapter may also be helpful. Assertions do have a role to play in a solution to this problem of input validation. In line with the criterion of Modular Protection, the method encourages validating any objects obtained from the outside world — from sensors, from user input, from a network… — as close to the source of the objects as possible, using “filter” modules if necessary: “Modular protection”, page 45