Originally posted on Hint's blog.
Rails 6 is right around the corner, 6.0.0.rc1 was released in late April. Is your app ready for the latest version of Rails? Follow these best practices to make your upgrade go as smoothly as possible.
Make a Plan
The first step of any upgrade should be to determine your current Ruby and Rails versions, then collect data about your code, test suite, and their dependencies. Part of your planning will be to decide what can be done before the upgrade and what can be put off until after. You always want to limit the scope of the changes, so when errors occur it is much simpler to nail down the change(s) that caused the errors.
Note: We recommend migrating to new Rails conventions sooner rather than later. Too many teams choose to put off these migrations, which often means not coming back to them until they are forced by the next major Rails upgrade.
Ruby and Rails
Rails 5.x requires Ruby 2.2.2 or newer, while Rails 6.x requires Ruby 2.5.0 or newer. Run
ruby -v from within your project to see what version of Ruby your app is on. If you're on an old version, then you will need to upgrade Ruby before you upgrade Rails.
5.2.3 is the current stable version. We recommend upgrading to the latest patch version of the minor version your app is on before taking the next step in the upgrade. For example, if your app is on
5.2.0 then you will update to
5.2.3. There should not be any issues making this patch update since patch releases are only bug and security fixes. The Rails Core Team states they will make API and feature changes for security fixes, so this is the reasoning of validating the latest patch version.
When reviewing your app, one of the first things to do is check the production logs for any deprecation warnings. Over the years, the Rails Core Team has implemented a strong deprecation policy, so these are very helpful warnings to clear up before the upgrade. Many gem developers have implemented similar deprecation policies, so don't ignore those while doing this work.
Note: All too often, we see that people have disabled deprecation warnings in the production and test environments. Be skeptical if you don't see any deprecations in those log files.
Review any monkeypatches as they will most likely need to be modified to work with the next version of Rails. This is a great time to look at new Rails APIs to decide if there is a Rails way to replace them.
Look for any uses of undocumented Rails methods. The Rails Core Team considers any undocumented method as private and changeable at any time. If you discover any, make sure to review the changes in the next version of Rails. This is a great time to decide if this code currently necessary or if you should replace the usage with a more solid solution.
A solid automated test suite is invaluable when performing an upgrade; tests ensure that your app executes the same way before and after the upgrade.
How many lines of code are covered? Get your latest coverage stats and look for the holes. Test coverage isn't everything, but is a metric to help ensure your code paths are being exercised by your test suite. Your test suite doesn't just validate that needed changes return the same results, it also exercises the code paths of the app which will bubble up deprecation warnings. If you have little or low test coverage, or you are generally not confident in the suite, now is the time to invest in strengthening it.
Many surprises could be lurking for you in your
Gemfile. The best way to get a clear picture of which dependencies will need to be upgraded alongside Rails is to use Bundler to step through the upgrade, with the goal of a successful build.
Let's say your app is currently locked at Rails
5.1.7. Your first step to getting to Rails 6.0 will be to bump Rails to
5.2.3 (currently the latest in the
5.x series) then run
bundle update rails.
Bundler will attempt to bundle the app against Rails
5.2.3 but will most likely require other gems to be updated.
The output from Bundler can be daunting, but for the sake of brevity let's say one gem that needed to be updated is
devise. Once you find a compatible version of
devise you continue to run
bundle update adding each gem to the list as necessary until you get the app to bundle. Once it is bundled on
5.2.3 you would follow the same pattern to upgrade to Rails 6.0.
Now that you have a dependency roadmap it is time to review which gems can be upgraded now and get them into production. It is especially important in the case of gems that require syntax changes to upgrade them separately from the Rails upgrade. If the gem is not required to be upgraded then now is not the time to upgrade the gem and it should be added to the post-upgrade list of projects.
Since you have collected all the data, let's review some things that should be tackled before jumping into the actual upgrade. Each of these steps should go through your production release process separately.
- Upgrade Ruby to a compatible version for the target version of Rails
- Update Rails to the current patch version
- Resolve all deprecation warnings
- Ensure compatibility of monkeypatches or calls to private Rails methods
- Expand the test suite where needed
- Review your gem dependency roadmap and upgrade gems that are compatible with your current version of Rails
With the pre-upgrade work done and in production it is time to begin upgrading Rails.
Bundle and Boot
The first step is to use the dependency roadmap as a guide to get the app to bundle with the next version of Rails. Once it is bundled you need to work on getting the app to boot. We generally try to run the test suite since that is what will be used to iterate changes needed to support the next version of Rails.
At this point, there will probably be errors due to configuration that must be changed to be compatible with the new version of Rails. The official Upgrade Ruby on Rails guide is a good place to look for these needed Rails configuration changes.
Iterate, Iterate, Iterate
Now, you can start iterating on the many test failures you undoubtedly have at the moment. You will benefit by starting at low-level tests like unit specs and working up to browser specs. Next, parse the output of the test suite and group the failures by similar type to be fixed at the same time. While iterating over the test suite, do your future-self a favor and make small commits with verbose commit messages. A commit message like
fixed spec is not going to be helpful when you have a production error and you need to know why you had to make that change in the first place.
Once you have a green test suite, you can move on to clearing up deprecation warnings. Again, parse the output of the test suite and group the deprecations by type - then start iterating on them.
With a green test suite and deprecation warnings cleaned up, it is time to follow your process for getting new code into production!!!
After the upgrade is in production and any issues have been resolved, it is time to circle back around to the items you held off on earlier. Where possible, opt-in to new configuration, migrate to new tools and features of Rails (ActionText, Webpacker, parallel testing), and update gems to the latest version you're able to.
If you would rather stay focused on shipping features, let us build a plan, expand your test suite, or perform the entire upgrade. Contact us at UpgradeRails.com to tell us more about your upgrade.
Top comments (4)
I've been mostly responsible for Rails upgrade projects in my team the last 4 years or so. In that time we've moved through all the versions from 4.0 to now 6.0.0.rc and I think this was by far the easiest update I've done, even including the minor release updates.
It seems like there has been effort by the Rails core team to make them more straight forward for app using the Rails Way.
Did you create a new rails6 project and moved all features?
If not how you manged the project structure change ?
No, we work within the current app to upgrade. We have found most new features or changes are opt-in.