What To Expect From Ruby 3.3

Ruby, the all-time popular programming language from Japan, has continued to evolve with each iteration, empowering developers worldwide to build powerful and elegant applications. As the community eagerly awaits the release of Ruby 3.3, there is a lot of excitement around the potential enhancements and features that this version promises to bring. With a focus on performance improvements, language refinements, and expanded capabilities, Ruby 3.3 is poised to further solidify its position as a versatile language for both beginners and seasoned developers alike.

While there is no confirmation around the expected date of release of Ruby 3.3, speculations suggest that it may be somewhere around the end of 2023. In this article, you will learn of the anticipated advancements and innovations that Ruby 3.3 is expected to introduce. You will also learn how to get started with the latest preview of Ruby 3.3.0 and how to set up application monitoring to ensure that any new changes don’t end up breaking your production Ruby apps.

Without further ado, let’s begin!

What’s New in Ruby 3.3.0 preview?

This section will dive deep into what’s new in Ruby 3.3.0preview and provide you with details on the two major areas in which Ruby 3.3.0 is expected to improve.

JIT Changes

MJIT (Method-based Just-in-Time Compiler) was one of the earliest experiments with just-in-time compiling in Ruby. Implemented by Vladimir Makarov, it greatly enhanced performance for programs that are not input/output-bound. In the 3.3.0 preview version, MJIT is being replaced by RJIT, which is a pure-Ruby JIT compiler. Since it is not C-based (like MJIT), it does not require a C compiler at runtime. However, the current introduction of RJIT is purely experimental, and you should keep using the standard YJIT in your production Ruby apps.

Speaking of the YJIT compiler, it was introduced recently (merged into Ruby 3.1) by Shopify, and it showed a lot of improvements over the regular compilation benchmarks of Ruby. Official Shopify figures showed speedup over the CRuby interpreter of 20% on railsbench, 39% on liquid template rendering, and 37% on active record. Ruby 3.3.0preview version brings major performance improvements to this compiler.

With changes like register allocations for stack operations of the virtual machine, instance variables no longer exiting to the interpreter with megamorphic Object Shapes, etc., the YJIT compiler now uses a lot less memory for the metadata of the compiled code. Code generation on ARM64 devices has also been improved, and you can now start YJIT in paused mode to enable it manually later. Overall, this version has been a great one for JIT compiler improvements.

Parser Improvements

The new Ruby 3.3.0preview version now uses the pure-Ruby parser lrama instead of GNU Bison, which is often used to cause compatibility issues and version mismatches. Up until May 2023, Ruby used to rely on GNU Bison to generate AST (Abstract Syntax Tree) from tokenized source code before sending it for compilation into bytecode. 

Pat Shaughnessy did an awesome job of explaining how Ruby parses and compiles your code in 2012, and an important point to take away from their writeup is that Bison doesn’t directly parse your tokenized source code. Instead, it generates the parser code just before build time-based on the given grammar rules (via a parse.y file). This generated parser code is often the culprit of creating version-related and other compatibility issues.

Apart from this, the Ruby team was also looking for a more error-tolerant parser. It was discussed and established in “Ruby Committers vs The World” in 2022 and 2021 that the next Ruby parser would be more error-tolerant, easily maintainable, and a universal parser for the entire Ruby ecosystem, as much as possible.

Bison’s parsing approach currently involves discarding parts of input so that it can parse the rest of the input in its panic-mode error recovery process. While this enables Bison to make sense of the rest of the source code, it still results in losing parts of the input, which may be crucial to the final programs.

The lrama parser makes use of a combination of insert and delete operations to turn an invalid input script into a valid copy with as few changes as possible. If you want to learn how exactly it does that, make sure to look at Yuichiro Kaneko’s presentation on “The future vision of Ruby Parser” delivered at RubyKaigi 2023.

The lrama parser is also implemented 100% in Ruby and is installed in the ruby/ruby tool directory. It uses Bison’s input grammar file (parse.y), and can generate 100% compatible C parser files for Ruby 3.0.5, 3.1.0, and 3.2.0. Additionally, there are efforts to turn the lrama parser into a universal parser for the Ruby ecosystem, so it seems to satisfy all three criteria for the new Ruby parser set in 2021 & 2022.

Other Improvements

Apart from the major performance improvements discussed above, the preview also brings along changes in the language, such as defined?(@var) now being optimized with Object Shapes and compatibility fixes like ext/readline being retired and replaced by reline. A set of default gems have also gone through updates for the new Ruby version, and you can learn more about them here.

What’s on the horizon for Ruby 3.3?

Looking at the changes that have been brought about by this point, it seems like Ruby 3.3.0 will be a major improvement over the current version. Some of the key areas where you could see substantial changes include:

How can Scout APM help?

While new releases of Ruby have always been quite exciting and promising, moving your production app directly to a new and untested version isn’t the smartest move. At the very least, you need a robust monitoring setup to keep a close eye on your app as soon as it moves to the new (and possibly unstable) runtime to get notified of incidents and bottlenecks as soon as possible.

Scout APM has been a premier APM for Ruby and Ruby on Rails now for years, and it can help you with this problem. If you are experimenting with the 3.3.0 preview version, it will be best to use an APM tool like Scout to monitor its performance closely. Additionally, it is super simple to set up Scout APM in a Ruby 3.3.0 preview-based app as well.

Setting up Monitoring with Scout APM in a Ruby 3.3.0 preview App

This section will show you how to set up monitoring with Scout APM in a Ruby 3.3.0 preview app deployed on Render.

Installing Ruby 3.3.0 preview

To start, you will need to install the Ruby 3.3.0 preview version first. While there are package managers like rvm and rbenv to help you easily install and manage Ruby versions on your system, this tutorial will rely on building from source to maintain uniformity across environments.

To do that, download the tarball from this link and unzip it using the following command:

tar -xvzf ./ruby-3.3.0-preview1.tar.gz -C .

Next, change your working directory to the newly unzipped folder by running the following command:

cd ruby-3.3.0-preview1

To build and install Ruby from the source, here are the commands you need to run:

./configure
make
sudo make install

This will install Ruby 3.3.0-preview1 version on your system. You can check its version by running the following command:

ruby -v

This should print the following:

ruby 3.3.0preview1 (2023-05-12 master a1b01e7701) [x86_64-linux]

This confirms that you have the right version installed and configured. For the next section, you will need a copy of any version of Rails installed locally to create a new Ruby on Rails app. You can install it by following this guide. Once you have it set up, you are ready to move to the next section

Creating a new Rails App with Ruby 3.3.0 preview

To create a new Ruby on Rails app using Ruby 3.3.0 preview version, run the following command:

rails new todo

This will create a new Rails app with the name ‘todo’ in a directory named ‘todo’. You can now check out the files of the app and look in the ‘Gemfile’ to check the version of Ruby used in the project. You should expect to find something like this:

source 'https://rubygems.org'
ruby "3.3.0-preview1"
...

This signifies that the new project uses the latest preview version of Ruby. You can now deploy this app to Render.

Deploying the Sample App

Since Scout APM only works for non-development sessions, you must deploy your Ruby app to a remote host like Heroku or AWS. Feel free to refer to their documentation and set up a GitHub or GitLab-based repository from where Heroku or AWS build and deploy your app.

Setting up Instrumentation with ScoutAPM

Once you have deployed the Ruby app, you can now move on to setting up ScoutAPM for monitoring. To start, sign up for a free trial of Scout APM and create a fresh account. Once the account is ready, log into Scout APM and click on Applications > Add Application from the top navigation bar:

Next, choose Ruby on Rails on the Choose your language screen:

Scout will now present instructions on how to set up instrumentation in your Ruby app.

The process is simple: Install Scout’s gem in your Ruby app by adding the following line in your Gemfile:

gem 'scout_apm'

Run bundle install to install the gem. Next, you will need to download the customized config file from step B of the Get Started with Scout page and place it in the <project-root>/config directory.

Finally, you will need to deploy the app once again (most probably by committing it to your VCS). This will set up Scout APM in your Ruby 3.3.0preview1 app.

You can now take a look at the Scout dashboard to start seeing similar insights from your deployed app:

Scout will provide you with key metrics around your Ruby app, such as N+1 queries consuming over 150 ms time, SELECT database queries that are consistently slow, transactions contributing to memory bloat, and active errors or crashes in your app. 

You can also view performance metrics for each of the endpoints in your app, such as response time, throughput, max allocations, error rate, etc.

Furthermore, Scout also enables you to view insights from databases and other external services in the same dashboard.

You can view time spent in queries, throughput, database events, and more.

Conclusion

This article explored the exciting advancements that await us with the upcoming release of Ruby 3.3. Throughout this article, you have gained insights into the performance optimizations, enhanced concurrency, and refined language features that will shape the future of Ruby programming. By understanding these changes, you can make informed decisions and leverage the power of Ruby 3.3 in your projects.

If you're eager to experience the capabilities of Ruby 3.3 firsthand, you can start experimenting with it on platforms like Heroku. By trying out Ruby 3.3.0 preview in a test environment, you can explore its features, assess compatibility with your existing codebase, and discover the advantages it offers for your projects.

To further optimize your Ruby applications and gain deeper insights into their performance, you should consider tools like Scout. These monitoring and observability solutions can help you identify bottlenecks, analyze runtime metrics, and fine-tune your code to maximize efficiency.

Are you ready to explore the new possibilities offered by Ruby 3.3? Try it out on Heroku today and leverage the power of Scout to optimize your Ruby applications for peak performance.