views:

177

answers:

4

In Rails, migrations have a down method by default for reverting a migration. In what scenario would I ever want to revert a migration, though?

Some thoughts:

Whether in development or production, I always have a snapshot of my database to go back to, before I even run the migrations. Especially for migrations which perform data conversion, I find in most cases that reverting a snapshot is even faster than reverting a migration. (So I would never do it in a rush!)

If a migration were to fail, it would either:

  • fail with an exception on a non-transactional database, and thus leave the database broken, or
  • fail with an exception and roll back the transaction, and thus there would be no need to revert otherwise.

If the changes made are in production (or late in development), and later turn out to be a mistake, I would fix my mistake in a new migration. I would not revert the old one. In development, I'd simply delete the migration.

I also find that the down method introduces extra code in which I repeat myself, and thus may introduce new bugs. This is against the DRY principle.

So I'm curious about the pros, because I can't think of any.

+4  A: 

In development, it is easy and fast to incrementally "improve" migrations by using the down method automatically. Eg

  1. Create a migration and migrate to it
  2. Realize you need to make a change
  3. Migrate to the ver prior to your new migration by using db:migrate with a version
  4. Improve/fix your migration
  5. Rerun the migration task

Your method of taking snapshots works fine. But rails includes the same effect auto-magically using the "down" migration techniques. Works with all db's, tastes great

Added:

For production, I agree that a down migration shouldn't be needed. But sometimes mistakes happen and you need to roll back. The down migration path gives you a first, and quick opportunity to fix things in an emergency situation during an upgrade that goes wrong.

-- it is much faster to try a down migration in an emergency than to restore the db using a checkpoint.

Larry K
Okay, but in this case, I still don't see the reason why I would ever commit the `down` code, if it is only a temporary means for myself towards perfecting the migration. To me, I would still be committing redundant and error-prone code.
Shtééf
I noticed you added another point there, but I'm still not totally convinced. It seems like a niche case. If the database is large enough for snapshots to be slow, then I probably wouldn't be able to verify the results of my down migration either. Migrations don't have automated tests, and in my experience are not tested nearly enough manually either. They seem incredibly fragile.
Shtééf
+1  A: 

The "down" migration used for DB Rollbacks is in place so that every action has an equal and opposite action. It takes the onus off of the developer to maintain database snapshots, and allows them to use code to achieve the same ends. As Larry K said, they're good for situations like so:

  • Add a database column called 'resubmitted', it's a boolean.
  • Product owner says they can resubmit multiple times, so change that column needs to be an int

Now, if you're 10 or 15 migrations deep, it's easier to just write a new one instead of losing all the dev data in the new tables/columns by doing a rollback. However, if you've just written that migration, it's cleaner and less cluttered to roll back, change the migration, and re-run it.

The other extremely useful feature of rollbacks is this:

  • Developer 1 has his own Dev DB. He writes a migration and runs it.
  • Developer 1 commits his migration to source control
  • Developer 2 has her own Dev DB. She writes a migration and runs it.
  • Developer 2 updates from source control
  • Developer 2 tries to run migrations but can't, as her local DB says "the latest migration has already been run", since her migration (the latest one), has technically already been run. Now she needs to rollback, then do a db:migrate to get all migrations in her local DB.
Mike Trpcic
Your first argument seems the same as Larry K's to me. Yes I can roll it back and perfect the migration, but why would I commit the down code? As for the second example of developers working independently, I believe this was fixed in Rails 2.1? Rails keeps track of the individual migrations applied, rather than just the last version.
Shtééf
A: 

When a migration hasn't had the expected result, it is better to roll back and rewrite it than to keep the failed migration in the code.

nanda
+2  A: 

The idea of running a down migration in production horrifies me. Back when the preferred way to rollback all migrations was rake db:migrate VERSION=0 I would do this all the time in development. However, then I got paranoid that because it was committed to muscle memory I would accidentally type this on a production server when I meant to simply migrate.

Because of this paranoia, I add the following to all of my down methods.

  def self.down
    if Rails.env.production?
      raise ActiveRecord::IrreversibleMigration
    else
      drop_table :foo_bars
    end 
  end 

This way, it still works in development, but I can't accidentally nuke my production database from orbit while half-asleep at 2:00 AM.

jdl
That is awesome. I'll have to see if I can create a base class that does this for me.
Shtééf