Upgrading Rails is easy, right? Sure, as long as you are upgrading your patch version. A Rails upgrade for a big application is not a trivial project: It took GitHub a year and a half to upgrade from Rails 3.2 to 5.2.
While upgrades have become easier with every new Rails version, your application has only become more complicated with every new dependency you added.
In this workshop you will learn a proven Rails upgrade process for major and minor version changes of Rails. You will leave this workshop with a roadmap to upgrade your Rails application.
3. #RailsUpgrades | https://fastruby.io/rc2022
Senior Engineering
Manager
at FastRuby.io
I’m a Senior Engineering Manager at FastRuby.io. Over the past 25 years I’ve worked in various roles - from Developer, to Product Owner, to Director - for
ActBlue, The University of Pennsylvania, Stanford University, Ask Jeeves, E*Trade, and others. I’m passionate about helping teams improve and I've led
several Agile transitions. And I’ve been working with Rails for the past 10 years.
12. #RailsUpgrades | https://fastruby.io/rc2022
🐦 #RailsUpgrades 🐦
If you want to tweet about this workshop, use the hashtag #RailsUpgrades. We’re always looking for feedback on our process and ways to improve this
workshop.
13. #RailsUpgrades | https://fastruby.io/rc2022
Why do we need
this workshop?
This workshop is for anyone who needs to upgrade a Rails application. At FastRuby.io, we have performed over 80 Rails upgrades, and have
fi
xed
thousands of old dependencies and evaluated countless migrations. Today we’ll be sharing with you some of the strategies we’ve developed over the
years to perform Rails upgrades ef
fi
ciently and reliably.
If you are in this workshop, it would serve you best if you have your computer and an outdated Rails application in front of you.
If you don’t have a Rails app, feel free to stay and take notes. I have a list of outdated, open source, Rails applications, for you to use during the workshop.
14. #RailsUpgrades | https://fastruby.io/rc2022
`bundle update`
should be easy
But can’t you just run bundle update? Maybe, but often it’s not enough. We often have large, complex applications with a multitude of dependencies, and
bundle update can’t address every issue.
15. #RailsUpgrades | https://fastruby.io/rc2022
bundle update rails ?
So often what happens is you try to run bundle update, and you spend days or weeks trying to sort out problems that come up. Then your company needs
you to start delivering features again, and you decide to put off the upgrade to another day. And then that day never comes, because there are too many
other things you have to do. Then you end up running an old version of Rails in production, which poses security risks and imposes limitations on your
development, as you are tied to old gems and old ways of doing things.
16. #RailsUpgrades | https://fastruby.io/rc2022
Ruby For Good:
Refuge Restrooms
In this workshop we’re going to show you a gradual, step-by-step way to upgrade. And the best way to do that is to work with a real life application.
We’re going to upgrade an application from Ruby For Good called Refuge Restrooms. It’s an open source application that indexes and maps safe
restroom locations for trans, intersex, and gender nonconforming individuals. It’s currently on Rails 5.2 and we’re going to upgrade it to 6.0.
18. #RailsUpgrades| https://fastruby.io/rc2022
7 Exercises:
1. Prerequisites: Docker and Git
Refuge Restrooms is con
fi
gured to use Docker. So we’ll make sure you have git, docker, and other prerequisites set up on your computer if you don’t have
them already.
20. #RailsUpgrades| https://fastruby.io/rc2022
7 Exercises:
1. Prerequisites: Docker and Git
2. Setup with docker-compose
3. Introduction to next_rails
Then we will set up the `next_rails` gem. It’s a gem we maintain at FastRuby.io, and it will prove useful for all your future rails upgrades. It will help with
things like dual booting and
fi
nding incompatibilities with future versions of Rails.
21. #RailsUpgrades| https://fastruby.io/rc2022
7 Exercises:
1. Prerequisites: Docker and Git
2. Setup with docker-compose
3. Introduction to next_rails
4. Start the dual boot setup
Then we are going to start the set up for Refuge Restrooms to boot with two different versions of Rails. The current 5.2 version, and 6.0, which is the
version we want to upgrade to.
22. #RailsUpgrades| https://fastruby.io/rc2022
7 Exercises:
1. Prerequisites: Docker and Git
2. Setup with docker-compose
3. Introduction to next_rails
4. Start the dual boot setup
5. Finish the dual boot setup
The next_rails gem lets us try running bundle update with the next version of Rails, without disturbing the current version. When we try it, we’ll run into an
issue in Refuge Restrooms that needs to be
fi
xed, and we’ll work with you to resolve it.
23. #RailsUpgrades| https://fastruby.io/rc2022
7 Exercises:
1. Prerequisites: Docker and Git
2. Setup with docker-compose
3. Introduction to next_rails
4. Start the dual boot setup
5. Finish the dual boot setup
6. Run the Rails console & run the tests
We’ll open the Rails console in each version of Rails, to con
fi
rm the dual boot is working. Then we’ll run the tests in both versions. We’ll see a deprecation
warning for Rails 6.
24. #RailsUpgrades| https://fastruby.io/rc2022
7 Exercises:
1. Prerequisites: Docker and Git
2. Setup with docker-compose
3. Introduction to next_rails
4. Start the dual boot setup
5. Finish the dual boot setup
6. Run the Rails console & run the tests
7. Find and
fi
x a class autoloading issue
Lastly, we’ll try to upgrade to the new class autoloader in Rails 6, and we’ll encounter an error, and
fi
x it.
25. #RailsUpgrades| https://fastruby.io/rc2022
fastruby.io/
rc2022
There’s a companion page for this workshop at fastruby.io/rc2022 which describes all the steps. You’ll see the link for it the footer of every slide, so it’s
easy for you to
fi
nd at anytime during the workshop. If you bookmark it, you can also refer to it later.
26. #RailsUpgrades| https://fastruby.io/rc2022
Steps:
1. Explanation
2. Questions
3. Exercise time
For each step in the upgrade process, we’ll provide an explanation for why we want to do it, how we’ll do it, and what you should expect to see. Then
we’ll answer any questions, and after that you can do the step yourself. There will be 5 to 15 minutes for each exercise, and we’ll be here to help you
individually if you get stuck or have any problems completing the step.
34. #RailsUpgrades | https://fastruby.io/rc2022
Docker setup
Once we checked all the pre-requisites, we can start setting up the sample application with Docker
TODO: Explain a bit about Docker and why we’re using it here
35. #RailsUpgrades| https://fastruby.io/rc2022
$ git clone https://github.com/fastruby/refugerestrooms
$ cd refugerestrooms
$ git checkout start-exercise-1
To do that,
fi
rst clone the refuge restrooms repo from Github
And once it
fi
nished downloading, go to that folder
36. #RailsUpgrades| https://fastruby.io/rc2022
If you are on a Mac with an M1 chip, you will need to run:
$ docker-compose -f docker-compose.yml
-f docker-compose.mac-m1.yml up --no-build
Then Ctrl-C when it's done. And run:
$ docker-compose -f docker-compose.yml
-f docker-compose.mac-m1.yml run web /bin/bash
/About This Mac
If you are on a Mac with an M1 chip, you will need to run these commands
37. #RailsUpgrades| https://fastruby.io/rc2022
If you are NOT on a Mac with an M1 chip:
$ docker-compose run web bash
To setup the Docker image you can run this command
This will take a few minutes to run, depending on the WI-FI connection. Last time I tried it took me around 7 minutes
40. #RailsUpgrades | https://fastruby.io/rc2022
How outdated is
our application?
Before we start making changes that will get us closer to the next version of Rails, we want to know how outdated is our application.
That way we can have a more clear idea of how far we are from the next version of Rails.
44. #RailsUpgrades| https://fastruby.io/rc2022
# Gemfile
group :development, :test do
gem 'next_rails'
end
To install this gem you simply add it to your Gem
fi
le, like this.
Keep in mind that you'll need Ruby 2.3 or higher for it to work. Our sample application runs on ruby 2.7 so we’re good
45. #RailsUpgrades| https://fastruby.io/rc2022
$ bundle
Fetching gem metadata from https://rubygems.org/..........
...
Fetching next_rails 1.0.5
Installing next_rails 1.0.5
...
Bundle complete! 50 Gemfile dependencies, 163 gems now
installed.
And then you run `bundle` in the terminal to install the gem.
46. #RailsUpgrades| https://fastruby.io/rc2022
1) bundle_report outdated
2) bundle_report compatibility —rails-version=6.0.5
next_rails commands
So, now that we have the next_rails gem installed, we are able to run these 2 di
ff
erent commands:
47. #RailsUpgrades| https://fastruby.io/rc2022
1) bundle_report outdated
2) bundle_report compatibility —rails-version=6.0.5
next_rails commands
The 1st one is `bundle_report outdated`, which outputs all your gems from oldest to newest with speci
fi
c release details on each of the gems.
48. #RailsUpgrades| https://fastruby.io/rc2022
$ bundle_report outdated
puma 5.6.2: released Jan 1, 1980 (latest version, 5.6.4,
released Jan 1, 1980)
formtastic_i18n 0.6.0: released Mar 10, 2016 (latest version,
0.7.0, released May 23, 2021)
…
rails 5.2.6.3: released Mar 8, 2022 (latest version, 7.0.3,
released May 9, 2022)
bundler 2.1.4: released Mar 29, 2022 (latest version, 2.3.13,
released May 4, 2022)
0 gems are sourced from git
105 of the 163 gems are out-of-date (64%)
It looks like this when you run it.
As you can see, for each gem it shows:
49. #RailsUpgrades| https://fastruby.io/rc2022
$ bundle_report outdated
redcarpet 3.0.0: released over 5 years ago (latest version,
3.4.0, released over 2 years ago)
bootstrap-sass 3.3.7: released over 2 years ago (latest
version, 3.4.1, released about 2 months ago)
...
mail 2.7.0: released over 1 year ago (latest version, 2.7.1,
released 6 months ago)
activesupport 5.1.6: released about 1 year ago (latest
version, 5.2.3, released 11 days ago)
railties 5.1.6: released about 1 year ago (latest version,
5.2.3, released 11 days ago)
0 gems are sourced from git
65 of the 102 gems are out-of-date (64%)
- When was it released
50. #RailsUpgrades| https://fastruby.io/rc2022
$ bundle_report outdated
redcarpet 3.0.0: released over 5 years ago (latest version,
3.4.0, released over 2 years ago)
bootstrap-sass 3.3.7: released over 2 years ago (latest
version, 3.4.1, released about 2 months ago)
...
mail 2.7.0: released over 1 year ago (latest version, 2.7.1,
released 6 months ago)
activesupport 5.1.6: released about 1 year ago (latest
version, 5.2.3, released 11 days ago)
railties 5.1.6: released about 1 year ago (latest version,
5.2.3, released 11 days ago)
0 gems are sourced from git
65 of the 102 gems are out-of-date (64%)
- What's the latest version
51. #RailsUpgrades| https://fastruby.io/rc2022
$ bundle_report outdated
redcarpet 3.0.0: released over 5 years ago (latest version,
3.4.0, released over 2 years ago)
bootstrap-sass 3.3.7: released over 2 years ago (latest
version, 3.4.1, released about 2 months ago)
...
mail 2.7.0: released over 1 year ago (latest version, 2.7.1,
released 6 months ago)
activesupport 5.1.6: released about 1 year ago (latest
version, 5.2.3, released 11 days ago)
railties 5.1.6: released about 1 year ago (latest version,
5.2.3, released 11 days ago)
0 gems are sourced from git
65 of the 102 gems are out-of-date (64%)
- And how old is that version
52. #RailsUpgrades| https://fastruby.io/rc2022
1) bundle_report outdated
2) bundle_report compatibility —-rails-version=6.0.5
next_rails commands
And the 2nd command is `bundle_report compatibility`, which will tell you what are the gems that won't work with the version of rails that you want to
upgrade to. In this example it's Rails 6.0.5
53. #RailsUpgrades| https://fastruby.io/rc2022
$ bundle_report compatibility --rails-version=6.0.5
=> Incompatible with Rails 6.0.5 (with new versions that are compatible):
These gems will need to be upgraded before upgrading to Rails 6.0.5.
dotenv-rails 2.2.2 - upgrade to 2.7.6
1 gems incompatible with Rails 6.0.5
It looks like this when you run it.
The output shows what gems will not work with that rails version, but have a version that works
And it can also show what gems will not work and don't have a newer version of that gem that is compatible. But this speci
fi
c project doesn’t have any gem
like that so it’s not showing this section in the output
54. #RailsUpgrades| https://fastruby.io/rc2022
Exercise #3:
Time: 5 to 10 minutes
1) Install next_rails gem
2) Run bundle_report outdated
3) Run bundle_report compatibility —-rails-version=6.0.5
https://fastruby.io/rc2022#3
So now let's take a few minutes to install next_rails, and run these two commands in your application
Also you can go to the link on screen to see the things I just shared in the slides
55. #RailsUpgrades | https://fastruby.io/rc2022
Dual Boot:
Setup
The next step toward upgrading your Rails application is setting up Dual Booting.
This allows you to run your application with two di
ff
erent versions of Rails. The one that you currently have, and the want you want to upgrade to.
The main bene
fi
t of this is that you can easily see what parts of your code won't work with the next version of Rails and need to be updated.
The next_rails gem has a command for doing this setup
59. #RailsUpgrades| https://fastruby.io/rc2022
# Gemfile
def next?
File.basename(__FILE__) == "Gemfile.next"
end
$ next --init
And the 2nd thing `next --init` does is that adds this useful method to our Gem
fi
le that we can use in our code. We'll see how we can use it after the next
exercise.
60. #RailsUpgrades| https://fastruby.io/rc2022
Exercise #4:
Time: 5 minutes
1) Run next --init
2) Check that a Gemfile.next was created
3) Check that you have the next? method in your Gemfile
https://www.fastruby.io/rc2022#4
So now let's take a few minutes to run `next --init`
If for some reason the command does’t work for you. You can add those things manually. That link has instructions on how to do it.
63. #RailsUpgrades| https://fastruby.io/rc2022
$ next bundle update rails
...
Bundler could not find compatible versions for gem "railties":
In Gemfile.next:
dotenv-rails (~> 2.2.1) was resolved to 2.2.2, which depends on
railties (>= 3.2, < 6.0)
rails (~> 6.0.5) was resolved to 6.0.5, which depends on
railties (= 6.0.5)
If you see something like this, means you'll need to add the `next?` conditional to the gems that are con
fl
icting, which in this case is dotenv-rails
66. #RailsUpgrades| https://fastruby.io/rc2022
Exercise #5:
Time: 5 to 10 minutes
1) Use the `next?` method in the Gemfile
2) Run next bundle update rails dotenv-rails
https://www.fastruby.io/rc2022#5
So now let's take a few minutes to run `next bundle install`
67. #RailsUpgrades | https://fastruby.io/rc2022
Run the
Rails console
One of the things we can try doing now is to run the rails console with the next version of rails
By doing this, we’re are gonna be exercising the initializers and environment
fi
les, and we’ll be able to see if if they raise any error caused by the version of
rails we’re using
68. #RailsUpgrades| https://fastruby.io/rc2022
68
$ bundle exec rails console
Loading development environment (Rails 5.2.6.3)
irb(main):001:0>
$ next bundle exec rails console
Loading development environment (Rails 6.0.5)
irb(main):001:0>
So here there are the two options to access to the console
69. #RailsUpgrades| https://fastruby.io/rc2022
69
$ bundle exec rails console
Loading development environment (Rails 5.2.6.3)
irb(main):001:0>
$ next bundle exec rails console
Loading development environment (Rails 6.0.5)
irb(main):001:0>
The
fi
rst one will run it with your current version of Rails
70. #RailsUpgrades| https://fastruby.io/rc2022
70
$ bundle exec rails console
Loading development environment (Rails 5.2.6.3)
irb(main):001:0>
$ next bundle exec rails console
Loading development environment (Rails 6.0.5)
irb(main):001:0>
And the other with with the next version of Rails
72. #RailsUpgrades| https://fastruby.io/rc2022
72
$ bundle exec rspec
.........................................................
Finished in 58.22 seconds (files took 2.49 seconds to load)
64 examples, 0 failures
We can run the tests with the current version of rails
fi
rst, which shouldn’t have any failure in the sample application
73. #RailsUpgrades| https://fastruby.io/rc2022
73
$ next bundle exec rspec
DEPRECATION WARNING: Class level methods will no longer inherit scoping
from `current` in Rails 6.1. To continue using the scoped relation,
pass it into the block directly. To instead access the full set of
models, as Rails 6.1 will, use `Restroom.default_scoped`. (called from
block in <class:Restroom> at /refugerestrooms/app/models/
restroom.rb:47)
.........................................................
Finished in 35.61 seconds (files took 3.04 seconds to load)
64 examples, 0 failures
And then we can run them with the next version rails, which in this case, since it’s a small app, with not a lot of tests, it doesn’t have any failures either
The only difference in the output is that there’s a deprecation warning, but we don’t have to worry about that yet. That would be need to be addressed
once we’re on 6.0 and we’re ready to upgrade to 6.1
74. #RailsUpgrades| https://fastruby.io/rc2022
Every deprecation warning
is a story
One thing to note about Deprecation Warnings is that for every unique deprecation warning that you see in your application, should create a story or a
ticket in your rails upgrade roadmap.
In the bonus section of the companion page we explain how to use a feature that next_rails has to track deprecation warning
75. #RailsUpgrades| https://fastruby.io/rc2022
Exercise #6
Time 5 to 10 min
1)Run bundle exec rails console
2)Run next bundle exec rails console
3)Run bundle exec rails rspec
4)Run next bundle exec rspec
https://www.fastruby.io/rc2022#6
A last thought on the deprecation warnings. We’re not going to
fi
x them right now. These are deprecation warning that were introduced in Rails 6.0, which
means they should be
fi
xed as part of an upgrade to Rails 6.1
76. #RailsUpgrades| https://fastruby.io/rc2022
Strategy for
fi
xing deprecation warnings
when doing multiple version jumps in a row
For example, going from Rails 5.1 to 6.1:
• 5.1 to 5.2:
fi
x Rails 5.1 deprecation warnings
• 5.2 to 6.0:
fi
x Rails 5.2 deprecation warnings
• 6.0 to 6.1:
fi
x Rails 6.0 deprecation warnings
• Then
fi
x Rails 6.1 deprecation warnings
A
fi
nal thought on deprecation warnings…
This helps you keep organized with how you
fi
x deprecations warnings, especially if you have not addressed deprecation warnings in your current version
of Rails. In the interests of time, we are going to skip
fi
xing the new Rails 6.0 deprecation warnings we saw, and instead focus on the error that you’ll see in
a minute. But on the companion page for this workshop, there’s a bonus section at the end, which explains how to
fi
x the deprecation warnings.
77. #RailsUpgrades | https://fastruby.io/rc2022
Dual booting
& debugging
an error
As we move to the next step in the Rails 6 upgrade, we’ll encounter an error that needs to be
fi
xed. For this part of the workshop, please follow along
with me during each slide, instead of waiting for the exercise at the end.
79. #RailsUpgrades| https://fastruby.io/rc2022
You should see an error, after the deprecation
warning
An error occurred while loading ./spec/models/
rating_level_spec.rb.
Failure/Error: require File.expand_path('../
config/environment', __dir__)
NameError:
uninitialized constant API
Did you mean? Api
Why did this happen?
82. #RailsUpgrades| https://fastruby.io/rc2022
Using Ruby WITHOUT Rails, you have to
explicitly load class files before you can
use the class
# For example, you need to:
require 'workers/observation'
# Before you can:
Observation = Workers::Observation.new
85. #RailsUpgrades| https://fastruby.io/rc2022
Some of the changes
with Zeitwerk are not
backwards compatible
Zeitwerk was designed to be as compatible as possible with the classic autoloader. But there are some incompatibilities, and we’ll encounter one in this
project.
86. #RailsUpgrades| https://fastruby.io/rc2022
Let’s get the tests to pass with the
“classic” autoloader
# In config/application.rb, add:
config.autoloader = :classic
# The tests should pass again now:
$ next bundle exec rspec spec/models/
The original Rails autoloader is now called the “classic” autoloader. Let’s temporarily switch to using it with Rails 6.
87. #RailsUpgrades| https://fastruby.io/rc2022
87
Now let’s update the app for Zeitwerk!
# In config/application.rb, change:
config.autoloader = :zeitwerk
# Then run:
$ next bin/rails zeitwerk:check
# You should see an error that’s similar
# to the one we saw in the tests
88. #RailsUpgrades| https://fastruby.io/rc2022
How do we fix it?
See the Acronyms section of Classic to Zeitwerk HOWTO
# In config/initializers/inflections.rb add:
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym "API"
end
# The check should pass now:
$ next bin/rails zeitwerk:check
# And the tests should pass again now:
$ next bundle exec rspec spec/models/
Say more about how acronyms are handled in the classic autoloader vs Zeitwerk
89. #RailsUpgrades| https://fastruby.io/rc2022
89
How do we make this work for Rails 5.2 and Rails
6, so we can dual boot?
# Let’s define a $next_rails global var
# in the Gemfile:
def next?
$next_rails = File.basename(__FILE__) == "Gemfile.next"
end
93. #RailsUpgrades| https://fastruby.io/rc2022
Start with a
test suite
you can trust
We start with a test suite you can trust.
It has no
fl
aky tests.
Above 50% code coverage.
And it runs fast. So you can get quick feedback.
94. #RailsUpgrades| https://fastruby.io/rc2022
One minor version
jump at a time
Then you start with one minor version jump at a time.
This will start a long running branch that will include the dual booting code.
95. #RailsUpgrades| https://fastruby.io/rc2022
...
✅ 2.3 👉 3.0
✅ 3.0 👉 3.1
✅ 3.1 👉 3.2
✅ 3.2 👉 4.0
✅ 4.0 👉 4.1
✅ 4.1 👉 4.2
✅ 4.2 👉 5.0
✅ 5.0 👉 5.1
✅ 5.1 👉 5.2
✅ 5.2 👉 6.0
✅ 6.0 👉 6.1
✅ 6.1 👉 7.0
Gem
fi
le Gem
fi
le.next
{ }
If you start in Rails 2.3 we would recommend this to get to Rails 6.0
You do NOT WANT to do Rails 2.3 to 6.0
102. #RailsUpgrades| https://fastruby.io/rc2022
For every jump:
bundle install
102
For every version jump you want to make sure that you can bundle install the dependencies.
Then run the entire test suite without any failures.
Finally you want to smoke test your application. In some version jumps you might
fi
nd that the CSS or JS is broken but your test suite won’t catch it.
103. #RailsUpgrades| https://fastruby.io/rc2022
For every jump:
bundle install
bundle exec rake test
For every version jump you want to make sure that you can bundle install the dependencies.
Then run the entire test suite without any failures.
Finally you want to smoke test your application. In some version jumps you might
fi
nd that the CSS or JS is broken but your test suite won’t catch it.
104. #RailsUpgrades| https://fastruby.io/rc2022
For every jump:
bundle install
bundle exec rake test
smoke test & deploy
For every version jump you want to make sure that you can bundle install the dependencies.
Then run the entire test suite without any failures.
Finally you want to smoke test your application. In some version jumps you might
fi
nd that the CSS or JS is broken but your test suite won’t catch it.
105. #RailsUpgrades| https://fastruby.io/rc2022
1. Tweak
2. Test
3. Merge
And
Iterate
As you start working on the upgrade, you want to focus on one small change or patch at a time.
Fix that deprecation warning, test that everything works
fi
ne, merge it, and deploy it.
And start over again. And again. And again.
126. #RailsUpgrades | https://fastruby.io/rc2022,
We are
hiring!
(ombulabs.com/jobs)
If you are interested in this sort of work, please reach out to us or
fi
nd us in the hallway
track. Mike, Luciano, and I would be happy to answer any questions about this.
127. #RailsUpgrades | https://fastruby.io/rc2022,
If your company
needs help
upgrading Rails
(fastruby.io)
If you need help with your upgrade and don’t have the time, feel free to contact us.
We are always looking for challenging projects to upgrade.