Upgrade rails from 4.2 to 5.0
Upgrading rails to a new major version can be tricky because there might be many breaking changes from the framework itself or from many gems used in project that will become either deprecated or unsupported. However, it can also go smooth if done in a right procedural way. This post shares the procedure based on my recent hands-on experience in upgrading rails from 4.2.11
to 5.0.7.2
for our API server. Please be kindly noted that depending on project configuration or dependencies, the upgrading steps might be slightly different for various projects.
Prerequisites
- Read the official General Advice of upgrading rails to get a rough idea on how to proceed and what needs to be done;
- Read the upgrading guide specific to our case: Upgrading from Rails 4.2 to Rails 5.0;
- Confirm ruby version meets requirement, we use ruby
2.4.5
which is fine (Rails 5 requires ruby 2.2.2+); - Check all versions of rails, based on the guide from general advice and the current rails version used (
4.2.11
), determine the target version to go:5.0.7.2
; - Based on the guides, break down the upgrading process to small tasks and try to limit each task to deal with only one topic.
Strategies
- Run all rspec tests in the
master
branch and make sure all tests pass; - Create a new branch based on
master
and name itrails_upgrade_master
for example; - For each of the small tasks, branch from
rails_upgrade_master
to deal with one topic at a time; - Create PR after the task is completed (e.g. breaking code fixed, API replaced with new ones, etc);
- Ask at least 2 other team members to review each PR and merge into
rails_upgrade_master
after approval; - After all the upgrading tasks are solved and merged into
rails_upgrade_master
:- Merge
master
into therails_upgrade_master
to catch up if by this time themaster
has been ahead for awhile; - Run all rspec tests on
master
again to confirm all tests pass; - Run all rspec tests on
rails_upgrade_master
and make notes for the failed tests (in our case, after all previous tasks were solved, there were only 18 failed tests out of 900+ tests); - Create tasks to deal with the failed tests and merge into
rails_upgrade_master
; - Until all rspec tests pass: congratulations! You're one step away to a successful upgrading;
- Merge
- Last but not the least, create tasks for regression testing all the major features and ask more people to help in the regression testing (Well if you have QA team, that's another story...):
- If there are any failed cases found during regression testing, fix them in the
rails_upgrade_master
; - Until all regression testing is completed and no more problems found;
- If there are any failed cases found during regression testing, fix them in the
- Finally, merge the
rails_upgrade_master
intomaster
and cheers!
Steps
-
Update
Gemfile
andGemfile.lock
- Set
gem 'rails', '~> 5.0.7', '>=5.0.7.2'
inGemfile
; - Run
bundle update rails
to try updating rails only first:- if it succeeds, then congratulations all your gems should work for rails 5;
- if it fails (in our case) probably due to dependency resolution, then you should run
bundle update
and this would probably upgrade other gems too which also means possible more tasks to deal with breaking changes;
- After successful dependency resolution, run
bundle install
to make sureGemfile
andGemfile.lock
both are updated.
- Set
-
Update configuration and binary files to rails 5
This might be the hardest step. Below was the procedure I followed:
- Create a fresh project in rails
5.0.7.2
with default configuration; - Compare content one by one for each of the files under
bin/
andconfig/
directories between the current version and the rails 5 fresh project; - Note each file for operation such as:
- To skip: meaning to keep the old version without changes;
- To override: meaning to replace with new version completely;
- To merge: meaning to manually merge changes.
- Now run
rails app:update
and follow what was noted; - References:
- https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#the-update-task
- https://guides.rubyonrails.org/5_0_release_notes.html#railties-deprecations
- Create a fresh project in rails
-
Update models and mailers to follow new conventions
- Create
ApplicationRecord
as base class and apply for all models; - Create
ApplicationMailer
as base class and apply for all mailers.
- Create
-
Update model relationship changes related to
belongs_to
- For all
belongs_to
declarations withoutrequire: false
option, addoptional: true
option; - Comment out or set value to
true
inconfig/initializers/new_framework_defaults.rb
forRails.application.config.active_record.belongs_to_required_by_default
option.
- For all
-
Update model callback chains halting logic
- For all models check the
before_xxx
callbacks and for any method that relied onreturning false
to halt the chains, replace withthrow(:abort)
; - Set value to
false
inconfig/initializers/new_framework_defaults.rb
forActiveSupport.halt_callback_chains_on_return_false
option.
- For all models check the
-
Update existing migration scripts
- For all existing migrations, update the class definitions to
class SomeMigration < ActiveRecord::Migration[4.2]
; - References:
- https://stackoverflow.com/a/35930912
- For all existing migrations, update the class definitions to
-
Update params in controllers
- For all params used in controllers, confirm or update so that all mass assignments are protected by strong parameters with
params.require.permit
; - For all params used in controllers, confirm or update so that no hash methods (e.g.
map
orlength
and etc.) are called onparams
becauseparams
is no longer inheritingHashWithIndifferentAccess
; - For all params used in controllers, confirm or update so that all nesting objects and their attributes are also applied with strong parameters protection;
- https://github.com/rails/rails/issues/9454
- https://github.com/rails/rails/issues/9454#issuecomment-280853714
- For all params used in controllers, confirm or update so that all mass assignments are protected by strong parameters with
-
Run all rspec tests against the
rails_upgrade_master
branch- Make notes and create tickets to address the failed tests
-
Address issues found by failed tests - may be project specific
- Replace
Sass.compile
withSassC.new.render
as the former is deprecated in rails 5:- https://sass-lang.com/blog/ruby-sass-is-deprecated
- https://github.com/sass/sassc-rails/issues/105
- https://github.com/rails/sass-rails/issues/315
- https://rubygems.org/gems/sass
touch: true
stops working in associated objects, solution: manually setinverse_of
forbelongs_to
andhas_many
pairs in models associations:- https://stackoverflow.com/a/46452718
- https://github.com/rails/rails/issues/26726
- https://github.com/rails/rails/issues/7567
- https://github.com/paper-trail-gem/paper_trail/issues/874
- https://github.com/rails/rails/pull/29132
- https://rossta.net/blog/use-inverse-of.html
- https://www.viget.com/articles/exploring-the-inverse-of-option-on-rails-model-associations/
- Time comparison related tests failed due to precision; switch to compare time change with
be_within(1.second).of
:- https://stackoverflow.com/a/26207378
- https://github.com/rails/rails/issues/33715
- https://tddium.wordpress.com/2011/08/07/rails-time-comparisons-devil-details-etc/
- https://gitlab.com/gitlab-org/gitlab-foss/-/issues/48430
- https://blog.bigbinary.com/2016/02/23/rails-5-handles-datetime-with-better-precision.html
- Replace
-
Run regression testing and solve failed cases if any - may be project specific
- Issues related to
ActiveRecord::ConnectionTimeoutError: could not obtain a database connection within 5.000 seconds (waited 5.015 seconds)
:- https://stackoverflow.com/questions/38004673/connection-pool-for-rails-5
- https://qiita.com/katsuyuki/items/42b3c69bcd76c44ad64a
- Issues related to
skip_callback
in rake tasks:- https://github.com/thoughtbot/factory_bot/issues/931
- Issues related to
<<
orpush
not working forrelation
object:- http://techspry.com/ruby_and_rails/active-records-or-push-or-concat-method/
- https://github.com/rails/rails/issues/25906
- https://github.com/rails/rails/issues/27998
- Issues related to
-
Generate and commit
schema.rb
inrails_upgrade_master
- Run
rails db:schema:dump
to generateschema.rb
with new version; - Merge
rails_upgrade_master
intomaster
.
- Run
-
Cheers!