Chapter 3: Fortify Your Subsystems

  • look at your subsystems and ask yourself how likely are other programmers to miss-use them. Add assertions and validation checks to catch hard to spot and common bugs;
  • remove random behavior to force bugs to be reproducible;
  • destroy garbage data/objects in order for them not to be misused;
  • look for rare behaviors in the program and try to make them happen more often (even in the debug versions of the code with directives). A rare behavior might have a bug which is not easily noticed because the behavior itself is rare. This kind of bug can be very difficult to track down. If one were to make this behavior execute more often, there is a good chance that the bug will be discovered eventually;
  • all implementations should be well thought keeping in mind whether a possibility either generates or helps finding bugs;
  • nothing should be arbitrary. Analyze all options for as long as you need before taking a decision;
  • for each design you consider, ask yourself how can you thoroughly validate it. If the implementation is difficult or even impossible to test, seriously consider a different design even if that means trading speed/size for the ability to test the system;
  • the debug version is not shipped, you can do whatever tests you want in the debug version. Even if it’s slower, if there is a chance to catch a bug before the code reaches production, it’s a win-win situation for everybody;
  • if debug code is about to be tested by somebody else, warn them about the code being loaded with internal debug checks which affect performance;
  • don’t apply ship version constraints to the debug version. Trade size and speed for error detection.