Tuesday, April 10, 2007

Rails: Use Ruby Schema syntax without using Migrations

I've touched on this topic a few times. I previously listed the pains of using Migrations. And, in my last post I gave an example of our solution. But, I never went further into why we made the choice.

For my current team, the choice was a fairly easy one. It's a large team and we were churning out migrations very quickly. Of course, the numbering became an issue, but keeping a consistent view of the database became a larger issue. There was no one place to get a clear view of what the table looked like (in code).

Another issue with Migrations is that the self.down methods are never really tested. Sure, you might go down then up with each build, but do you test the application at every step down and up? Without running all the tests at each down, it's impossible to know that the self.down did exactly what you expected.

Also, how often do you need to step up or down? We found that we wanted to drop the entire db with each build to ensure that we didn't have any data dependencies. Therefore, going back a version or 2 never seemed valuable.

Most importantly, we were eventually going to need to generate SQL and send it to the database group. This meant that migrations were only ever going to be run by our team. And, if we needed a view of the database at any given date, we could just look in Subversion for that copy of our schema.

Given the above thoughts, we decided to create one schema file per release. The schema file uses the same syntax as migrations and even allows us to specify the schema version (very much like the idea of using one migration per release).
ActiveRecord::Schema.define(:version => 1) do

create_table :accounts, :force => true do |t|
t.column :first_name, :string
t.column :last_name, :string
t.column :username, :string
t.column :password, :string
t.column :email, :string
t.column :company, :string
end

...
end
And, in case you missed it in the previous post, the task to run it is very simple.
task :migrate => :environment do
ActiveRecord::Base.establish_connection(environment)
require File.dirname(__FILE__) + '/../../db/schema/release_1.rb'
end
Migrations are fantastic, but if you don't need them you shouldn't live with the overhead.

2 comments:

  1. I'm sort of thinking that Migrations are being abused a little in the way that they're proposed as being used.

    The way that it seems that the DHH advocates using them is that any time you make a change during development, in goes a new migration and then you go all db:migrate.

    You've of course pointed out the problems before with this. It *seems* to me that migrations really want to be release tools, for managing the changes to the production database, probably with a separate set of tests for up and down movements to ensure that data stays valid from version to version.

    Within development, I'd think that, like you've mentioned, all the changes oughta go into one file, and you run a rake task to update your repo and drop and reload your databases for development and test.

    Maybe then a migration task could be added that automagically generates the migrations between two version tags? (Too complicated maybe?) And then you write tests for migrational data validity and add in any code needed to make those tests pass.

    If I was smart enough and had time, that's what I'd try for.

    ReplyDelete
  2. We have a pretty big development team so we run into a problem of having too many migrations pretty soon. What we now is as soon the release goes to production, we run all migrations up the the version in production, dump the DBs into SQL and then consolidate all already run migrations into a single one which simply loads the generated SQL.

    ReplyDelete

Note: Only a member of this blog may post a comment.