At Shopify, we make keeping our dependencies up to date a priority. Having outdated dependencies exposes your project to security issues and contributes towards technical debt. Upgrading a large dependency like Rails can be overwhelming, if you are lost and don’t know where to start, you can read our post explaining our journey to upgrade Rails from 4.2 to 5.0 and you can watch the Upgrading Rails at scale recording from RailsConf 2018.
Our Core monolith, used by millions of users, has been running the unreleased version of Rails 6 in production since February 2019. Going forward, our application will continuously run the latest revision of the framework. There are multiple advantages for us and for the community to be living on the Edge of Rails that we’ll cover through this blog post.
The Edge of Rails is the Rails master branch which includes everything up to the very newest commit. Living on the Edge of Rails means that anytime a change is introduced upstream it becomes available in our application. We no longer need to wait multiple months for a new release.
Targeting the HEAD of Rails
Another advantage of targeting the HEAD of Rails is to cut down the time it takes for us to upgrade. Continuously integrating Rails with a small, weekly bump instead of a big one every year, reduces the size of the diff. We also realized that developers are more inclined to contribute to Rails and implement ideas they have if they can use the feature they wrote right away.
Rafael Franca, member of the Rails at Shopify team and a Rails core contributor, as well as release manager, runs our continuous integration (CI) against the framework before any new release or before merging an important change upstream. By being able to run our massive test suite composed of more than 130,000 tests, we're able to discover edge cases, find improvements where needed and propose patches upstream to make Rails better for everyone.
Zeitwerk gives Shopify a shout out for helping improve their gem
We're already seeing the positive impact this has for the Ruby and Rails community. One example is our close contribution to Zeitwerk, the new autoloader that ship with Rails 6.
Updating to the Latest Revision
Solid Track, a bot that upgrade Rails on a weekly basis to the latest revision upstream
Targeting the HEAD of Rails means that we now need to periodically bump it to the latest revision. To avoid manual steps, we created Solid Track, a bot that upgrade Rails on a weekly basis to the latest revision upstream. The bot opens a Pull Request on GitHub and pings us with a diff of the changes introduced in the framework.
Every Monday, we receive this ping and go over the new commits merged upstream and check if something that our CI didn’t catch could break once in production.
If CI is green, it’s usually good to ship. It’s possible that our test suite didn’t catch a possible issue, but we mitigate the risk thanks to the way we deploy our application. Each time we deploy, only a subset of our servers get the new changes. We called those servers “canaries”. If no new exceptions happen on the canaries for ten minutes, our shipping pipeline proceed and deploy the changes to all remaining servers.
Solid Track bot triggering git bisect
However, if CI is red, our bot automatically takes care of triggering a git bisect
to determine which change is breaking the test. This step allows us to save time and instantly identify which commit is problematic. Then we need to determine whether the change is legit or it introduced a regression upstream
Should My Application Target the HEAD of Rails?
If targeting the HEAD of Rails is something you’d be interested in doing in your application, keep in mind that using an unreleased version of any dependency comes with a stability tradeoff. We evaluated the risk in our application and were confident in our tooling, test suite and the way we deploy to take this decision.
Here are the questions and answers we asked ourselves before moving forward:
1. How much you and your team will benefit from targeting HEAD?
We’ll get a lot out of this. Not only we’ll be able to get all bug fixes and new features quickly, we’d also save time and won’t have to dedicate a whole team and months of work to upgrade our application.
2. Do you have enough monitoring in place in case something goes wrong?
We have a lot of monitoring. Exception reporting on Slack, Datadog metrics correctly configured with threshold when a metric is too high/low and 24h on call rotation.
3. Do you have a way to deploy your application on a small subset of servers?
We use canary deploys to put in production the changes only a small subset of servers.
4. Finally, how confident are you with your test suite?
Our test suite is large and coverage is good. There is always room for improvement but we're confident with it.
Upgrading your dependencies is part of having a sane codebase. The more outdated they are the more technical debt you accumulate. If you have outdated dependencies, consider taking some time to upgrade them.We’re always looking for awesome people of all backgrounds and experiences to join our team. Visit our Engineering career page to find out what we’re working on.