Elixir and the BEAM for AI systems
DHH's Rust Benchmark Measured the Agent, Not Elixir
Added the verified figures for all eight ports, current commit counts, what happened to the pull requests, and a Kotlin port.
DHH's Campfire benchmark has Rust far ahead of Elixir and Go. The repos show Rust got 398 commits and 16 merged performance PRs. The others got three.
In this post
- What DHH published
- Whether the three ports got the same treatment
- Whether the benchmark conditions matched
- What others have reported since
- What changed after October 6
- Whether DHH is wrong that agents change how we choose languages
- Why this matters more than the language argument
- What to take from the Campfire numbers
Agents meet the bar you set, and only the Rust port of Campfire got a performance bar. DHH had agents port Campfire to Rust, Go and Elixir and posted a table with Rust far ahead. The public repos show why that table can’t rank languages: Rust got 398 commits in six days, 16 merged performance PRs among them, while the Elixir and Go ports had three commits each. I agree with DHH’s premise that agents write the code. I disagree that this data shows which language is fast.
Update, October 24, 2026: After this post went up, two community pull requests were merged, DHH’s agent carried the same caching and query work into every port, and a new shared harness remeasured them. On the room page, Elixir went from 722 to 5,350 requests per second, Go from 3,860 to 53,060 and Rust from 36,260 to 106,494. The current figures and what changed are further down.
What DHH published
DHH posted a requests-per-second table comparing agent-written ports of Campfire, the chat app 37signals open-sourced, on X. Rust led every workload by a wide margin. The Elixir, Go and Rust figures match the Performance section of the Rust repo’s README at commit ccece30, which says they were measured with 16 concurrent clients on an AMD Ryzen AI MAX+ 395, with four hardware threads allocated to each app. That README has changed since, and today’s figures are in the update below.
| Workload | Elixir | Go | Rust |
|---|---|---|---|
| Room | 722 | 3,860 | 36,260 |
| Messages | 1,053 | 5,573 | 40,872 |
| Sidebar | 1,275 | 19,753 | 34,672 |
| Search | 1,156 | 7,053 | 33,299 |
| Post | 801 | 4,767 | 6,896 |
Those are requests per second, DHH’s numbers, not mine. I haven’t rerun them and I’m not presenting them as a result.
The ports live in the basecamp org under the MIT license: Rust, Go and Elixir. The upstream Rails repo documents the ports in PRs #293 and #294, both merged 2026-10-04.
Whether the three ports got the same treatment
No, and the git history is the whole story. I counted commits on each repo’s main branch on 2026-10-06.
| Port | Created | Commits on main | AGENTS.md says |
|---|---|---|---|
| Rust | 2026-09-26 | 398 | “The app may now diverge from Rails where that makes it faster or better.” |
| Go | 2026-10-03 | 3 | “Port of the Rust Campfire in reference/ … Preserve …
behavior of the Rust app.” |
| Elixir | 2026-10-04 | 3 | “Preserve the pinned Rails reference behavior…” |
Of Rust’s 398 commits, 380 are DHH’s, 10 are Abdelkader Boudih’s and 8 are Daniel Collin’s. 397 of them landed between 2026-09-26 and 2026-10-01; the last, on 2026-10-05, updated the README. The Elixir repo has one port commit and two docs commits. Go has the port commit, one “Optimize room rendering and message writes (#1)” commit, and a README.
The Rust repo also has 44 pull requests (numbered up to #45), 39 of them merged. Going by title, 16 merged plus 2 open are performance-themed. Examples from the merged list: #3 “Index messages by room and time”, #4 “Cache every part of a page”, #32 “Database reads on dedicated reader threads” and #38 “Hash with the ARMv8 SHA-256 instructions”.
José Valim replied on X that “The Rust version has hundreds of commits, with multiple optimizations rounds, over several days. The others have 1 or 2 commits from an initial agentic rewrite.” He added that “There were 18 performance pull requests to the Rust codebase…” His count matches mine by title if you count the two open ones. I’m taking his replies from screenshots, so I’m quoting only what they show.
So the instructions differ too. The Go and Elixir AGENTS.md files tell the agent how to benchmark honestly, but neither gives it a number to beat or permission to trade parity for speed. Rust’s grants the permission and demands the evidence: “Performance changes come with before-and-after measurements.” The loop around it did the rest, six days of someone measuring and asking for the next improvement.
Whether the benchmark conditions matched
Not by the harness’s own account. The Elixir repo’s bench/README.md
lists the differences itself:
- Elixir ran with Redis and Thruster, while Go and Rust ran their own integrated processes.
- Elixir’s numbers came from an earlier session against an older Rust build. Ruby, Go and the optimized Rust image were measured separately afterward on the same host.
- It says: “This is a benchmark comparison, not a claim that every implementation passes the Elixir parity ledger.”
The workloads aren’t the same size either. Go PR #8 notes the Go sidebar sends a 9 KB frame where Rust sends a 30 KB page. That is a different amount of work per request, so the Go sidebar number isn’t a like-for-like comparison.
That answers the obvious objection, that Go got one pass too and
still beat Elixir five to six times on most workloads. Go’s
reference/ is the Rust repo pinned at 2026-10-01, after 397
of its 398 commits. The Go agent ported the tuned Rust app, likely
inheriting its caching design. The Elixir agent ported Rails.
What others have reported since
In the first few days, several pull requests claimed large gains, and they all came with caveats. Every number below is the PR author’s own, run on their own hardware, and none of these PRs was merged.
- Elixir #1, by Zach Daniel, replaces a single DB process with a read pool plus a single writer. It drops Redis and Resque and says it doesn’t keep strict Rails parity.
- Elixir #2, by kurtome, claims “8–10x on pages”. It was opened as a draft, and its parity gates haven’t been run.
- Elixir #5, by lau, is titled “Faster than Rust for some benchmarks” and adds 78,045 lines. It doesn’t state its hardware.
- Go #8 reports Room going from 12,216 to 19,896 requests per second, against Rust’s 21,417, on an M4 Pro.
PR authors report these gains. I’m not saying Elixir matches Rust, and nothing here shows it. What the PRs do show is that the first Elixir and Go ports left a lot on the table, which is exactly what you’d expect from three commits.
Update, October 24, 2026: All four are closed now, each by its own author, after reviews by “GPT on behalf of DHH” on October 7 turned them down on correctness or compatibility grounds. The reviewers read the code and didn’t rerun the benchmarks, so the claimed speedups were neither confirmed nor refuted. Narrower changes were merged instead, among them Elixir #4 by Oliver Kriska and Go #9 by nijaru.
A port in a language that isn’t in either table has turned up too. On October 9, Mystic at We Code Fire ported the Rust version’s room page to Kotlin in about a day with Claude Code, kept every response byte-identical to Rust’s, and benchmarked both on one- and two-core AMD EPYC servers at Hetzner. On those machines, Mystic writes, “it kept up with Rust and often pulled ahead.” Those are the author’s own runs, the port serves only the room page, and the post spells out the limit: “DHH’s table uses four cores of a Ryzen AI MAX+ 395, so the absolute numbers aren’t comparable, only the ratios within each table.” Mystic reads the table the way I do: “These numbers measure how much attention each port got at least as much as they measure the language.”
What changed after October 6
The repos kept moving after I wrote this. By October 8, every port had a bounded response cache, the same search and refresh-index fixes, and Fetch Metadata checks in place of CSRF tokens. Most of that work, including the merges of Elixir #4 and Go #9, was committed as “GPT on behalf of DHH”. A new shared verification harness measured all eight ports, checking every response and auditing every write. The figures below come from the Rust repo’s README at commit 6dae2fd, which I read on October 24, 2026. Like the first table, they’re DHH’s numbers, not mine. The request rates last changed on October 8 and the code-size column on October 9.
| Port | Room page, Oct 5 | Room page, now | Post a message, now | Backend KLOC |
|---|---|---|---|---|
| Rails | 241 | 4,101 | 330 | 5.5 |
| Django | 170 | 1,507 | 262 | 5.3 |
| Laravel | 164 | 3,872 | 794 | 4.1 |
| Express | 559 | 42,636 | 2,155 | 6.8 |
| Elixir | 722 | 5,350 | 1,278 | 11.6 |
| Go | 3,860 | 53,060 | 9,021 | 23.2 |
| Rust | 36,260 | 106,494 | 8,037 | 30.7 |
| C | n/a | 137,524 | 7,530 | 94.8 |
Room page and Post a message are in requests per second. Backend KLOC is thousands of lines of backend code, counting executable code in templates and leaving out tests, frontend and dependencies. The two room-page columns come from different setups: the October 5 run gave each app four hardware threads, the new harness gives it four cores, and the apps changed in between, so treat the gap as what the shared work bought rather than a controlled before-and-after.
Go went from about a tenth of Rust’s room-page rate to about half. Express, which trailed Elixir on October 5, now serves eight times as many room pages per second as Elixir. Rust still leads Elixir and Go on every read workload in the README, and a C port now leads Rust on reads. The write path ranks differently: on Post a message, Go leads at 9,021, then Rust at 8,037 and C at 7,530.
Elixir is still far behind, at about a twentieth of Rust’s room-page rate. Its README says Redis and a Resque-compatible worker still handle its jobs and broadcasts, and the same Thruster binary as Rails handles TLS, while Rust’s single executable replaces all of them.
Go and Elixir got their own rounds of work too. As of October 24, main has 437 commits on the Rust repo, 40 on Go and 45 on Elixir, against 398, 3 and 3 when I counted on October 6. The new commits were merged between October 7 and 9, and most are by “GPT on behalf of DHH”. Rust still has about ten times as many commits as either.
The verification report is careful about what its table shows. Its October 7 version says the table “compares complete production configurations, rather than isolating language speed.” The current version combines separate sessions, and its Elixir, Go, Rust and C figures come from an earlier October 8 run that wasn’t repeated.
Whether DHH is wrong that agents change how we choose languages
No. He’s right about the premise. In a follow-up reply on X, DHH wrote that “Speed is not the only consideration for a code base. Rust has other drawbacks, like slow compile times… when agents write the code and validate the output, we need to adjust to a new reality.” I agree with that sentence.
When agents write the code, throughput per dollar matters more and ergonomics for the human matter less, because in DHH’s model nobody reads the output. A language that is painful to type but cheap to run gets a better deal than it used to. I covered the other half of it, the “code nobody reads” problem, in my read of his Rails World keynote. I won’t restate it here.
Where I part ways is the reading the table invites, that it ranks the languages. The table is evidence that a six-day tuning loop beats a one-pass port. It is not evidence about what Elixir, Go or Rust can do, because only one of the three had the loop. Rust would likely still lead a fair rematch; a compiled language with no VM and no garbage collector usually wins on raw throughput. What the table can’t tell you is by how much, and neither can I.
Why this matters more than the language argument
Agents meet the bar you set. If the bar is “preserve the reference behavior,” you get something that preserves it and runs at whatever speed the first draft happens to run. If the bar is “beat this number on this harness,” the agent iterates until it does, and the Rust history reads like exactly that: indexes, caching, reader threads, hardware-specific hashing.
That matches how I work. I came to Elixir from Rails, run it in production, and run four to seven agent sessions at a time. My practice is that we don’t need to read every line, but we iterate until the code’s characteristics match our tests and our performance expectations. The point is the iteration, not who typed the code.
I’ll be straight about how that has changed. I used to pair with the agent and look at every line, and I wrote that down. My practice has moved on. I read where the risk is. In recent months we aren’t reading nearly as much code, and honestly, in some cases I read none of it. I trust the tests, or I read just the tests, to make sure they look correct and test the right parameters.
That puts the weight on the tests and on the targets. If the tests are weak, “I read none of it” is negligent. If they’re strong and include performance, it’s the same trade DHH is describing. His benchmark shows what happens when the performance target exists for one port and not the others.
What to take from the Campfire numbers
If you are choosing a language because of this table, stop. If you are choosing how to run agents because of it, the lesson is useful.
- Write the performance bar down before the agent starts. An AGENTS.md that only says “preserve behavior” produces a port that preserves behavior. Add the harness, the hardware, the workloads and the number to beat.
- Count iterations before you compare outputs. Commit counts and merged pull requests are a free proxy for how much feedback each result got. Three commits against 398 should end the comparison before it starts.
For the language question itself, the Tencent benchmark across 20 languages is a better start than a chat-app port, though it measures something narrower: whether models can complete problems at all. What Elixir gives a coding harness for free covers why agents work well in Elixir regardless of raw speed.
If your team is figuring out how to set that bar, and what to read when agents write most of the code, that’s the kind of rollout decision I help with as a fractional Chief AI Officer.