Overview
The open-source chat app ONCE Campfire was rewritten by AI agents into eight implementations under the basecamp organization. On the same Ryzen AI MAX+ 395 machine, with 4 cores per app and 16 concurrent clients hitting the same room page, requests per second were:
| Implementation | Requests/sec | |---|---| | Rails | 4,101 | | Django | 1,507 | | Laravel | 3,872 | | Express | 42,636 | | Elixir | 5,350 | | Go | 53,060 | | Rust | 106,494 | | C | 137,524 |
DHH's Rust rewrite replaced the Ruby/Puma/Redis/Resque/Thruster stack with a single campfire binary that reads the original database directly—no migration needed for existing users. The official claim was 19–44x faster than Rails; 106,494 / 4,101 ≈ 26x, which checks out.
Timeline
- Sep 26 – Rust repo quietly created
- Sep 28 – DHH announces rewrite complete
- Oct 4 – Three-language performance comparison published
- Oct 7 – Rails room page measured at 230 req/s
- Oct 8 – Jumps to 4,101 req/s after back-porting architectural improvements
- Oct 9 – Kotlin port added in about a day
- Oct 11 – Long-form teardown of the whole table
The twist: Rails went from 230 to 4,101 overnight
Same language, same framework, same machine—an 18x jump. The only change: DHH had the AI agent port the architectural improvements from the fastest port into every implementation ("I let the clanker run for a day, then have it bring all the architecture improvements to every implementation"). The next day, C overtook Rust.
Piotr Sarnacki's teardown (three layers of problems)
1. Undocumented AI decisions
Choices like backward compatibility, dropping notifications on crash, or latency vs. throughput have no single right answer—agents flip coins on them. The Rust port dropped CSRF tokens for caching convenience and replaced Redis with an in-process queue, while the Elixir port closely mirrored the original. Cross-language comparisons are distorted from the start.2. Unverified async correctness
Rust's cooperative scheduler stalls a whole thread when a task doesn't yield. The Rust port ran SQL queries in a blocking manner inside async tasks and held synchronous locks for up to 10ms. The Elixir port had its own issue: a single process serially processed all SQL, even read-only queries.3. The most painful finding
Under heavy load testing by Zach Daniels, the Rust version delivered only about 1% of the 6,000–7,000 notifications it sent; the Elixir version delivered all ~1,700 in the same scenario. But the Elixir community's "BEAM concurrency wins" was also premature: the two sides were tested at different request rates. Closed-loop testing measures "how fast are you," constant-rate testing measures "how stable are you"—mixing the two in one table is an accident waiting to happen.The Kotlin wildcard
On October 9, a developer ported the Rust version to Kotlin on the JVM in about a day using Claude Code, aligning even the embedded HTML parser byte-for-byte across 658 test cases. It matched Rust on x86, trading wins. The JVM wasn't on DHH's table—add it, and the rankings change again.
Takeaway
AI agents have driven the cost of "typing code" to the floor while leaving the cost of "making decisions" untouched. Every cell in a benchmark table now deserves the question: is this the language being fast, or the port being favored?
The repos live under the basecamp organization (github.com/basecamp/once-campfire-rust). Running the benchmarks yourself beats trusting anyone.