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:
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:
1 2 3 4 5 6 7 8 9
If you delete or refactor the Section model later, this migration will likely break. The solution for this one is to define the model in the migration:
1 2 3 4 5 6
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:
1 2 3
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.
Err the blog posted a task a while back. There’s also this often referenced snippet that works. And today, I noticed this post on Ryan’s Scraps – it looks like Rails itself now has a task to do this.
However, my favorite solution at the moment is sitting in a patch #8389 (not committed at this date), which offers this bit of sugar:
This setting would force rails to build the database schema from migrations, not from sql or
So before you check in migrations, make sure you can run them up from scratch. And then, don’t forget to make sure your fixtures are still valid, too!