I'm looking for remote Ruby / Rails job!
You'll never walk alone.
How to upgrade a big Rails application
Why should we upgrade applications?
- Software development issues
- Security issues
- Dependency hell
- New software
- Improved APIs
- New features
- Performance
- Involving new people
- Developers
- Novice Developers
About upgrading applications
Upgrading an application especially a big one is a complicated task, but it shouldn’t be so. It should be easy!
It’s an opportunity to learn something new.
It’s a good chance to contribute to Open Source.
Long-running branch strategy
Long-Running Branches Considered Harmful - New Relic Blog: https://blog.newrelic.com/culture/long-running-branches-considered-harmful/
Shopify’s Rails upgrade story
In '17 it took us 1y to upgrade from @rails 4.2 to 5.0. @kirshatrov @rafaelfranca @DaroudeDudek were happy but not satisfied. Why doesn't this take a week instead of a year? Today, we are running @rails 6.0 in production *before* it releases.
— Jean-Michel Lemieux (@jmwind) February 27, 2019
Shopify is one of the largest Ruby on Rails codebases in existence - Shopify Engineering Blog
RailsConf 2017: Upgrading a big application to Rails 5 by Rafael França
Link: https://www.youtube.com/watch?v=I-2Xy3RS1ns
Upgrading Shopify to Rails 5: https://engineering.shopify.com/blogs/engineering/upgrading-shopify-to-rails-5-0
Dual boot strategy
Dual boot is the process of booting your application with a different set of dependencies.
Dual boot: BUNDLE_GEMFILE
# Gemfile
gem "rails", "~> 5.2.2"
eval_gemfile "Gemfile.common"
# Gemfile.next
gem 'rails', '~> 6.0.0.beta3'
eval_gemfile "Gemfile.common"
# Gemfile.common
source "https://rubygems.org"
ruby "2.6.2"
# ...
$ bundle install # Gemfile.lock
$ BUNDLE_GEMFILE=Gemfile.next bundle install # Gemfile.next.lock
$ rails runner "p Rails.version"
"5.2.2.1"
$ BUNDLE_GEMFILE=Gemfile.next rails runner "p Rails.version"
"6.0.0.beta3"
Dual boot: BUNDLE_GEMFILE
You need to deal with three Gemfiles and the confusion that comes with it:
Gemfile
, Gemfile.next
, Gemfile.common
vs. Gemfile
You need to ensure that all the lockfiles are in sync whenever a developer updates or adds a dependency:
$ bundle install # Gemfile.lock
$ BUNDLE_GEMFILE=Gemfile.next bundle install # Gemfile.next.lock
vs.
$ bundle install # Gemfile.lock, Gemfile.next.lock
https://github.com/Shopify/bootboot
Bootboot is a Bundler plugin meant to help dual boot your ruby application.
Installation
- In your
Gemfile
, add thisplugin "bootboot", "~> 0.1.2"
- Run
bundle install
- Run
bundle bootboot
- Commit the
Gemfile
and theGemfile_next.lock
Dual boot: Shopify/bootboot
diff --git a/Gemfile b/Gemfile
-gem 'rails', '~> 5.2.2'
+if ENV['DEPENDENCIES_NEXT']
+ gem 'rails', '~> 6.0.0.beta3'
+else
+ gem 'rails', '~> 5.2.2'
+end
$ DEPENDENCIES_NEXT=1 bundle update rails # Gemfile_next.lock
$ rails runner "p Rails.version"
"5.2.2.1"
$ DEPENDENCIES_NEXT=1 rails runner "p Rails.version"
"6.0.0.beta3"
Dual boot: Make it work
$ rails test
$ DEPENDENCIES_NEXT=1 rails test
if ENV['DEPENDENCIES_NEXT']
def execute
# ...
end
else
def execute
# ...
end
end
Dual boot: Rollout to production
$ RAILS_ENV=production rails server
$ DEPENDENCIES_NEXT=1 RAILS_ENV=production rails server
Upgrading GitHub to Rails 3 with Zero Downtime: http://shayfrendt.com/posts/upgrading-github-to-rails-3-with-zero-downtime/
Upgrading a Rails application incrementally: http://www.recursion.org/incremental-rails-upgrade/
Dual boot: Gradual rollout to production
# https://nginx.org/en/docs/http/load_balancing.html#nginx_weighted_load_balancing
upstream myapp1 {
server srv1.example.com weight=99;
server srv2.example.com weight=1;
}
srv1.example.com:~/myapp1$ RAILS_ENV=production rails server
srv2.example.com:~/myapp1$ DEPENDENCIES_NEXT=1 RAILS_ENV=production rails server
If you have lots of servers in production, you can just roll out the app with the next dependencies on 1% of servers and on the 99% servers run the app with the current dependencies and gradually change the ratio.
Once you are 100% in production with the app that uses the next dependencies, then it’s time to remove all if ENV['DEPENDENCIES_NEXT']
conditions from the codebase and prepare to the next upgrade.
RailsConf 2018: Upgrading Rails at Scale by Edouard Chin
Link: https://www.youtube.com/watch?v=N2B5V4ozc6k
Eliminate deprecations from your codebase: https://github.com/Shopify/deprecation_toolkit