When maintaining an existing rails application, you have to update the rails version regularly in order to stay on a current and maintained version. While the rails team already does a great job in keeping the updates as simple as possible and explaining the required changes, the update still requires a lot of effort in bigger applications. Furthermore, if the application that you are working on is still under development, you have to coordinate the update with the other changes to avoid one large pull request that potentially has multiple merge conflicts and is hard to review.
The starting point
Before you can begin the actual update of the rails application, there are some prerequisites that you should follow:
- Update the application to the most recent minor version before the new version
- Fix all the deprecation messages that occur in your log files and within the test output
- Have a reliable and automated test suite
The main target of the approach described here is to allow an ongoing development of the application by other team members during the update phase. For us, this means avoiding a single huge update branch as it would likely result in a lot of merge errors when it’s finished and also be hard to review. In order to achieve this, we want to make as many small changes as possible that can already be applied to the old application version and merge them into the master branch as early as possible.
Step 1: Update Gemfile
Before you get to update your application code, you need to use versions of all your gems that work with the version of rails that you are updating to.
Update your Gemfile to use the new version of Rails and run bundle update rails
If you see an error message like Bundler could not find compatible versions for gem "xxx":
- Find the Gem that is causing the issue. There must be a gem which has a dependency on Rails or a part of Rails with a version smaller than the version you are updating to
- Find the Gem on rubygems.org and find a newer version of it that supports the requested version of Rails
- Create a new branch of your source code for updating this Gem only.
- Update your Gemfile to use the old version of Rails and the new version of that gem; run bundle update <gem_name>
- Find and read the Changelog of the Gem that you just updated and adjust your application code accordingly
- Run your whole test suite and adapt your application until all tests are green
- Merge these changes into your master branch
Repeat until bundle update rails succeeds
These steps assume that you will find a version of the Gem that is compatible both with the new and the old version of Rails. In our experience this is mostly the case as the Rails updates tend to be backwards compatible; however you might have difficulties if you are an early adopter of the new Rails version and the maintainers of the Gem have not yet updated their Gem.
Notice that the difficulty of that step depends on two factors:
- The number of gems you are using that have a dependency on Rails or one part of Rails, in our experience those are mostly Rails Engines (e.g. Devise, ActiveAdmin) or dependencies on ActiveRecord internals (e.g. Ransack)
- How recent the version of your gems are
Step 2: Update the source code of the application
You are now able to tackle you own application code and update it to the new version of Rails. Just as when updating the gems, try to find changes that can already be applied to the code base with the old version of Rails.
- Read the Guide for Upgrading Ruby on Rails for your specific version carefully
- If you find any changes to your code base that also work in the old version of rails, create a separate branch for them, test them and merge them into master. This also includes using gems like protected_attributes that paved the path from 3.2. to 4.0
- Talk to all team members to make them aware of these pitfalls so that no new incompatible code is generated
- If there is a backwards compatibility gem that allows the old code in the new version (like the rails-controller-testing gem when updating from rails 4.2 to rails 5.0), feel free to use it and remove it after the actual rails update has been finished
- Run rails app:update to update your config files
- Watch the changes carefully and choose only the changes you need
- Make the test suite pass (this is the hard part)
- Find the error or deprecation warning that creates the most output in the test log
- Find and repair the code to fix this issue
- Again: if you find that this change is also possible in the old version of rails: create a separate pull request and merge the changes into the master branch
Step 3: Test your application manually on a staging server
While the automatic test suite already gives confidence, you still have to test the application manually. In order to do this, you should not rely on your local development machine but use a staging server that mimics the production environment as closely as possible. Besides clicking through the whole application, you should:
- Check if all required assets are being delivered correctly (this differs between Rails environments)
- Test the integration to all external systems (they might be mocked out in the test suite).
Step 4: Deploy on production server
If you have followed the steps above, there is a good chance the final deployment works smoothly. However, there might still be some changes you have missed; you should therefore be prepared that some exceptions might occur in your exception tracking system and be ready to tackle those. You should also not schedule the deployment on a friday afternoon.