Ruby 4.0.0 arrived this morning, as is tradition, on Christmas Day. Major version releases always carry more weight than point releases - not just for the new features, but for what they signal about where the language is heading. This one is no exception.
Here's what's worth paying attention to.
ZJIT: the next generation compiler
Ruby 3.x shipped YJIT, the in-process JIT compiler originally developed by Shopify, which delivered meaningful real-world performance improvements for Rails applications. Ruby 4.0 introduces ZJIT as its experimental successor.
ZJIT requires Rust 1.85.0 or later and is currently faster than pure interpretation but hasn't yet matched YJIT's performance in benchmarks. The team's stated target is production readiness for Ruby 4.1. That's an honest position - shipping an experimental compiler in a .0 release for community testing rather than waiting until it's perfect is the right call.
For most applications, YJIT remains the better choice today. But ZJIT is clearly the direction of travel, and having it available to experiment with in 4.0 is useful. If you run a performance-sensitive Rails application, it's worth keeping an eye on the 4.1 roadmap.
Ractor gets a proper redesign
Ractor was introduced in Ruby 3.0 as the answer to Ruby's longstanding lack of true parallelism, and while the concept was sound, the API was awkward. Ruby 4.0 takes a significant step toward fixing that.
The key change is the introduction of Ractor::Port as the new message-passing primitive. The old Ractor.yield and Ractor#take methods have been removed, replaced by Ractor#join and Ractor#value. The new model is cleaner and more predictable - closer to how most developers would expect concurrent message passing to work.
This is a breaking change for any code using Ractors, but realistically, Ractor adoption has been limited partly because the API wasn't comfortable to work with. The redesign makes parallel Ruby significantly more approachable. We expect to see more production usage now that the interface has matured.
Set joins the core
Set has been promoted from the standard library to a core class. Previously you needed to require 'set' - now it's available everywhere, like Array or Hash.
It's a small change in practice, but it's the right one. Set semantics are genuinely useful and the manual require step was a persistent friction point, particularly for developers coming from other languages where sets are first-class. The cleaner Set[1, 2, 3] inspect output is a welcome polish touch.
Ruby Box: experimental isolation
The most conceptually interesting addition in 4.0 is Ruby::Box - an isolation mechanism that, when enabled with RUBY_BOX=1, separates monkey patches, global variables, and library definitions across independent execution contexts within a single process.
The stated use cases are test suite isolation (no more worrying about one test's monkey patches bleeding into another) and blue-green deployments within a single Ruby process. Both are genuinely useful, and the approach is more elegant than the process-level isolation workarounds people have been building for years.
It's experimental, and it's not something you'll be reaching for on day 1 in production. But it's addressing real problems that large Rails applications hit at scale, and it's worth following as it matures.
Performance improvements
Beyond ZJIT, several lower-level performance improvements landed in 4.0:
Class#newis faster, particularly with keyword arguments - a common hot path in Rails applications- Garbage collection has been improved with independent heap growth for size pools and faster sweeping on large object pages
- Lock-free hash sets for frozen strings and symbols reduce contention in multi-threaded workloads
- Per-ractor object allocation counters reduce CPU cache contention in parallel code
None of these are headline features, but they compound. Rails applications on Ruby 4.0 should see measurable improvements simply from upgrading.
What's been removed
Every major version removes things, and 4.0 is no different. The headline removals:
Process::Status#&andProcess::Status#>>are gone- Process creation via pipe syntax in
IOandKernel#openis removed - this was a longstanding security footgun and its removal is overdue - The full CGI library has been removed from the standard library (only
cgi/escaperemains) SortedSetno longer autoloads and requires a separate gem
ObjectSpace._id2ref is deprecated rather than removed outright, giving codebases that depend on it time to migrate.
If you're upgrading an existing Rails application, run your test suite against 4.0 before deploying. Most well-maintained Rails apps will need minimal changes - but it's worth checking for any reliance on the removed features, particularly the CGI library if you have legacy code that touches it.
What this means for Rails development
Ruby 4.0 isn't a revolution - it's a strong evolutionary step. The headline story is infrastructure: ZJIT sets up a faster JIT for 4.1, the Ractor redesign makes parallelism more practical, and Ruby Box opens up new patterns for test isolation and deployment architecture.
For day-to-day Rails development, the performance improvements are real and the language changes are minimal enough that upgrading should be straightforward for most applications. For teams building at scale - high-traffic Rails applications, background processing-heavy workloads, anything where the GIL has been a constraint - the Ractor improvements are worth investing time in.
The language is in good shape. We'll be upgrading our own production applications in the new year and will write up anything notable that comes out of the process.
Read the full Ruby 4.0.0 release notes on ruby-lang.org.