Dealing With Schema Changes in Rails 7

Dealing With Schema Changes in Rails 7

When you upgrade a Rails application from Rails 6.1 to 7.0, you may suddenly see a lot of changes in the schema.rb file and wonder where those changes come from and how to deal with them.

In this post, we look at what those changes are, and how to deal with them when upgrading a Rails application.

What are the changes?

When you upgrade a Rails application to Rails 7.0, you will start seeing a lot of differences in the schema.rb file. There are mainly 2 kinds of changes.

First, you will start seeing the Rails version in square brackets along with the ActiveRecord::Schema class. It will look like this: ActiveRecord::Schema[7.0].define(version: 2021_12_17_090228)

Second, you will start seeing precision being added or removed in a lot of places in the schema.rb file. It may look something like this:

   create_table "tasks", charset: "utf8mb4", force: :cascade do |t|
-    t.datetime "deleted_at"
+    t.datetime "deleted_at", precision: nil
-    t.datetime "created_at", precision: 6, null: false
+    t.datetime "created_at", null: false
-    t.datetime "updated_at", precision: 6, null: false
+    t.datetime "updated_at", null: false

Precision changes in Rails 7

Rails now generates datetime columns with a default precision of 6. So that means t.datetime calls that don’t set the precision will have a default value of 6. But if you want to fallback to the default precision of the database, or set any specific value other than 6, you will need to use precision: nil to fallback to the database default, or set precision: 4 to use a specific precision value (4 in this case).

Before Rails 7, the default value of the precision was nil. So if your schema looked like this before Rails 7:

t.datetime "deleted_at"

Since this did not have any custom value of precision, it was internally being set to nil. Now when you upgrade to Rails 7, the default value is 6, so we need to explicitly set the precision: nil in the updated schema file. So the above line changes to:

t.datetime "deleted_at", precision: nil

What about when you had to set the precision to 6 like this?

t.datetime "deleted_at", precision: 6

Now when you upgrade to Rails 7, since 6 is also the default value of precision in Rails 7, we do not need to set this explicitly anymore. So the same line in the updated schema becomes this:

t.datetime "deleted_at"

And now if you look at the overall diff in the schema file, the changes would make sense.

   create_table "tasks", charset: "utf8mb4", force: :cascade do |t|
-    t.datetime "deleted_at"
+    t.datetime "deleted_at", precision: nil
-    t.datetime "created_at", precision: 6, null: false
+    t.datetime "created_at", null: false
-    t.datetime "updated_at", precision: 6, null: false
+    t.datetime "updated_at", null: false

ActiveRecord schema class name changes in Rails 7

Since Rails 7.0 changed the default precision value, now if you were to upgrade an application from Rails 6.1 to 7.0 and load the schema, there would be a mismatch between the production schema and local schema. To avoid this problem Rails introduced the use of Rails version along with the schema class name.

It also froze the use of ActiveRecord::Schema to Rails 6.1 implementation. And from Rails 7.0 onwards, the ActiveRecord::Schema should be followed by the Rails version and it will look like this: ActiveRecord::Schema[7.0].

You can see the implementation here opens a new window .

How to update the schema file?

Now that we know that upon upgrading an application to Rails 7, the schema file will need to be updated with the correct class name and precision values for the datetime columns. There are a few ways to update the schema file.

The first option is to run the rails app:update command and among other things, it will update the schema.rb file with the changes. Sometimes you may choose not to run the app:update command opens a new window and for such scenarios, there is another way to update the schema.

The second option is to add a migration and run that migration. When you do that, it will update the whole schema file with the changes in migration and all the other changes related to the precision and the ActiveRecord::Schema class name as well. While this is an option, you do not have to add a migration just so you can update the schema for Rails 7. The third option solves for this.

The third option is to run the rails db:schema:dump with Rails 7 and it will update the schema file with the changes needed.

While you have multiple ways to update the schema file, it is also important to acknowledge that you do not have to update the schema file during the upgrade at all. Especially if you are dual booting the Rails application with a version running in Rails 6.1 and another running in Rails 7.0, the updated schema file will become unusable in Rails 6.1. In this case, you can migrate the app completely to Rails 7.0 and once you have removed the dual booting or the app is running on version >= 7.0 in dual boot, you can update and commit the schema file changes.

Conclusion

You can choose one of the approaches to update the schema file or decide against updating it. But if you do update the schema file, the important part is to not be surprised when you see a lot of changes in the schema.rb file. Choose an approach that works for you.

PS: Need help upgrading Ruby or Rails to their latest stable version? We have some availability to help your team upgrade Ruby/Rails opens a new window !

Get the book