RubyConf 2026

Take out your phone.

QR code for rubys.github.io/roundhouse/blog

rubys.github.io/roundhouse/blog

There Is No Server

Beautiful Ruby, Weird Ruby, and the Robots We Live With


class Article < ApplicationRecord
  has_many :comments, dependent: :destroy
  validates :title, presence: true
  scope :published, -> { where(published: true) }
end
          

Rails is to web applications
what SQL is to data.

The compiler is the query planner.
The deployment target is the execution plan.


Beautiful Ruby  (has_many :comments)
        │
        ▼
   Roundhouse IR  (typed, effect-annotated)
        │
        ├─▶ Rust        ─▶ rustc       ─▶ machine code
        ├─▶ TypeScript  ─▶ V8          ─▶ machine code
        ├─▶ Crystal     ─▶ LLVM        ─▶ machine code
        ├─▶ Elixir      ─▶ BEAM        ─▶ machine code
        ├─▶ Go          ─▶ gc          ─▶ machine code
        ├─▶ Python      ─▶ CPython     ─▶ machine code
        ├─▶ C#          ─▶ RyuJIT      ─▶ machine code
        ├─▶ Kotlin      ─▶ JVM         ─▶ machine code
        ├─▶ Swift       ─▶ LLVM        ─▶ machine code
        └─▶ typed Ruby  ─▶ Spinel ─▶ C ─▶ machine code   ← hold that thought
          

There is no interpreter

Specialize an interpreter to one program, and the interpreter melts away.
What remains is a compiled program.

Rails is the interpreter. Your app is the program.
You just watched Rails melt away. Fifty-year-old theory — GraalVM runs on it. The weird part was pointing it at has_many.

Ruby was never the slow part

Same app, same CRuby, same YJIT — only the framework melted:
≥ 87% of a Rails request is the framework re-answering questions whose answers were fixed at boot.

YJIT's verdict: +33% on the melted version. −3% on Rails.
Fresh Arel trees and polymorphic attribute reads, every request — the inline caches get nothing to hold.

Hand both to the JVM: the gap widens to 27×.
JITs amplify static shape; Rails erases it. Language ≈ 2–8×. Architecture ≈ 8–27×. They multiply.

The robots we live with


real-blog (Rails) ─▶ Roundhouse ─▶ typed Ruby ─▶ Spinel ─▶ native binary
                                                              │
                              ┌────── failure ◀───────────────┘
                              ▼
                 Claude Code analyzes ─▶ drafts issue
                              ▼
                    Sam files at matz/spinel
                              ▼
                 Matz + Claude Code design fix
                              ▼
               Sam verifies, reverts the workaround
          

Two humans, two agents, two compilers.
The robots don't replace either of us —
they collapse the distance between us.

72 minutes

matz/spinel #126 · Module assigned to a module-level attr_accessor reads back as 0

02:46:53Zrubysfiled — the accessor reads back as 0
03:14:19ZmatzStage 1 landed in 31bade0 — static constant fold
03:39:47ZmatzStage 2 landed in e61124b — runtime sentinel switch
03:58:33Zrubysverified on master — workaround reverted
- def self.adapter; @adapter; end     # the workaround
+ class << self
+   attr_accessor :adapter             # the Ruby you meant
+ end

What doesn't work

The line isn't the keyword — it's whether the answer exists before the app runs.

  • method_missing — the answer only exists at runtime. Never.
  • define_method — a diagnostic today; statically knowable definitions are tractable.
  • C extensions — a wall for most targets. Spinel speaks FFI, so there it's a door.

Every unsupported construct becomes a diagnostic.
The diagnostic list is the roadmap, not a gate.

The rewrite

Rails gets you to market.
That part, everyone agrees on.

The startups that survive face the sequel:
the rewrite. Proverbially, in Go.

What if the rewrite were a build target?
Go — or nine others. Readable, idiomatic, yours to evolve.

Where this goes

Every chronic Rails weakness lives at a language boundary —
where Ruby ends and JavaScript, WebDriver, a native bridge, or a C extension begins.
Don't manage the boundary. Compile across it.
System tests: 21/21 in under 100 ms, deterministic, on every save — no browser in the loop. Kotlin and Swift without the bridge is issue #41.

The same whole-app inference, pointed backwards into your editor and your agent — types, nil-safety, can-this-be-nil for plain Rails. No annotations, no app boot. Real today: it answers on lobste.rs in under a second.
Or skip the editor — an IDE in a tab, preloaded with all of Mastodon: 1622 files, ~83K lines, whole-app inference in under 3 s. The IDE has no server either.

"The JavaScript you write not only isn't the JavaScript you run, but under closer examination isn't even JavaScript at all."
The Ruby you write isn't the Ruby you run.
It's Rust. It's your phone.
It's Matz's C.
Or nothing at all — there is no server.

Continue the argument

QR code for this deck