Rails comes with a toolkit for managing migrations: the changes you will make to your application’s database as you develop an application. Enabling agility in your process, migrations allow you to continually change the database definition (the schema) as you develop. This can only be possible in the context of an Agile environment, and conversely, Agility can only be possible with the right tools to make changes to your database as you.
That’s why Migrations and Agility go together like peas and carrots. In the alternative styles of software, (that is, Big-Design-Up-Front or “BDUF”) the general mindset of software development is to begin by defining the schema completely up-front and there are no migrations.
With its embrace of change, the Rails-Agile approach lets you make database migrations any time you want. There’s only one caveat: downtime. For most applications, a short period of unavailability in the early mornings or late at night is acceptable. For larger projects, a no-downtime migration strategy becomes necessary. In short, you then need to move towards a two-step deploy process. This beginner article covers only what you need to know about running Rails migrations with standard short downtime periods which is sufficient for most small-to-medium-sized applications.
Reloading After Rails Migrations
Always remember that the Rails migrations are a little “sacred” to Rails. But there isn’t an actual mystery to it: After you change the database, always remember to tell Rails to reload. How you do that differs depending on the version of Rails you are on.
Rails 3.2
User.connection.schema_cache.clear!
User.reset_column_information
(in this example, assume User object related to the table where you made the database modification)
For Rails 4.0, 4.1, 5.0, 5.1, and 5.2, you need to call
User.reset_column_information
The migrations are integrated with the deploy process. This is why you should keep in mind is that the reload happens by each of your webservers. On a 12-Factor platform like Heroku, the answer is often simply to restart the app entirely (which will restart all of the app’s running dynos on Heroku’s grid). If your deploy is more complicated, you can orchestrate the reloading of the column information on each dyno after deploy. (Because that’s a headache, typically you just restart the application which will tell ActiveRecord to reload its cache.)
As of Rails 6, you no longer need to do this.
Defaults In Rails Migrations — Works Even with .new
(Rails 5.2+)
Starting with Rails 5.2, defaults apply to both the database and to the objects you create with .new
. That’s handy because it means you can just define those defaults in one place.
A Funny Thing About the ActiveRecord::Migration (Rails 5.0 and up)
In the older days of Rails, all migrations were subclasses of ActiveRecord::Migration. Starting in Rails 5, the class name itself is appended with a bracket and the ActiveRecord version (that is, the Rails version) where that migration came from. This is to make sure you don’t try to run a old migration with unsupported syntax on newer versions of Active record, because that API (the old syntax you used) could be deprecated. Migrations created in a Rails 5.0 app look like
ActiveRecord::Migration[5.0]
Many Migration Tricks
Remember you can do more than just field name change in migrations check out
change_column
add_index
remove_index
rename_table
Sometimes too much is a good thing.
When you define a :string as a field, for example, the :name field on the fruits table, you can specify to the database what type it will use by specifying the limit:
in the migration. This is slightly counter-intuitive because you don’t actually specify tiny, text, mediumtext, or longtext.
What’s interesting here is that automatically pick the smallest necessary type from the text types implemented by your database that would be able to accommodate the value. It rounds up the limits for whichever database you are using. For MySQL, this is:
- Tiny 256 bytes (string equivalent in Rails)
- Text 65,535 bytes (text equivalent in Rails if you don’t specify a limit, or your limit < 65535)
- Medium Text 16,777,215 bytes (If you specify text type and a limit > 65535 but < 16777215)
- LongText 4,294,967,295 bytes (If you specify text type and a limit > 16777215 up to the LongText maximum)
Remember, when you need to change the storage of your underlying database field, there’s no need to drop and add the field back (loosing the data).
change_column :fruits, :name, :string, limit: 1000000
Raw SQL
Some people like their steaks raw. If you need SQL, you need SQL. Your migrations class implements a handy method called
execute
for just this purpose. For example, you might want to execute to put some fancy indexes on your tables, or adding other views that are non-standard. I also personally really like to have the fields on my database ordered in a logical way. (For example, similar fields grouped together.) If you follow most migration practices, your fields are added in the order they were created by your developers over the lifetime of your application. For this reason, I like to do this after creating a new column
execute("ALTER TABLE fruits MODIFY xyz varchar(20) AFTER some_other_column")
In this example, I would run this just after adding the xyz
column to the fruits
table. Importantly, you need to know that I would have to specify the type of the column again (even though I just specified it). The only thing this achieves is to move the field (column) xyz
to after the column some_other_column
, which as I said, keeps your table looking nice and tidy.
I hope you’ve enjoyed this tips & tricks for Rails Migrations.