Website loading

A Quick Guide to Ruby & Rails Version Upgrades

Blogpost-featured-image
publisher-image
Victor
Head of Web Development
Jul 15, 2022 • 5 min

We recently needed to upgrade a project’s Ruby version from 2.6.6 to 3.x and the Rails version from 6.0.4.8 to 7.x. The task's scope proved to be much more demanding than expected, as we encountered plenty of challenges, some better documented than others. So if you are going through a similar process, this article will include all the main changes that we had to deal with. ðŸ—’️

Short disclaimer: this does not include the changes behind the hood that Ruby and Rails have done with their upgrades. These are widely covered, and we highly recommend these resources: upgrading Ruby, upgrading Rails. The mentioned guides also include some implementation changes that need to be done. This is the main goal of the article as well, but we’ll also cover some unexpected situations that are not included there.

 

Let’s start with Ruby ðŸ’Ž

We started with the Ruby upgrade, and we strongly recommend everyone does the same. First of all, Rails 7 requires Ruby 2.7.0 or a newer version, and we only had 2.6.6. Then, many of the gem updates will depend on their compatibility with Ruby 3, and overall, most of the work had to be done with this version upgrade.

Let’s get started! ðŸš€

We are using rbenv, so updating the version on the computer was pretty easy. After installing Ruby 3.1.2 (in this case) and updating it in the project Gemfile, try to run “bundle install” and then run the tests. Unfortunately, gem incompatibilities pop up immediately. 

This is the first takeaway we want to talk about. Having some gems versions locked for more than one year probably means that those versions are incompatible with Ruby 3. You will probably know which dependencies need an update, but we’ll cover three specific examples:

  • Rspec-rails - this is the testing gem of choice on our project, and it needed an upgrade to version 5+ on our project.
  • Faker - we mostly use this gem for specs & factories. Upgrading the Faker version did come with some changes, as all the methods now needed keyword arguments to work properly (Faker::Number.unique.number(digits: 2) instead of Faker::Number.unique.number(2)).
  • Psych - this was a bizarre issue. A very detailed explanation can be found here. In short, Ruby 3.0 comes with Psych 3, while Ruby 3.1 comes with Psych 4, which has a major breaking change; the fix for us (at least for the Ruby upgrade) was to add this - gem ‘psych’, ‘< 4 - to the Gemfile, to enforce psych version under 4.x; that also meant that we had to change the default version from the computer for psych from 4.x to 3.3.2 (gem install --default -v3.3.2 psych and then follow this comment to remove the 4.0.4 version).

There may be many other incompatible gems from your Gemfile that you might not use or that might have a different version. These are the ones that required the most work on our side and are also the most widely used.

After advancing from the gems’ incompatibilities, we had to ensure that the new Ruby 3 updates were also applied on the project. One of the main changes we had to deal with was a change when using positional or keyword arguments (more details here).

For example, we had: let(:params) { key1: ‘value1’, key2: ‘value2’ } & service_base_child.call(params). This had to be changed to service_base_child.call(**params).

Finally, we could move to the easy task: updating Docker & CircleCI images.

 

Upgrading Rails ðŸ›¤ï¸

Our version for Rails (and its dependencies) was 6.0.4.8. Because of security vulnerabilities and small changes, this was updated a few times since the start of the project. Still, like with the Ruby version, the updates were incremental and very small. Fortunately, the Rails version upgrade does not produce so many incompatibilities, and most of the changes were syntax, config, or implementation changes rather than gem updates.

Fortunately for us, the Ruby on Rails site has a great guide on how to upgrade the Rails version. We won’t cover what’s already there, but some smaller things aren’t as smooth as expected. âš ï¸

So the first step also covered in the guide is updating the Gemfile and the gems, and then running rails app:update. This will trigger a set of changes in many of the files already present in the project. We suggest allowing inserts/updates for all of the files and manually going through each git diff to see whether the changes make sense. Many changes are just updating single quotes to double quotes but also expect some critical changes. Also, be careful if the update removes any specific settings, like action mailer config.

From the Rails upgrade, we only had five main takeaways/updates to take care of. Of course, there were some smaller changes, like some other gem upgrades, but they are easy to catch and handle.

  • Inside the application.rb file, the config.load_defaults value should be 7.0. This will load the default configuration Rails has set up for version 7+
  • Updating to the new Active Record encryption - this means changing everything that used attr_encrypted to this new implementation. Fortunately for us, we only used attr_encrypted for some third-party APIs access tokens, so we did not need to migrate instances in our database. We could just delete them and then generate them again. If you need to persist that data, some special steps need to be taken into consideration; this guide describes how to do that. But for us, writing up a migration to remove columns encrypted_access_token & encrypted_access_token_iv, which were required attributes for the attr_encrypted gem, and also add a new attribute, access_token, as a string. Also, attr_encrypted :access_token, key: :some_key will now become encrypts :access_token. The next step would be running rails db:encryption:init, which will then generate a set of keys to be added to the credentials. Also, we found that adding this line config.active_record.encryption = Credentials[:active_record_encryption] to each environment file is required for the Rails app to find and use these values for the encryption.
  • errors[:base] << 'foo' needs to be replaced with errors.add(:base, 'foo').
  • If, like us, you are using bullet, I recommend upgrading bullet to the latest version and making sure no n+1 warnings are thrown. In my case, it looks like the new version caught about 10 cases that weren’t considered n+1 queries before. Double checking that might save some seconds from the next query.
  • Also, if you are using CarrierWave and have attachments, be mindful that the attachment.file#filename method is deprecated, so you should change that to attachment.file#identifier. Also, Kernel#open now should be refactored to URI.open, or even better to URI.parse#open.

 

Conclusions ðŸ’¡

For us, working on the upgrade brought to light three main key takeaways, and these are the reason we decided to write about this topic:

  • Write tests! Having great test coverage has made my job so much easier. Changing some methods because of deprecation, migrating to the new encryption system, or upgrading some libraries? Just run the test suite. If everything passes and the coverage is good, you can confidently say the change was successful. Otherwise, you can easily miss some things because “there’s nothing new here”, but errors can still pop out.
  • Take your time. For us, this task took longer than I expected. Many situations like inconsistencies or incompatibilities will appear, and they are very particular to each project, previous versions, and the number of dependencies. Also, after an update like this, you should thoroughly test the API or the app to make sure nothing escaped unnoticed. In our case, the Kernel#open issue did slip by initially.
  • Think about the reason to upgrade. As we mentioned above, it is a long task. Having the latest version and the latest security updates and so on might be good, but major version changes need consideration. We should all upgrade ongoing projects at some point, but choose that timing carefully.

We want to thank you for your interest and hope this guide will help you with your own upgrades. We prepare articles like these whenever our community or we run into unexpected difficulties and no helpful resources. So, if there’s a technical problem you’re battling, don’t hesitate to shoot us a message, and we’ll do our best to help! ðŸ‘

ruby on rails

tech insights & news

blog

Stay up to date with the tech solutions we build for startups, scale-ups and companies around the world. Read tech trends and news about what we do besides building apps.