We’ve open sourced shopify-ruby-definitions, which contains definitions to build the Ruby we use at Shopify in development, testing, and production. Our builds of Ruby are API compliant with the vanilla Rubies, but with additional bug fixes and performance improvements we’ve backported from the next major version.

Why we use custom builds of Ruby

Shopify has one of the largest and highest traffic Ruby and Rails deployments in the world. We encounter bugs and performance bottlenecks in Ruby that very few other companies experience, so we’re constantly working on fixing bugs and improving performance. We’ve worked on performance features such as YJIT, Variable Width Allocation, and Object Shapes, just to name a few.

However, Ruby’s release schedule is once a year, on Christmas Day. This meant that we often have to wait several months before we can experience the performance improvements that our features provide. Additionally, we debug crashes collected from our production environments and often write fixes to make Ruby and gems more stable. Ruby releases a patch release with backported fixes once every few months, but we would like to have these fixes applied as soon as possible.

By backporting bug fixes and some performance improvements, we can switch Ruby builds and deploy these improvements to production in a matter of hours. This also gives us a faster feedback loop on the production impact of these improvements, so we can make Ruby even better by the time of release.

Why we don’t run on Ruby head

You might be asking: all of these issues can be alleviated by running on Ruby head, the development version of Ruby. You’d be right. However, running on Ruby head brings some other issues, including:

  • We cannot use precompiled native gems. Some native gems (e.g., nokogiri, gRPC) are precompiled for common Ruby versions and systems. Since Ruby head does not have a stable API, there are no precompiled native gems, which means that we have to compile them when installing the bundle. This adds several minutes to our CI pipeline.
  • We cannot cache compiled native gems due to unstable API in Ruby. The development version of Ruby could change, deprecate, and remove parts of the C API, which would make previously compiled gems not compatible, so we need to rebuild native gems frequently, which adds many minutes to our CI.
  • Ruby head is constantly adding deprecations and features that are subject to change before release, so we would need to frequently make changes to our codebase to ensure compatibility.
  • New features and improvements are constantly being committed in Ruby, so we’re not confident about the stability to run it in production.

We do run nightly CI against Ruby head on our monolith to test the stability of Ruby head and to ensure compatibility of our monolith.

We also run experiments on Ruby head on a small portion of traffic on some production systems to test real-world performance of our improvements.

Using Shopify’s Ruby builds for your app

You can run your app on Shopify’s Ruby builds by installing with rbenv’s ruby-build tool. Instructions for setup are in the repository.