ActiveRecord Migration Rollback: learning the pitfalls of the `change` method the hard way
Tags: activerecord, ruby, flatiron, beginners
Published: 2021-08-09 01:39:07

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](https://raised.netlify.app/). 

![demo of 'raised' gardening app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/23x7me3q0jreshp8eo2u.gif)

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](https://guides.rubyonrails.org/active_record_migrations.html#using-the-change-method), 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:

```ruby
class RemoveGardenIdColumnFromPlants < ActiveRecord::Migration[6.1]
  def change
    remove_column :plants, :garden_id
  end
end
```

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.
```

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:

```ruby
class RemoveGardenIdColumnFromPlants < ActiveRecord::Migration[6.1]
    def up
        remove_column :plants, :garden_id 
    end
    def down
        add_column :plants, :garden_id, :integer
    end
end
```

or using `change`:

```ruby
class RemoveGardenIdColumnFromPlants < ActiveRecord::Migration[6.1]
  def change
    remove_column :plants, :garden_id, :integer
  end
end
```

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__.