3 simple tips/stuff to use migrations in a better way.

Clément Morisset
3 min readDec 26, 2019
 rails generate migration AddSomethingToSomething
rails db:migrate
rails db:rollback
etc..

Are you bored to only use these commands ?

I have always been frustrated to use migrations daily without trying to make the most of them. In this way I would like share 3 small tips/stuff I found interesting.

#1 Test your migrations

Sometimes it sounds overkill especially for basic migration, even though it could be useful to test a specific behaviour.

Let’s take the following migration:

class AddIndexClubAndSeasonToPurchaseOrder < ActiveRecord::Migration[5.2]
def up
add_index :purchase_orders,
[:club_id, :season_id],
name: "index_purchase_orders_club_and_season_unique",
unique: true
end
def down
remove_index :purchase_orders, [:club_id, :season_id]
end
end

Here we’re simply adding an scoped index on the purchase_orders table. Testing could be as simple as the code above:

require Rails.root.join('db', 'migrate', '20191225180138_add_index_club_and_season_to_purchase_order.rb')describe AddIndexClubAndSeasonToPurchaseOrder, :migration do
let(:migration) { described_class.new }
before do
ActiveRecord::Migration.verbose = false
migration.down
end
it '#up' do
expect(migration.index_exists?(:purchase_orders,[ :club_id, :season_id], name: 'index_purchase_orders_club_and_season_unique')).to be false
migration.up
expect(migration.index_exists?(:purchase_orders,[ :club_id, :season_id], name: 'index_purchase_orders_club_and_season_unique', unique: true)).to be true
end
it '#down' do
expect(migration.index_exists?(:purchase_orders,[ :club_id, :season_id], name: 'index_purchase_orders_club_and_season_unique')).to be false
end
end

Several step from top to bottom:

  1. You need to require your migration file
  2. Before run your test you can optionnally disable verbose to keep your logs clean and above all you need to rollback the current migration to test it.
  3. Migrations are just a class with methods. Like up method, you can test it by expecting that your index exist after to have run you migration
  4. Same things for down !

If you want to know more about testing migration you absolutely need to check it out the Gitlab repository: https://gitlab.com/gitlab-org/gitlab-foss/tree/master/spec/migrations

#2 Use bulk migration

Some migrations run on huge table, it can be heavy and slow to execute. In order to avoid this waste of time you can use the bulk option. The following will produce only one (= faster) ALTER TABLE statement:

class AddAllUserFields < ActiveRecord::Migration[5.1]
def change
change_table :users, bulk: true do |t|
t.string "nickname"
t.string "personal_email"
t.string "zip_code"
.....
t.text "medical_certificate"
t.text "business_card"
t.boolean "emailing"
t.string "picture_uid"
t.string "country"
end
end
end

Protips: Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.

#3 Organize your migrations !

Can you imagine a second your app tree without folders ? An ugly blend between controller, model and other service without clear distinction. Why keep a messy migration folder ?

Perhaps you have already work on projects with a huge background which means a lot of migrations. You can organize your migration of different manners, the most usually used is the sort by year but I like to sort my migrations like my spec. One folder for one class. So each migration concerning a class/model like User will go in the user folder.

It’s not a big deal and It take your project forward. :)

That’s it for me. Let me know if you have any question or if you want to share your own tips.

--

--