I'm sorry, but you still have to think
Apologies, But Critical Thinking is Still Required
Written by Piotr Sarnacki | October 11, 2026
DHH, the mastermind behind Ruby on Rails, recently embarked on a project to migrate Campfire Once from its original Ruby on Rails implementation over to Rust.
However, he didn't actually write the code. Instead, he delegated the task to a "clanker" (an AI agent), primarily because he finds the process of reading and writing Rust code to be intolerable. To further the experiment, he had the AI generate rewrites in other languages as well, specifically Go and Elixir.
This scenario provides a fascinating case study on what happens when someone—even a highly experienced programmer—utilizes AI agents to generate code without performing a manual review.
The takeaway: If you were hoping that you could
stop reading codeor cease thinking entirely in the near future, this is not a promising sign.
The "Coin Flip" of Ambiguous Prompting
The rewrites suffer from various issues, but the non-functional discrepancies are the most telling. They highlight a common blind spot: when a prompt lacks extreme specificity, the AI makes arbitrary decisions.
Many architectural questions simply do not have a single "correct" answer. For instance:
- Is it acceptable to lose new content notifications if the system crashes?
If you don't specify the answer, the LLM will implicitly decide for you during implementation.
Divergent Implementations
The AI treated constraints and backwards compatibility differently across versions. Consider the Rust version:
| Feature | Original Rails | Rust Rewrite | Reason/Result |
|---|---|---|---|
| CSRF Tokens | Maintained | To simplify caching | |
| Queue System | Redis | In-process queues | Changed for notifications |
| Compatibility | 100% | Functional divergence |
Because these changes are language-agnostic, the comparison between Rust, Elixir, and Go becomes essentially meaningless. The AI didn't choose to drop Redis because of a "Rust-specific" requirement; it just happened.
The Danger of "Blind" Coding
If you actually bothered to look at the code—which we are told is no longer necessary—you would find significant flaws.
The Elixir Bottleneck
Critics noted that the Elixir version implemented a single process to handle SQL queries sequentially. This means even read-only queries, which should be concurrent, were queued up. While DHH viewed this as a failure of the AI to write performant Elixir, the Rust version had its own systemic issues.
The Rust Async Trap
In Rust, async runtimes use cooperative scheduling rather than preemptive scheduling.
If a task does not explicitly yield, it hogs the worker thread. If you execute a blocking SQL query that takes , every other task on that thread is frozen.
The Technical Error: In the rewrite, some queries run as blocking operations within async tasks. To avoid this, one should use async locks.
// ❌ Avoid this in async contexts if held for long
let lock = std::sync::Mutex::new(data);
// ✅ Use this for async-aware locking
let lock = tokio::sync::Mutex::new(data);
If a synchronous lock is held for even , the entire thread stalls while the blocking task does nothing. This proves that coding is not "solved"; domain expertise is still mandatory.
The Mirage of "Slop" Benchmarks
The benchmarks provided were largely "slop"—they measured raw throughput while ignoring system reliability.
Closed-Loop vs. Open-Loop
Both DHH and Zach Daniels used closed-loop benchmarks.
- Closed-Loop: clients send a message wait for response send next message. (Measures: "How fast can the system respond?")
- Open-Loop: Requests are sent at a constant rate regardless of response time. (Measures: "How does the system handle real-world load?")
The Delivery Failure
Zach Daniels discovered a shocking disparity in notification delivery under heavy load:
- Rust Version: success rate () out of notifications.
- Elixir Version: success rate out of notifications.
Analysis:
While the Elixir version handles backpressure more effectively, it only does so until the system is completely overwhelmed. The Rust version's failure rate is catastrophic, yet it looked "fast" in throughput-only tests.
Final Thoughts: The Reality of Trade-offs
Programming is an exercise in managing trade-offs. While some tools are objectively superior in specific niches, "silver bullets" rarely exist for complex, reliable systems.
Some in the Elixir community reacted to the Rust failure by claiming cooperative scheduling is unlivable. However, Elixir (and the BEAM) is not perfect either. While it makes concurrency significantly easier to write than in Rust, that ease comes with its own set of costs.
Summary Checklist for AI-Generated Code:
- Did I verify functional parity?
- Did I check for blocking calls in async runtimes?
- Did I test with open-loop benchmarks?
- Did I actually read the code? Most Important