DEV Community

Raquel Román-Rodriguez
Raquel Román-Rodriguez

Posted on

ActiveRecord Migration Rollback: learning the pitfalls of the `change` method the hard way

I recently finished my project for the third phase of FlatIron's Software Engineering Bootcamp. I built a raised-bed garden planning app using React for the front-end, and Ruby with ActiveRecord and Rack for my back-end. It was my first real back-end experience. You can check out the deployed app here.

demo of 'raised' gardening app

I got to learn a lot from my own mistakes on this project, and one mistake in-particular caused such grief that I'm hoping this blogpost can serve as a word of caution to others learning about ActiveRecord Migrations.

The gist of this is that not all ActiveRecord Migrations support the change method equally (or at all). This is well-documented in the Active Record Migration docs, a section that apparently eluded my eyes before disaster struck.

The Mistake

Calling this "a mistake" is a bit misleading. It really was a chain of mistakes caused by my own lack of understanding of ActiveRecord migrations and rake commands.

This chain of mistakes started with the 37 records of my Plant model and three times this of my Note model that I needed to import into my database.

I thought I'd be cute, give myself some extra 'controlled forms in React' and 'writing routes in Rack' practice by making a form to POST this data to my back-end. A time-wasting labor that I destroyed in a single command later.

My next misstep was when I realized I had an incorrect association in my models. To fix it, I wrote a migration to remove the foreign id:

class RemoveGardenIdColumnFromPlants < ActiveRecord::Migration[6.1]
  def change
    remove_column :plants, :garden_id
  end
end
Enter fullscreen mode Exit fullscreen mode

After migrating this, I created a new model and yet another migration. I went to check my schema and realized the association from my remove_column migration was still in the Plants table, because this was not the correct way to remove an association id.

"No problem," I thought, "I'll roll it back, try again."

"HA! Not so fast!" said ActiveRecord:

===========================================================
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

remove_column is only reversible if given a type.
Enter fullscreen mode Exit fullscreen mode

Here's where things go very south for me.

It's important to note that up until this point, it was absolutely possible for me to reverse this migration and not lose my data in the process.
Taking a break before the panic starts might be the most important lesson here.

I began googling, trying things with increasing recklessness as my frustration grew. It was here that I misread a Stack Overflow question thinking it was asking how to solve the problem I was facing, and made the fatal mistake of running rake db:drop. While this did allow me to remove the migration mistakes, I also lost all of my data I had spent time submitting through that form I made.

🙃 Awesome.

Avoiding the Mistake

First and foremost: When something goes wrong, especially when working with data, STOP. Take a break. Go for a walk. Mistakes with database commands can have dire consequences that cost you precious time. And don't you dare run that #@$!-ing command until you know what it does.

Once you've stepped away from the problem, come back to it, and read advice, carefully.

In writing this post, I re-read the first answer I had come across that had told me to change the migration file to use an up and down method in the remove_column migration. I did try this, but received the same error.

The next line of that answer explained how to rollback to a specified version using rake db:down VERSION=<your-version-number-here>. I tried this, trying to rollback to the first version of my migration, which also did not work, because what this line was actually telling me was that, since I had run another migration after the remove_column, I needed to rollback to the remove_column migration, and then try to rollback with up\down implemented.

The Solution

  1. If you've created another migration after the irreversible one, rollback to the version of the irreversible migration using rake db:down VERSION=<your-version-number-here>

  2. Change the irreversible migration file to use up and down instead of change, like so:

class RemoveGardenIdColumnFromPlants < ActiveRecord::Migration[6.1]
    def up
        remove_column :plants, :garden_id 
    end
    def down
        add_column :plants, :garden_id, :integer
    end
end
Enter fullscreen mode Exit fullscreen mode

or using change:

class RemoveGardenIdColumnFromPlants < ActiveRecord::Migration[6.1]
  def change
    remove_column :plants, :garden_id, :integer
  end
end
Enter fullscreen mode Exit fullscreen mode

ActiveRecord is an awesome tool, but migrations can be a steep learning curve. Take your time, take a breath, and don't cry over dropped tables.

Top comments (0)