· via Hacker News – Front Page (native)
Rails enums rewritten to generate static methods, nearly 5x faster under Ruby JITs
A railsatscale.com post details a Rails core pull request that swaps enum methods built on define_method and public_send for runtime-generated static methods, lifting microbenchmark throughput up to nearly five times.

Rails enums, compiled at runtime
A post on railsatscale.com titled "Rails Is a JIT Compiler" describes a core Rails optimization in which the framework generates faster code for enum methods at definition time. The title is bait, the author admits, but the substance is real: Rails already compiles small pieces of your application, and this change makes it do a better job of it.
According to the post, the work began when a developer named Sam asked in a Ruby JIT channel whether Rails enum methods could be optimized in Roundhouse, motivated by an Order model whose shippable? predicate chains the generated paid? and refunded? checks. Sam's initial instinct was to inline the enum behavior away in a build step external to Rails. The author pushed back on two grounds: Rails has largely moved away from dynamic lookups in favor of generating and caching methods, and a Ruby JIT such as ZJIT should handle dispatch to those methods without help.
What enum generated before
When you declare an enum such as enum :status, { pending: 0, paid: 1, ... }, Rails defines predicate methods like pending? on the model. The implementation, as the post shows, used define_method with a block that invoked public_send on the attribute reader and compared the result against a value held in the block's captured environment.
That avoids hash lookups at call time, since the generated method is cached on the class, but it is still needlessly dynamic, the post argues. public_send dispatch is not cached and carries extra machinery even though the method name is known statically. The block also reads the enum value from its enclosing environment on every call, even though that value is fixed the moment the enum is declared.
Burning the constant into the method
The proposed fix was to generate an ordinary method with module_eval on a string, so dispatch is direct and the enum value is literally written into the method's source. By the author's own account, knowing little about Rails internals, they nerd-sniped Aaron into doing the actual work; the benchmark output in the post comes from Aaron's machine.
The change turned out not to be quite one line. The Rails test suite includes enum labels that are not valid Ruby identifiers, such as timezone labels containing slashes and plus signs, and real applications use capitalized labels like :Foo. Float values added another wrinkle: while enum values must be serializable, non-finite floats do not round-trip cleanly in generated source code, so the implementation includes a check that excludes them. A contributor named Ufuk also pointed out that the generator should use ActiveSupport::CodeGenerator rather than hand-built strings.
The numbers
The post benchmarks a trivial order.shippable? call. On Rails main, the plain interpreter runs at 1.721 million iterations per second, YJIT at 3.568 million and ZJIT at 3.812 million. With the pull request, those figures rise to 3.145 million, 16.274 million and 18.509 million respectively. That is roughly 1.8 times faster on the plain interpreter, and around 4.6 to 4.9 times faster under YJIT and ZJIT.
Why it matters
The post closes with three lessons: static beats dynamic, public_send is slow, and performance problems can often be fixed upstream in libraries that already generate code, without reaching for an external compiler or build step. More broadly, it is a compact demonstration of metaprogramming as a form of compilation. By emitting the most static code possible at definition time, a framework hands Ruby's runtimes code they can optimize aggressively, and the framework's gains compound with the JIT's own work rather than competing with it.
- #ruby-on-rails
- #ruby
- #performance
- #code-generation
- #jit