- #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
#3: Automate All Changes to Every Database Instance
The process mentioned in tip #2 can be completely automated. Not only can you automate that process, you must. The correct amount of manual intervention to allow in your database construction process is the same in every organization, in every environment, and probably even on every planet.
If you guessed “0,” then you got it.
In an agile environment, every single change needs to be funneled through the process outlined in #2, and that can only happen if it’s easy. Anything that has a bunch of steps and can only be done manually is, pretty much by definition, not easy. In most cases, the cost of writing a little code that applies only the right scripts and only in the right order is lower than the cost of building a single test database by hand.
In addition to automation being easier than a manual process, it has two other traits of incredible power. For one thing, automation does the same thing every time you run it; at least it will if you build it right. For another, you can write tests for something that’s automated and confirm that the thing it does every time it’s run is, in fact, the right thing.
To make a long story short, make sure your database build tool knows how to record its progress in any database it builds and then uses that record the next time it runs to ensure no script is run a second time. I like to use a table that declares what class was used to build a database and which version scripts have been applied. This is depicted in Figure 6.
Having a tool like this gives you the ability to spin up as many databases of any particular class as you like. You’ll know that each instance works precisely the same way as all its siblings so, when you execute a test against a throw-away development instance, you can use the result to predict behavior in production.
It shouldn’t be hard for you to guess the minimum requirements for how a tool like this should work. The algorithm is expressed at the end of tip #2. A detailed example implementation with several features not identified in this article is available in the companion code download.