Skip to content
Ruby 4.0 is released with Ruby Box, ZJIT, and performance advances

Ruby 4.0 is released with Ruby Box, ZJIT, and performance advances

Ruby 4.0.0 was released on December 25, 2025 and marked one of the most relevant updates to the language in recent years. The new version introduced Ruby Box, ZJIT, important performance improvements, advances in Ractor, and changes that directly affect developers building Ruby and Rails applications.

Published 6 min read

Also available in Português (Brasil)

Share

For the third week of Ruby Insights, one story stands above everything else in the ecosystem: the release of Ruby 4.0.0 on December 25, 2025. Matz chose the 4.0 number to mark Ruby's 30th anniversary rather than to signal a wave of breaking changes, but this is still far from an incremental update. It brings Ruby Box, a new JIT compiler called ZJIT, a reworked Ractor API, a long list of runtime and GC optimizations, and a round of cleanup that teams should plan for before upgrading.

Ruby Box: Isolation Inside a Single Process

Ruby Box is an experimental feature, enabled with the RUBY_BOX=1 environment variable and exposed as the Ruby::Box class. Code loaded inside a box stays there: monkey patches, changes to global and class variables, class and module definitions, and even loaded Ruby and native libraries are isolated from other boxes.

The release notes list the use cases the core team has in mind: running tests that monkey-patch core behavior without leaking into other tests, running two app boxes side by side for blue-green deployment inside one app-server process, and comparing responses between current and updated dependencies before committing to an upgrade. Box is also meant to become the low-level foundation for a future "package" API. For anyone who designs Ruby architectures or operates Ruby systems, this is one of the most interesting runtime additions in years. Because it is still experimental, treat it as something to explore, not to deploy.

ZJIT: The Next Generation After YJIT

ZJIT is a new method-based JIT compiler, developed as the next generation of YJIT. It compiles larger units of code through an SSA intermediate representation, which raises the ceiling for optimization, and its more conventional design is meant to make outside contributions easier. Building Ruby with ZJIT requires Rust 1.85.0 or newer, and you turn it on with --zjit (or RubyVM::ZJIT.enable at runtime).

The caveat matters: as of 4.0.0, ZJIT is faster than the interpreter but not yet as fast as YJIT, and the core team advises against using it in production for now. Their stated goal is a ZJIT that beats YJIT and is production-ready in Ruby 4.1. For Rails apps today, YJIT remains the right choice.

A Faster Runtime Without Touching Your Code

Beyond the JIT, 4.0 ships runtime improvements that add up for Rails workloads:

  • Class#new is faster in all cases, especially with keyword arguments, and the gain is built into YJIT and ZJIT.
  • GC heaps for different size pools now grow independently, reducing memory use, and sweeping is faster on pages of large objects.
  • Instance variable access is faster for "generic ivar" objects such as String and Array.
  • The GC skips its internal id2ref table until something actually needs it, which speeds up object_id and GC sweeping. object_id and hash are also faster on classes and modules.
  • More internal objects are write-barrier protected, which cuts GC overhead.

All of these land where Rails apps spend their time: allocating objects, reading instance variables, and collecting garbage. That translates directly into throughput, latency, and infrastructure cost, and you get it just by upgrading.

Ractor Grows Up

Ractor, Ruby's parallel execution model, received a substantial overhaul. The new Ractor::Port class replaces the old messaging model, Ractor#join and Ractor#value mirror their Thread counterparts, and Ractor.shareable_proc makes it easier to pass procs between Ractors. Internally, many data structures were reworked to reduce contention on the global lock and on CPU caches. The core team says it aims to drop Ractor's experimental label in 2026.

The trade-off is a breaking change: Ractor.yield, Ractor#take, Ractor#close_incoming, and Ractor#close_outgoing are gone, so code written against the old API needs to move to ports.

Language Changes and What to Check Before Upgrading

Two language changes stand out. *nil no longer calls nil.to_a, matching how **nil already skips nil.to_hash. And a logical operator at the start of a line now continues the previous line, the same way a leading . does for method chains:

if user.active?
   && user.confirmed?
  # Ruby 4.0 reads this as a single condition
end

The compatibility list is where upgrades actually break. Before bumping production apps, check for:

  • Set output: Set is now a core class, and Set#inspect prints Set[1, 2, 3] instead of #<Set: {1, 2, 3}>. Snapshot tests and log parsers that match the old format will fail.
  • SortedSet: No longer autoloaded. Add the sorted_set gem and require "sorted_set" explicitly.
  • Net::HTTP: Requests with a body no longer get Content-Type: application/x-www-form-urlencoded by default. Set the header yourself if a server depends on it.
  • CGI: Removed from the default gems. Only cgi/escape (CGI.escape, CGI.escapeHTML and friends) remains.
  • Backtraces: <internal:...> frames are gone, and "wrong number of arguments" errors now include the receiver's class name. Tests that assert on exact backtraces or messages may need updating.
  • Removed APIs: Process creation through a leading | in Kernel#open and IO methods, plus Process::Status#& and #>>. ObjectSpace._id2ref is now deprecated.

Tooling: Bundled Gems and Bundler 4

Several libraries moved from default gems to bundled gems, including logger 1.7.0, irb 1.16.0, reline 0.6.3, rdoc 7.0.2, benchmark, ostruct, and pstore. Bundled gems still ship with Ruby, but under Bundler they only load if your Gemfile or another gem declares them, so an app that requires ostruct or benchmark directly needs to add them. Ruby 4.0 also ships with RubyGems and Bundler 4.0.3, and the RubyGems team published an upgrade guide that is worth reading before you bump.

The release is not limited to the interpreter. It moves the language, the official tooling, and the developer experience forward together, and that combination is where Ruby and Rails developers will feel the real impact.

Why It Matters

Ruby 4.0 turns priorities that have been building across the ecosystem for years into shipped features: isolation, performance, concurrency maturity, and a cleaner standard environment. With the market pushing languages and frameworks toward efficiency and predictability, Ruby answered with a release that pairs genuine experimentation (Box and ZJIT) with pragmatic gains you get the moment you upgrade.

Sources

Comments

Sign in with GitHub to comment.

No comments yet.