The Psychology of Quality and More
CHAPTER 10 : Programming Usage
10.3 Defensive programming
Defensive programming assumes that 'Murphy's Law' is true, and that "the worst possible thing will happen, at the worst possible moment," and thus caters for all possible eventualities (and also for the impossible ones).
One of the 'problems' of defensive programming is that it can require significantly more coding that the code it is defending, and there may be strong temptation to cut corners as it is catering for things that should (and often will) never happen. It is admitting that your program could go wrong. It also adds significantly to the program size and to its complexity, and can thus also affect performance.
The degree to which defensive programming is required will be directly proportional to the required integrity of the program, which will usually depend on the impact of program failure. A space probe control system would require a high degree of not only defensiveness but also recoverability, whereas as line-counting program may only be required not to damage the file it is reading.
10.3.1 Defend against hardware-level errors
Errors at the hardware level are classic problems to defend against. These typically trigger processor exceptions or traps which then cause program crashes (with a dump and diagnostic, if you are lucky).
A typical such problem is arithmetic error, including overflow and divide-by-zero. This may be defended against by checking variables before use:
if ( NumLines != 0 )
Other hardware-related hazards include using uninitialized or wrongly initialized (or null!) pointers and attempting to write to a full disc. The results from doing either could be quite unpleasant and are well worth defending against.
10.3.2 Defend against program errors
A common defense within programs is ensuring that variables are within expected bounds. Often this is at interfaces between functions, where an unexpected parameter value might cause unexpected problems. Function outputs are equally liable to scrutiny: not checking error returns properly is a typical problem. Loops are another situation where bounds problems can cause such as out-by-one errors.
The ultimate approach is: do not trust anything; verify everything. For example, one might have several computers, each with programs written by different programming teams, with the final decision based on a voting system. Thus if one program contains an error, then the others (which are unlikely to contain the same error) will overrule it. This system is actually used in some aircraft control systems.
10.3.3 Defend the user against himself
If the user of a program used it sensibly and only pressed defined keys, always had the right disc mounted and fully understood the operation of the program, then the job of the programmer would be made much easier. However, users are notorious for doing the wrong thing in the wrong place, and it is necessary to defend against his random and ridiculous actions by handling not only expected user actions but unexpected ones also.
10.3.4 Defend the defenses
In sensitive situations, it often makes sense to have a series of defenses, so that if one defense fails, another, perhaps angled slightly differently, catches the problem. Also, the code that is doing the defending can cause problems! Thus errors in an error handler should not cause problems, and a recovery program should be able to recover itself.
And the big