Quick notes on upgrading Rails
Upgrading Rails version doesn’t have to be stressful. I have gone through the process a few times over the years without major issue. Here I have put together a basic ‘how to’ with some of the tips that have helped me complete the process successfully over the year.
Do
- Read every guide and readme related to rails and the gems used in the codebase
- Look up the meaning of the config options on the Rails site if you are unfamiliar, both for new and old options
- Make lots of notes in a public place to leave a paper trail for yourself and your team
- Read the console logs
- Separate work into many PRs that can be merged before opening the main upgrade PR. The goal is to keep it as small as possible
- Talk to your team about what is happening, ask if you have questions
- Check the change lists for any gems/packages that need to be upgraded, and do so with the usual care
Don’t
- Lump everything in one huge PR
- Ignore warnings
- Blindly update gems and packages
Steps
- Read the Official Rails migration guide! Especially the section for this specific version upgrade. It will note any aspect that must be addressed, as well as how to address it. Make a list of these, then check if they apply to your codebase . If they do apply, then make a note of where. Also if they can be addressed before the upgrade (i.e. the change is not dependant on the next version of Rails), then do so in a separate PR.
- Ensure rails is the latest patch of the current version, and upgrade if not. i.e. 7.0.3 → 7.0.8
- Run the app and check the logs for deprecation warnings. These will need to be addressed before starting teh upgrade.
- Set the rails gem version in the Gemfile to the next version set with a pessimistic version constraint to the next minor version, e.g.
- Moving from Rails 6.1 to 7.0;
gem "rails", "~> 6.1"
→gem "rails", "~> 7.0"
- Moving from Rails 7.0 to 7.1;
gem "rails", "~> 7.0"
→gem "rails", "~> 7.1"
- Moving from Rails 6.1 to 7.0;
- Run
bundle update rails
- This will uncover gems that have incompatible dependencies with the next version of Rails. Review the gems and see if they can be upgraded in separate PRs before continuing the Rails upgrade.
-
Run
./bin/rails app:update
to start the upgrade task, an interactive CLI that will check specific config files. Don’t change the value ofconfig.load_defaults
yet!-
If the file doesn’t match the expected, it will prompt for an action:
Y - yes, overwrite n - no, do not overwrite a - all, overwrite this and all others q - quit, abort h - help, show this help d - diff, show the differences between the old and the new m - merge, run merge tool
Always check the diff. But if
THOR_MERGE=code
is set, then selecting merge will open the existing file and a new copy with a timestamp containing the default for the next version. There will probably be specific options set that should kept enabled, or at least reviewed. (noteTHOR_MERGE="code -d $1 $2"
will allow using VScode’s merge tool) -
It will also generate a file,
config/initializers/new_framework_defaults_X_Y.rb
, whereX
is the major version andY
is the minor. This will be important later.
-
- Run
./bin/rails db:migrate
, this will update the schema for the new version - If using Rubocop, set the
TargetRailsVersion
in.rubocop.yml
. Check if any other tools track the targetted Rails version and update them too. - Run the app and see if it boots OK. Click around and test you favourite features. Do they work? Are there any strange outputs in the logs? Make a note. If the fix works with the previous Rails version then go fix in another PR and then rebase.
- Set any required ENVs in either environment dotfiles or rails credentials depending on the codebase. These will also need to be set for all deployed environments.
- Push the branch and see if it gets a green build in CI. If there are any failed specs, find the cause and make a note. If the fix works with the previous Rails version then go fix it in another PR and then rebase.
- Read the
config/initializers/new_framework_defaults_X_Y.rb
file generated by theapp:update
task. Enable each new option and check the related functionality works and that the branch still passes CI. Use discretion as some features will need to be tested in a deployed environment. - Run sanity tests for any specific feature changes in local. i.e. 7.0 introduced a new way to hash cookies that would invalidate user sessions if a cookie rotator was not implemented. So, we checked on local if 6.1 session would be retained if the server was upgraded to 7.0 locally.
- Repeat sanity tests on deployed environments.
- Get more of the team involved.
At this stage the upgrade should be ready to release, everyone is happy, and you can relax a little. Probably.