This chapter introduced what I’ve come to realize may be the most essential issue in programming, superceding language syntax and design issues: How do you make sure your code is correct, and keep it that way?
Recent experience has shown that the most useful and practical tool to date is unit testing, which, as shown in this chapter, may be combined very effectively with Design by Contract. There are other types of tests as well, such as conformance testing to verify that your use cases/user stories have all been implemented. But for some reason, we have in the past relegated testing to be done later by someone else. Extreme Programming insists that the unit tests be written before the code; you create the test framework for the class, and then the class itself (on one or two occasions I’ve successfully done this, but I’m generally pleased if testing appears somewhere during the initial coding process). There remains resistance to testing, usually by those who haven’t tried it and believe they can write good code without testing. But the more experience I have, the more I repeat to myself:
If it’s not tested, it’s broken.
This a worthwhile mantra, especially when you’re thinking about cutting corners. The more of your own bugs you discover, the more attached you grow to the security of built-in tests.
Build systems (in particular, Ant) and revision control (CVS) were also introduced in this chapter because they provide structure for your project and its tests. To me, the primary goal of Extreme Programming is velocity—the ability to rapidly move your project forward (but in a reliable fashion), and to quickly refactor it when you realize that it can be improved. Velocity requires a support structure to give you confidence that things won’t fall through the cracks when you start making big changes to your project. This includes a reliable repository, which allows you to roll back to any previous version, and an automatic build system that, once configured, guarantees that the project can be compiled and tested in a single step.
Once you have reason to believe that your program is healthy, logging provides a way to monitor its pulse, and even (as shown in this chapter) to automatically email you if something starts to go wrong. When it does, debugging and profiling help you track down bugs and performance issues.
Perhaps it’s the nature of computer programming to want a single, clear, concrete answer. After all, we work with ones and zeros, which do not have fuzzy boundaries (they actually do, but the electronic engineers have gone to great lengths to give us the model we want). When it comes to solutions, it’s great to believe that there’s one answer. But I’ve found that there are boundaries to any technique, and understanding where those boundaries are is far more powerful than any single approach can be, because it allows you to use a method where its greatest strength lies, and to combine it with other approaches where it isn’t so strong. For example, in this chapter Design by Contract was presented in combination with white-box unit testing, and as I was creating the example, I discovered that the two working in concert were much more useful than either one alone.
I have found this idea to be true in more than just the issue of discovering problems, but also in building systems in the first place. For example, using a single programming language or tool to solve your problem is attractive from the standpoint of consistency, but I’ve often found that I can solve certain problems much more quickly and effectively by using the Python programming language instead of Java, to the general benefit of the project. You may also discover that Ant works in some places, and in others, make is more useful. Or, if your clients are on Windows platforms, it may be sensible to make the radical decision of using Delphi or Visual BASIC to develop client-side programs more rapidly than you could in Java. The important thing is to keep an open mind and remember that you are trying to achieve results, not necessarily use a certain tool or technique. This can be difficult, but if you remember that the project failure rate is quite high and your chances of success are proportionally low, you could be a little more open to solutions that might be more productive. One of my favorite phrases from Extreme Programming (and one I find that I violate often for usually silly reasons) is “do the simplest thing that could possibly work.” Most of the time, the simplest and most expedient approach, if you can discover it, is the best one.