Recently, I added a new feature that significantly changes one of the database tables in my application. I ran into problems when I created a non-reversible database migration, and realized that other branches I am developing on still require the older database scheme. In the end I had to end up dropping the DB, deleting the offending migration, rebuilding the DB, and figuring out a smarter way to migrate. Not fun, and time wasted.
What got me into trouble was forgetting that I have multiple code streams I’m working on. I’m trying to do a good job and create different branches for different features, merge to master frequently, and re-base WIP branches to keep in sync with master. This works pretty well for code, but not so well for DB schemas. Specifically, the challenge I ran into occurred when I renamed a column, changed its type, and remove a couple columns I didn’t need any more in that branch.
Here are some best practices I came up with:
- Instead of renaming a column, create a new column with the correct name. Remove the old column once you get all branches migrated to the new column
- Before removing a column, make sure no other branches depend on it.
- When removing a column, explicitly define your “up” and “down” methods. It doesn’t seem that the “change” method can magically reinstate a removed column, which makes sense as the type, and other attributes once can define at column creation time do not exist in that file
- Evolve your table architecture incrementally. Like with TDD, make changes in small, coherent steps. Big changes smell of irreversibility.