Keeping Rails migrations happy

Two quick things we’ve learned about migrations at CDD:

  • Avoid using your model objects in your migrations, e.g. stuff like Group.create!(:name => "Watson Lab"). The problem with this is that later you might add a required field to your model, and then this migration will throw an exception. Occasionally you need some logic from a model in a migration, but if at all possible I’d suggest exposing that logic in a way that doesn’t require creating or loading model objects in your migration itself. The migration should just know about SQL, nothing else.
  • Say you branch your code base for a release, and you anticipate needing to support that branch for any length of time. Sometimes you’ll need to address an issue in the production code that requires another migration. What we’ve found works best with ActiveRecord migrations is:
    1. In the trunk, delete all existing migrations when you branch.
    2. Dump a version of the branch schema, and make that migration #1 (001_production_branch_schema.rb) in your trunk.
    3. Start your next trunk migration several numbers higher than your last migration on the production release branch. So, if your last migration on the branch was 40, start 40+N, where N gives you enough cushion to accommodate any additional migrations needed for the branch until your next release.
    4. Any time you add another migration to the branch, in the trunk replace 001_production_branch_schema.rb with a new dump of your branch schema.

Kind of a hack, but it works better than anything else we’ve come up with. My former colleague, Rhett Sutphin, took a different approach to this problem when he wrote a Java/Groovy port of migrations called bering (to which I minimally contributed in its early stages). In bering, migrations are specific to a particular release. Each release is numbered and gets its own separate migration directory, and migrations start at one again for each new release.

Leave a Reply