Roll back multiple migrations with Capistrano

Have you ever used Capistrano to run Rails’ database migrations in your production environment? It’s pretty great, just cap deploy:migrations and you’re good to go. You’ve probably also had to use Capistrano to roll back a bad deploy, using cap deploy:rollback. But what if your most recent deployment had a migration that introduced a schema incompatible with your old code?

Well, if there was only one migration, you could roll it back by using rake db:rollback RAILS_ENV=production. However, the problem I found myself in a few months ago was that I had deployed a feature with multiple migrations. And, it was a long running branch, so the migration files weren’t the most recent ones run. rake db:rollback would have, in fact, reverted only a single (and wrong!) schema change.

The correct command to use in these cases is rake db:migrate:down RAILS_ENV=production VERSION=<UR TIMESTAMP HERE>. Let’s be honest, though. If you’re rolling back a schema change, you don’t want to mess around with running these tasks one at a time. In my situation, I had 10 (!) migrations that needed to be reverted. I ended up rolling them back on at a time, until one halfway through threw an error. I then realized that the only way out was through, and fixed the bug that caused the rollback attempt.

So! Resolving to never be in that situation again, I created a simple Capistrano (ver. 2) task to revert multiple migrations. And I always ensure that all migrations in any pull request I merge can be run both ways.

desc 'Migrate downwards; Usage: cap db:migrate:down VERSIONS=TIMESTAMP,[...],TIMESTAMP'
namespace :db do
  namespace :migrate do
    task :down, roles: :db do
      ENV['VERSIONS'].split(',').each do |version|
        run "cd #{current_path} && bundle exec rake db:migrate:down VERSION=#{version}"