Healthy Migrations

Continuing with the fixtures/test theme, I want to focus on the place where fixtures actually live - the database. Migrations are the blueprint, however, they often break and we don’t notice. You should alway be able to do this:

12

$ rake db:migrate VERSION=0
$ rake db:migrate

I used to say “what does it matter? We’re never going back to migration 3, we’re on 156 now!” This kind of thinking showed how I didn’t understand the usefulness of migrations:

Setting up a new development system should not require a recent database snapshot.

Automated tests and build notifications are simpler when the migrations are clean.

cap rollback will save your life some day

Migrations are not just a historical record of your database design. They instead give you a way to build your database up from scratch, doing more than just creating a schema. You can seed data, create indexes, and make transformations.

When you first start a rails project, and everything is golden, migrations are easy. Eventually, you run into problems. It happens a lot, because we typically don’t test the entire migration sequence. For example:

A model changes somewhere, and breaks a dependent migration

Using models in migrations is a common way to seed the database, or manipulate things:

Someone on the team checks in a migration that has a bug

If the problem is trivial, they might be tempted to skip reporting it and just fix it in the database to keep things moving. Or, they may not even notice the problem, depending on when they updated. These issues can lead to inconsistencies, and tests that pass for one developer, but not another!

Developers only migrate up

Migrating down should work too, what if you need to roll back to fix something in production? Always write a sensible down method and test it. It does not have to perfectly reverse the database, it just needs to return it to a state that will enable the previous migration to run it’s down method. I’ve seen horrific migrations checked in like this:

123

defself.down# no need to do thisend

The team works from a production db snapshot based on a deployed site

This is bad, because it means the team is probably not using TDD, and are instead relying on browser interaction to develop the app. At minimum, they are blind to migration issues. Relying on an external database for development is an unwise dependency. It also complicates setup for testing.

Keep Migrations Working!

Each time you add a migration, or refactor a number models, you should check that all the migrations are working. There are a number of solutions for doing this - the most obvious is to drop the dev db and migrate up from scratch, see if it works.