---
title: "Migrations are versioned in Rails 5"
description: "Migrations are versioned from Rails 5 onwards."
canonical_url: "https://www.bigbinary.com/blog/migrations-are-versioned-in-rails-5"
markdown_url: "https://www.bigbinary.com/blog/migrations-are-versioned-in-rails-5.md"
---

# Migrations are versioned in Rails 5

Migrations are versioned from Rails 5 onwards.

- Author: Abhishek Jain
- Published: March 1, 2016
- Categories: Rails 5, Rails

We will see how migrations in Rails 5 differ by looking at different cases.

## Case I

In Rails 4.x command

```ruby
rails g model User name:string
```

will generate migration as shown below.

```ruby
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps null: false
    end
  end
end
```

In Rails 5 the same command will generate following migration.

```ruby
class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
  end
end
```

Let's see the generated schema after running migration generated in Rails 5.

```sql
sqlite> .schema users
CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);
sqlite>
```

Rails 5 added the `NOT NULL` constraints on the timestamps columns even though
not null constraint was not specified in the migration.

## Case II

Let's look at another example.

In Rails 4.x command

```ruby
rails g model Task user:references
```

would generate following migration.

```ruby
class CreateTasks < ActiveRecord::Migration
  def change
    create_table :tasks do |t|
      t.references :user, index: true, foreign_key: true
      t.timestamps null: false
    end
  end
end
```

In Rails 5.0, same command will generate following migration.

```ruby
class CreateTasks < ActiveRecord::Migration[5.0]
  def change
    create_table :tasks do |t|
      t.references :user, foreign_key: true

      t.timestamps
    end
  end
end
```

There is no mention of `index: true` in the above migration. Let's see the
generated schema after running Rails 5 migration.

```sql
sqlite> .schema tasks
CREATE TABLE "tasks" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);

CREATE INDEX "index_tasks_on_user_id" ON "tasks" ("user_id");
```

As you can see, an index on `user_id` column is added even though it's not
present in the migration.

## Migration API has changed in Rails 5

Rails 5 has changed migration API because of which even though `null: false`
options is not passed to timestamps when migrations are run then `not null` is
[automatically added](https://github.com/rails/rails/commit/a939506f297b667291480f26fa32a373a18ae06a)
for timestamps.

Similarly, we want indexes for referenced columns in almost all cases. So Rails
5 does not need references to have `index: true`. When migrations are run then
index is [automatically created](https://github.com/rails/rails/pull/23179).

Now let's assume that an app was created in Rails 4.x. It has a bunch of
migrations. Later the app was upgraded to Rails 5. Now when older migrations are
run then those migrations will behave differently and will create a different
schema file. This is a problem.

Solution is versioned migrations.

### Versioned migrations in Rails 5

Let's look at the migration generated in Rails 5 closely.

```ruby
class CreateTasks < ActiveRecord::Migration[5.0]
  def change
    create_table :tasks do |t|
      t.references :user, index: true, foreign_key: true
      t.timestamps null: false
    end
  end
end
```

In this case `CreateUsers` class is now inheriting from
`ActiveRecord::Migration[5.0]` instead of `ActiveRecord::Migration`.

Here [5.0] is Rails version that generated this migration.

## Solving the issue with older migrations

Whenever Rails 5 runs migrations, it checks the class of the current migration
file being run. If it's 5.0, it uses the new migration API which has changes
like automatically adding `null: false` to timestamps.

But whenever the class of migration file is other than
`ActiveRecord::Migration[5.0]`, Rails will use a compatibility layer of
migrations API. Currently this
[compatibility layer](https://github.com/rails/rails/blob/434c8dc96759d4eca36ca05865b6321c54a2a90b/activerecord/lib/active_record/migration/compatibility.rb#L6-L93)
is present for Rails 4.2. What it means is that all migration generated prior to
usage of Rails 5 will be treated as if they were generate in Rails 4.2.

You will also see a
[deprecation warning](https://github.com/rails/rails/blob/434c8dc96759d4eca36ca05865b6321c54a2a90b/activerecord/lib/active_record/migration/compatibility.rb#L105-L112)
asking user to add the version of the migration to the class name for older
migrations.

So if you are migrating a Rails 4.2 app, all of your migrations will have class
`ActiveRecord::Migration`. If you run those migrations in Rails 5, you will see
a warning asking to add version name to the class name so that class name looks
like `ActiveRecord::Migration[4.2]`.

## Links

- [Human page](https://www.bigbinary.com/blog/migrations-are-versioned-in-rails-5)
