I just love Steve McConnell’s classic book Code Complete 2, and I recommend it to everyone in the Software ‘world’ who’s willing to progress and sharpen his skills.
Other blog posts in this series:
Defensive-programming techniques make errors easier to find, easier to fix, and less damaging to production code.
Assertions
An assertion is a code that’s used during development-usually a routine or macro- that allows a program to check itself as it runs. When an assertion is true, that means everything is operating as expected.
Use error handling code for conditions you expect to occur and assertions for conditions that should never occur.
Design by contract
Preconditions and post conditions are part of an approach to program design and development known as “design by contract”.
- Preconditions are properties that the client code of a routine or class promises will be true before it calls the routine or instantiates the object.
- Postconditions are the properties that the routine or class promises will be true when it concludes executing.
For highly robust code, assert and then handle the error anyway.
Some experts argue that only one kind is needed. But real-world programs and projects tend to be too messy to rely solely on assertions.
Error-Handling Techniques
Depending on the specific circumstances, you might want to:
- Return a neutral value
- Substitute the next piece of valid data
- Return the same answer as the previous time
- Substitute the closest legal value
- Log a warning message to a file
- Return an error code
- Call an error-processing routine/object
- Shut down
Correctness means never returning an inaccurate result; returning no result is better than returning an inaccurate result.
Robustness means always trying to do something that will allow the software to keep operating, even if that leads to results that are inaccurate sometimes.
Safety-critical applications tend to favor correctness to robustness.
Consumer applications tend to favor robustness to correctness. Any result whatsoever is usually better than the software shutting down.
Exceptions
Exceptions have an attribute in common with inheritance: used judiciously, they can reduce complexity. Used imprudently, they can make code almost impossible to follow.
What you should know about using exceptions:
- Use exceptions to notify other parts of the program about errors that should not be ignored
- Throw an exception only for conditions that are truly exceptional
- Don’t use an exception to pass the buck – handle error locally
- Avoid throwing exceptions in constructors and destructors unless you catch them in the same place
- Throw exceptions at the right level of abstraction
Use barricades
Defining some parts of the software that works with dirty data and some that work with clean data can be an effective way to relieve the majority of the code of the responsibility for checking for bad data.
Debugging Aids
Another key aspect of defensive programming is the use of debugging aids, which can be a powerful ally in quickly detecting errors.
Be willing to trade speed and resource usage during development in exchange for built-in tools that can make development go more smoothly.
Determining how much defensive programming to leave in production code
One of the paradoxes of defensive programming is that during development, you’d like an error to be noticeable-you’d rather have it be obnoxious than risk overlooking it. But during production, you’d rather have the error be as unobtrusive as possible, to have the program recover or fail gracefully.
Here are some guidelines for deciding which defensive programming tools to leave in your production code and which to leave out:
- Leave the code that checks for important errors
- Remove code that checks for trivial errors
- Remove code that results in hard crashes
- Leave the code that helps the program crash gracefully
Being defensive about defensive programming
Too much defensive programming creates a problem of its own. Code installed for defensive programming is not immune to defects, and you’re just as likely to find a defect in defensive-programming code as in any other code-more likely if you write the code casually.