Upgrading Rails: The Dual-Boot Way at RailsConf 2021

This is a companion page to the Rails upgrade workshop at RailsConf 2021.
Here you will find a series of hands-on exercises to get started.

The main goal for this workshop is to define a roadmap to upgrade a Rails application. For all exercises you can either use our sample application, or upgrade your own application.

1. Pre-Requisites

Next Step

Pre-requisites depend on which path you want to take. You can either:

  • - Upgrade your own application, or...
  • - Upgrade our sample application

Here are the basic pre-requisites to participate with your own app:

  • - Ruby 2.3 or higher

If your application does not meet our minimum requirements, you will need to use our sample app. These are the requirements for our sample app:

2. Sample Outdated Rails App

Previous Step Next Step Back to Top

We will work with this sample application: Pecas -- an open source application to show a time tracking leaderboard for https://nokotime.com.

If you don't have Docker, please install it. Instructions here: Docker Installation

To make sure it is installed:


docker --version
Docker version 20.10.5, build 55c4c88
        

If you don't have `docker-compose`, please install it. Instructions here: Docker Compose Installation

To make sure it is installed:


docker-compose --version
docker-compose version 1.28.5, build c4eb3a1f
        

Once you have installed `docker-compose` and `Docker`, you can clone the repository:

git clone git@github.com:fastruby/pecas.git
        

Follow these steps to install the application in your local environment:

cd path/to/pecas
docker-compose build
docker-compose run web /bin/bash
./bin/setup
        

3. How outdated is our application?

Previous Step Next Step Back to Top

The first step to create the upgrade roadmap is to get an idea of how outdated our application really is. To check that we'll use the next_rails gem.


group :development, :test do
  gem 'next_rails'
  # ...
end
            

Then we will need to install:

$ bundle install
        

Let's check it out! It should give us a good idea about the shape of our dependencies:


$ bundle_report outdated
tilt 1.4.1: released almost 8 years ago (latest version, 2.0.10, released over 1 year ago)
hike 1.2.3: released almost 8 years ago (latest version, 2.1.3, released almost 7 years ago)
coffee-rails 4.0.1: released over 7 years ago (latest version, 5.0.0, released almost 2 years ago)
sass 3.2.19: released about 7 years ago (latest version, 3.7.4, released about 2 years ago)
docile 1.1.5: released almost 7 years ago (latest version, 1.3.5, released 3 months ago)
coveralls 0.7.1: released over 6 years ago (latest version, 0.8.23, released almost 2 years ago)
rails-deprecated_sanitizer 1.0.3: released over 6 years ago (latest version, 1.0.4, released about 2 months ago)
(...)

0 gems are sourced from git
74 of the 108 gems are out-of-date (69%)
            

We now know that the dependencies are 69% out of date.

`next_rails` also provides an interesting command that we could run to find known compatibility issues:


$ bundle_report compatibility --rails-version=5.0.7
=> Incompatible with Rails 5.0.7 (with new versions that are compatible):
These gems will need to be upgraded before upgrading to Rails 5.0.7.

rails-dom-testing 1.0.9 - upgrade to 2.0.3
sass-rails 4.0.5 - upgrade to 6.0.0

=> Incompatible with Rails 5.0.7 (with no new compatible versions):
These gems will need to be removed or replaced before upgrading to Rails 5.0.7.

coffee-rails 4.0.1 - new version, 5.0.0, is not compatible with Rails 5.0.7

3 gems incompatible with Rails 5.0.7
            

Now we know there will be two gems that will cause problems. We can add these gems to our TODO list.

4. Dual Boot: Setup

Previous Step Next Step Back to Top

We are going to use a helper tool for dual booting: `next_rails`.

$ next --init

This command created a symlink called `Gemfile.next` and added a helper method to the top of the `Gemfile`.

Now we can open our `Gemfile` and verify that we have a `next?` method defined in it. In the odd chance it didn't work, we can try doing it manually like this:

$ ln -s Gemfile Gemfile.next
            

Then we can add this method to the top of your `Gemfile`:


def next?
  File.basename(__FILE__) == Gemfile.next
end
            

Now we have all we need to start tweaking our `Gemfile` for the next version of Rails:


if next?
  gem 'rails', '~> 5.0.7' # our target version
else
  gem 'rails', '~> 4.2' # our current version
end
            

It's time to `bundle install` using the next version. We can use the `next` command like this:

$ next bundle install
            

If that doesn't work, you can try with:

$ BUNDLE_GEMFILE=Gemfile.next bundle install

5. Dual Boot: rails console

Previous Step Next Step Back to Top

Now that we have bundled our project with the both versions of Rails, we can start the rails console to check what errors are we going to have.

next bundle exec rails console

Now is the time to handle with the issues you may find.

6. Dual Boot: bundle exec rspec

Previous Step Next Step Back to Top

In the same way we can start running the test suite to identify possible issues.

next bundle exec rspec
            

We will probably find a problem with `twitter-bootstrap-rails` gem that will look like this:

Failure/Error: =require twitter-bootstrap-static/fontawesome
ActionView::Template::Error:
  couldn't find file 'twitter-bootstrap-static/fontawesome' with type 'text/css'
  Checked in these paths:
    /code/app/assets/images
    /code/app/assets/javascripts
    /code/app/assets/stylesheets
    /code/vendor/assets/javascripts
    /code/vendor/assets/stylesheets
    /usr/local/bundle/gems/jquery-rails-4.4.0/vendor/assets/javascripts
    /usr/local/bundle/gems/twitter-bootstrap-rails-4.0.0/app/assets/fonts
    /usr/local/bundle/gems/twitter-bootstrap-rails-4.0.0/app/assets/javascripts
    /usr/local/bundle/gems/twitter-bootstrap-rails-4.0.0/app/assets/stylesheets
    /usr/local/bundle/gems/twitter-bootstrap-rails-4.0.0/vendor/assets/stylesheets
    /usr/local/bundle/gems/coffee-rails-4.2.2/lib/assets/javascripts
    /usr/local/bundle/gems/actioncable-5.0.7.2/lib/assets/compiled
    /usr/local/bundle/gems/turbolinks-source-5.2.0/lib/assets/javascripts
# ./app/assets/stylesheets/bootstrap_and_overrides.css:6
            

This is because for bootstrap-sass gem now we need to replace require for @import.


So lets open the bootstrap_and_overrides.css file and replace this line:

=require twitter-bootstrap-static/fontawesome

with this one:

@import twitter-bootstrap-static/fontawesome

Now we should be finally ready to run all model specs.

next bundle exec rspec
            
............................

Finished in 4.04 seconds (files took 8.55 seconds to load)
37 examples, 0 failures
            

As we process the entire output from the test suite (like the sample here), we should consider a couple of things:

1. Every deprecation warning that we find should become a new story in our roadmap, like for example:


DEPRECATION WARNING: Using positional arguments in functional tests has been deprecated,
in favor of keyword arguments, and will be removed in Rails 5.1.
            

2. Every failure we find should become a new story. Be mindful to write down the potential root cause when creating the story.

Some of the failures we encounter might have a simple solution, some might take us hours or days to fix. That's why we won't fix all the issues we find today.

To find the root cause of a test failure, we will need to check the changes between Rails versions. You can find the official guides over here: https://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html

Or you can use the unofficial guides that we are maintaning over here: https://www.fastruby.io/blog/rails/upgrade/rails-upgrade-series.html

Another useful resource is RailsDiff, where we can see what changed between Rails 4.2 and Rails 5.0, for example.

7. Stay Current

Previous Step Next Step Back to Top

Deprecation warnings in the latest versions of Rails have been quite helpful in guiding us to the next version of Rails. We should treat all new deprecation warnings as exceptions:

# config/environments/test.rb

Rails.application.configure do
  # Raise on deprecation notices
  config.active_support.deprecation = :raise
end
            

That way we can turn _noise_ into many _signals_. Deprecation warnings can easily be ignored when they're just a message in `test.log`. They can't be easily ignored if they break your test suite.

Another great resource is to add bundler-audit gem as a dependency and make sure to run a check every time you run your test suite. We can do this by tweaking your `Rakefile`:


# Rakefile

require File.expand_path('../config/application', __FILE__)
require 'rake'

Rails.application.load_tasks

task default: %i[
  bundle:audit brakeman:check
  spec spec:javascripts cucumber
]
            

Now everytime you run `bundle exec rake`, it will not only run your test suite but also run `bundle:audit` which does this:


namespace :bundle do
  desc "Audit bundle for any known vulnerabilities"
    task :audit do
      unless system "bundle-audit check --update"
        exit 1
      end
    end
  end
end
            

Finally you can keep up with the latest version by using the Rails `main` branch:


# Gemfile

source 'https://rubygems.org'

git_source(:github) do |repo_name|
  repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
  "https://github.com/#{repo_name}.git"
end

if next?
  gem 'rails', github: 'rails/rails'
else
  gem 'rails', '~> 5.2'
end
            

You can find instructions on how to set this up in your CI server over here: https://fastruby.io/blog/upgrade-rails/dual-boot/dual-boot-with-rails-6-0-beta.html

At this point you should have a solid list of items in your roadmap. You can start addressing them one by one with many, tiny pull requests.

8. Questions?

Previous Step Next Step Back to Top

If you have any questions, you are welcome to reach out to us in Discord or sending us a message at FastRuby.io.
You can also find us on Twitter at: @etagwerker, @cleicar2 and @lubc32

9. Thank you!

Previous Step Back to Top

You are wonderful! Thanks for participating in my workshop. I hope you can apply all of this in your next Rails upgrade project! :)