- #1: Build Classes, Not Individual Databases
- #2: Don't Ignore History
- #3: Automate All Changes to Every Database Instance
- #4: Test Every Change to Database Design
- #5: Force Clients to Break Fast When Interfaces Change
- #6: Encapsulate Database Behavior
- #7: Drive Behavior from Information
- #8: Keep a Complete Unit Test Suite
- #9: Build Only What You Need
- #10: DON'T PANIC
#10: DON’T PANIC
Wise in all contexts, the words on the cover of the fictional The Hitchhiker’s Guide to the Galaxy are about as close to always right as anything anyone has written. Panic might have some evolutionary foundation. Perhaps our ancestors could escape predation by seeming crazy under maddening duress, but it has little place in most modern endeavors.
Yet, when the pressure is on is exactly when most people snap and revert to bad habits. I know this by way of observation and also by having fallen victim myself to that vestige of our pre-hegemony. Everybody has the potential to panic and do the wrong thing but, as the name implies, that thing tends to be the wrong one to do.
For the purposes of this article, I’m going to define error as “any behavior in your class of database that is not specified by tests.” No matter how good a job you do, there is always the potential for a mistake to be made. You can catch it earlier. You can skew toward errors that are lower in cost or risk. You cannot, however, prevent mistakes altogether.
So you need a plan for how to deal with mistakes when they occur. Broadly, this plan is depicted in Figure 18.
When you first discover an error in your database design, the most important thing to do is triage it. Does the existence of that error constitute a disaster? If so, you need to take immediate action, and that action looks a lot like what it would have in the case of a catastrophic problem prior to the introduction of agility or TDD. You probably are going to end up restoring a backup or doing something very similar to that.
Once ruin has been averted, you need to document the current behavior of your database in the form of one or more unit tests. It may not seem important, but it is. You don’t necessarily know what steps you are going to take to remediate this error or how long it will be until you take them. Having clear documentation of what the database does now will pay off often enough to just do it every time.
Has the error leaked out into production? If it has, guess what: it’s a feature. Like all features of your database, its genesis and subsequent transformations need to be documented. This is done by tracing the problem back to its origins, writing the appropriate transition tests, then following it through all subsequent historical transitions and ensuring there are tests that define how it changed, if at all.
Is the error desirable or not? If what you have is a feature that clients want, that was simply not specified by tests, then you are done. The only problem was the lack of specification and you’ve solved that. If, however, the feature in your database is somehow adverse or maladjusted, then you need to correct the behavior of your database class. That is accomplished using the normal test-driven database development path: modify a unit test to specify what should really happen, write transition tests to specify how the new behavior will be introduced, add the behavior to your database class, and refactor if necessary; repeat as needed.