Ruby 3.1: YJIT lands, and the new debugger
Ruby 3.1 arrives on Christmas Day with YJIT — a new JIT from Shopify that finally speeds up real Rails workloads, not just benchmarks — plus a modern debugger, better error messages, and quality-of-life syntax. A practical tour.
Ruby 3.1.0 has arrived on Christmas Day, and the headline is the one I have been waiting for since the JIT first appeared in 2.6: a JIT that actually helps Rails. The earlier MJIT was real engineering but, as I noted at the time, did little for typical web workloads dominated by I/O and method dispatch. YJIT, contributed by Shopify and shipping in 3.1, changes that. Alongside it comes a genuinely good debugger, much better error messages, and some pleasant syntax. Here is the tour.
YJIT: a JIT that helps real apps
The big news is YJIT (Yet Another Ruby JIT), a new just-in-time compiler from Shopify built with a different technique (lazy basic-block versioning) than the old MJIT. The crucial difference is the one that matters in practice: where MJIT mostly helped CPU-bound benchmarks, YJIT delivers real speedups on actual Rails workloads — the method-dispatch-heavy, allocation-heavy code that production web apps actually run. Shopify built it precisely because they needed their enormous Rails monolith to go faster, and it does, in production, on real traffic.
ruby --yjit my_app.rb # opt in to YJIT
# or RUBY_YJIT_ENABLE=1 / config in your app
This is the JIT story finally paying off for the people reading these posts. The 2.6 caveat — “the JIT doesn’t help Rails yet, watch this space” — is resolved: YJIT is something you can actually turn on for a Rails app and measure a meaningful improvement (reported double-digit percentage speedups on real workloads), with more to come as it matures. It is still young in 3.1 and improving fast, but it is the moment Ruby’s multi-year performance push reaches the web apps most of us build.
A modern debugger: debug.gem
The second standout is the new debug gem, now bundled and the recommended debugger. For years,
debugging Ruby meant byebug, pry, or scattering puts statements; the new debug is a
ground-up, modern debugger that is fast, full-featured, and integrates with editors (notably
VS Code’s debug UI) and remote debugging:
require "debug"
def calculate(order)
debugger # drops into a powerful, fast debugger session here
# step, next, breakpoints, watchpoints, remote attach…
end
It supports the things a serious debugger should — breakpoints (including conditional and watchpoints), stepping, inspecting frames, and attaching to a running process remotely — and it is notably faster than the older tools. Having a good, official debugger in the standard library (the same trajectory as the irb glow-up) is a real quality-of-life improvement, and worth switching to from byebug/pry.
error_highlight: errors that point at the spot
A small but delightful addition: error_highlight makes error messages point at the exact
part of the line that caused the problem, not just the line:
NoMethodError: undefined method `name' for nil
user.profile.name
^^^^^^^^
When user.profile is nil, the error underlines profile.name so you see immediately which part
of a chained expression was nil — instead of staring at a long line wondering which . blew up. On a
busy line with several method calls, this turns a moment of squinting into an instant diagnosis. It
is on by default, and it is the kind of small thing that quietly saves debugging time every day.
The syntax niceties
A few quality-of-life syntax additions:
-
Hash literal value omission. When a hash key matches a local variable name, you can omit the value —
{ x:, y: }means{ x: x, y: y }:name = "Ada"; age = 36 person = { name:, age: } # => { name: "Ada", age: 36 }It is a small thing that removes a common bit of repetition, especially when building hashes from variables you just computed.
-
Pinning with expressions in pattern matching. The pin operator now accepts expressions:
in { count: ^(a + b) }, making pattern matching (now a settled feature) more flexible. -
IRB improvements continue — autocompletion and a documentation display make the console even nicer, building on the 2.7 glow-up.
Should you upgrade?
Yes — and this is a release with a genuinely exciting reason beyond the usual. YJIT is the long
-awaited JIT that helps real Rails apps; you can turn it on and measure a real improvement, and it
will only get better. The new debug gem is worth adopting over your old debugger for the speed and
editor integration. error_highlight and the syntax niceties are free, additive quality-of-life. And
since the big breaking change (keyword arguments) was in 3.0, the jump from 3.0 to 3.1 is smooth.
The arc from the 2.6 JIT preview to here is satisfying to trace: the “3×3” performance journey gave Ruby a faster runtime, but the JIT specifically did not help web apps until now — and 3.1 is where that finally lands, courtesy of a company that runs one of the largest Rails apps in the world needing it to. A strong release, and a fitting one to close the year on.