Rails migrations revisited
No, we are not back to 2007 again. There are just a few things about
migrations that I think some people have still not got right. So, I thought I
will highlight them here.
Rails have had migrations for quite a few releases now. Here’s a brief review:
When you want to do a change in your database schema, like add a column or change a
table definition, you generate a migration, populate the code for forward
and backward migrations, and run rake db:migrate. It let’s you change your
DB incrementally. Everybody gets this part I think. See Rails guides for a wonderful overview.
Running a migration updates the db/schema.rb file. It is required to put
this in your source control and check it in with the migration. A lot of
people actively ignore this file (by using svn:ignore or .gitignore). Don’t.
Migrations are useful when you are upgrading your database. Where as if you
are setting up a new instance of the database (say a new user joins your
team or you are setting up a new staging server), you should not be running the
migrations. Instead do this:
$ rake db:create $ rake db:schema:load
Running a long winded migrations, in most non trivial projects, is almost
bound to fail (because of name changes in your source code, since you are not going back and
updating your migrations). This is official Rails policy (see the comment on
top of your db/schema.rb or DHH’s code commit here)
for a summary of the opposite view.
Data migration is porting the data in the tables to the new
schema. Unfortunately, Rails does not have a clearly specified way of dealing
with this. Usually developers do not treat this as a separate issue but handle
it as part of usual Rails migrations.
There are two cases when you may find yourself dealing with data in migrations:
- while seeding the database with some data, or
- porting the data in older tables to newer schema.
Most of the times it is possible to handle both using migrations. However, for the
second case, you may sometimes need to create special migration scripts.
However, if you are not running migrations to start a new database, you still
need a way to seed the database with the required data. Here is something I
have used which has worked well for several projects which I picked up from
- In addition to creating a migration, put seed data in db/fixtures/*.yml
- load the seed data from db/fixtures when initializing a new database
$ rake db:create $ rake db:schema:load $ rake db:seed # see link above for Rake task definition for this
Most projects ignore data migrations during development, which is not entirely
wrong. Who cares for lost data during development? But, you will need a robust
strategy for handling data migrations once you are on production and you
should prepare yourself with a methodology well in advance. And a last final
tip, backup your production database before running any migrations.