Exploring the Sorbet Ruby Compiler

People complain that Ruby isn't fast. Stripe—one of the world's largest and most influential Ruby shops—is working hard to change that. In addition to type-checking, this article explores a new, ahead-of-time Ruby compiler Stripe is developing as a part of their Sorbet Ruby Gem.

The most common criticism of Ruby is its performance when compared to other languages. However, Stripe, one of the world's largest and most influential Ruby shops, is working hard to resolve this issue.

Along with their existing Sorbet Ruby Gem, which provides type checking, they're developing an ahead-of-time Ruby compiler with an emphasis on performance. This article will explore both type checking and the new compiler.

What Is Sorbet?

Sorbet is a Ruby project developed by Stripe that aims to improve developer productivity and the performance of Ruby code. What started as a gem to incrementally introduce type checking into Ruby projects has grown into an experimental ahead-of-time compiler for Ruby.

The Ruby Type Checking Gem

The Sorbet Ruby gem allows developers to turn Ruby, a dynamically typed language, into a statically typed language.

Programming languages are either statically or dynamically typed. In a dynamically typed language, the interpreter infers each object’s type at runtime. In statically typed languages, programmers explicitly state the type of an object when writing the code, typically when a variable is declared.

The Sorbet Ruby gem provides a way to incrementally adopt dynamic typing into Ruby code. Picking and choosing where static typing is most useful keeps some of the Ruby magic while improving error rates and even developer productivity.

Compare to Types in Ruby 3

Ruby 3 introduced RBS to the Ruby community. It includes a syntax for introducing Ruby types directly into the language, with no gem necessary. If you haven’t adopted Ruby 3, you can still take advantage of RBS with the gem, which is named rbs.

One of the major differences between Sorbet and RBS and Sorbet is that Sorbet requires type annotations to be added directly in your code, while RBS requires the creation of new files that lay out the structure of your Ruby code. These RBS files contain class and method signatures and are then used by type checkers (like Sorbet) to type check the code.

Why This Is Useful

Dynamic typing is one of the features that makes Ruby such a fun language. Because there is no need to indicate types, code ends up being cleaner and easier to read, although a little less clear. In practice, programmers often make mistakes when assuming an object's type, leading to errors that are not apparent until runtime, where they cause problems for users.

My favorite way to describe Ruby is "reckless". The language doesn't care if you know what you're doing. It will let you change the implicit type of a variable without knowing. The following Ruby snippet is completely valid:

some_word = "This is a string"
some_word = 27
some_word = 2.7

This code can result in type errors if you later attempt to perform a math operation on a string or even use a string method on an integer. Static type checking can mitigate both the frequency and the impact of these sorts of errors by throwing warnings as part of the check process, which can easily be run in a continuous integration workflow.

Impact on Stripe's Error Rate

By adding type annotations to Ruby code and using Sorbet to check the codebase, Stripe has reduced the number of errors in their codebase and improved the reliability of their services. One of the greatest benefits of using Sorbet is its ability to identify type errors early in the development process, before code is deployed.

Developers can catch and fix errors before they have a chance to cause issues in production. Uninitialized Constant errors and NoMethod errors, which every Ruby developer has certainly encountered, are caught in the type checking process instead of in production.

Sorbet also protects against regression by flagging changes that may be incompatible with the existing type annotations. Overall, the use of Sorbet has helped Stripe to improve the quality and reliability of their code, leading to a reduction in errors and an overall improvement in their system's reliability. They talk more about it in their recent blog post.

Using the Sorbet Gem in a Rails Project

Before using Sorbet to type check, three gems need to be installed.

Add the following code to your Gemfile:

gem 'sorbet'
gem 'sorbet-runtime'
gem 'tapioca'

Next, install the gems:

bundle install

Then, use tapioca to set up interface files:

bundle exec tapioca init

Next, generate .rbi files for sorbet to run type checking in a Rails project. Run the following:

bin/tapioca gems

Then, add the following:

bin/tapioca require

Finally, run the following:

bin/tapioca dsl

Now, you can run Sorbet type checking with the following:

srb tc

Sorbet silences type checking warnings at the start. The Sorbet Static Checks documentation notes that to enable type checking in a file, you'll need to insert a "magic comment" at the top of the file:

# typed: true at the top of a file will instruct Sorbet to raise errors for non-existent methods or argument mismatching.

# typed: strict will enforce that all methods have signatures.

This is enough to get started with type checking, but the Sorbet docs provide more details on setting up type checking with Sorbet. If you are using tapioca, the tapioca gem's readme has more details.

The Sorbet Ruby Compiler

In addition to being a static type checker, Sorbet is also an experimental Ruby compiler. The Sorbet compiler is a separate component of the Sorbet toolchain that is designed to take Ruby source code and compile it to be faster.

The Sorbet compiler is still in the experimental stage and is not yet in production environments outside of Stripe. Stripe does claim, however, that they've seen up to a 170% speed increase since adopting it for their APIs.

The Sorbet compiler works by analyzing Ruby source code and performing a series of optimization passes on it. These optimization passes can include things like inlining method calls, dead code elimination, and other techniques designed to make the code run faster.

One of the most notable techniques that Sorbet uses for performance improvements is ahead-of-time compiling.

What is "Ahead-of-Time"?

An ahead-of-time (AOT) compiler compiles source code into machine code before the code is executed. AOT compilers are commonly used with compiled languages, such as C, C++, and Rust, but can also be used with interpreted languages, such as Ruby. In these cases, the AOT compiler takes the source code and produces machine code that can be run on the target platform.

AOT compilers are a useful tool for optimizing the performance and portability of code and can be an important part of a developer's toolkit. Ahead-of-time compilation takes advantage of type information present in a statically typed language, which Sorbet provides for Ruby. A just-in-time (JIT) compiler notes types at runtime; Sorbet provides this information for Ruby code, and the Sorbet compiler leverages it.

Reducing API Latency by Spending Less Time in Compute

API latency is caused by a combination of things, including unavoidable network travel time, I/O operations, and Compute. The Sorbet Ruby compiler improves API latency by reducing the amount of time the language spends in compute. It makes a lot of sense that speeding up the language itself would lead to faster API endpoints, but it's not an easy task.

To achieve this, the Sorbet compiler uses a variety of optimization techniques to run code faster. It's worth noting that reducing API latency does not have a one-size-fits-all solution. Different APIs have different performance requirements and may necessitate different approaches to optimization. However, using a tool like the Sorbet compiler can be a powerful way to improve the performance of your Ruby-based APIs and deliver a faster, more reliable experience to your users.

Conclusion

In conclusion, the Sorbet Ruby compiler, when combined with Sorbet type checking, is a powerful tool for improving the performance and reliability of Ruby applications. With static type checking, Sorbet helps developers catch type errors before code is run, leading to fewer errors and a better user experience. Additionally, the Sorbet compiler can significantly reduce API latency by optimizing code for faster execution.

Although it is still in the experimental stage, the Sorbet compiler has the potential to become a valuable tool for developers working with Ruby. Overall, Sorbet is an exciting development in the Ruby ecosystem and is worth exploring if you’re looking to improve the performance and reliability of your Ruby-based applications.

If you're looking to learn more about the Sorbet compiler, Stripe's announcement is a great place to start. They point out that all the code for the compiler lives in the Sorbet repository on GitHub, which contains instructions for running the compiler, testing the compiler, and contributing.

What to do next:
  1. Try Honeybadger for FREE
    Honeybadger helps you find and fix errors before your users can even report them. Get set up in minutes and check monitoring off your to-do list.
    Start free trial
    Easy 5-minute setup — No credit card required
  2. Get the Honeybadger newsletter
    Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    author photo

    Jeffery Morhous

    Jeff is a Software Engineer working in healtcare technology using Ruby on Rails, React, and plenty more tools. He loves making things that make life more interesting and learning as much he can on the way. In his spare time, he loves to play guitar, hike, and tinker with cars.

    More articles by Jeffery Morhous
    Stop wasting time manually checking logs for errors!

    Try the only application health monitoring tool that allows you to track application errors, uptime, and cron jobs in one simple platform.

    • Know when critical errors occur, and which customers are affected.
    • Respond instantly when your systems go down.
    • Improve the health of your systems over time.
    • Fix problems before your customers can report them!

    As developers ourselves, we hated wasting time tracking down errors—so we built the system we always wanted.

    Honeybadger tracks everything you need and nothing you don't, creating one simple solution to keep your application running and error free so you can do what you do best—release new code. Try it free and see for yourself.

    Start free trial
    Simple 5-minute setup — No credit card required

    Learn more

    "We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release."
    — Michael Smith, Cofounder & CTO of YvesBlue

    Honeybadger is trusted by top companies like:

    “Everyone is in love with Honeybadger ... the UI is spot on.”
    Molly Struve, Sr. Site Reliability Engineer, Netflix
    Start free trial
    Are you using Sentry, Rollbar, Bugsnag, or Airbrake for your monitoring? Honeybadger includes error tracking with a whole suite of amazing monitoring tools — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
    Start free trial