logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·30.10.26
← Back to news

I'm sorry, but you still have to think

Original article
itsallaboutthebit.com|92 points|28 comments|by drogus|Oct 11, 2026

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 code or 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:

FeatureOriginal RailsRust RewriteReason/Result
CSRF TokensMaintainedDropped\text{Dropped}DroppedTo simplify caching
Queue SystemRedisIn-process queuesChanged for notifications
Compatibility100%<100%< 100\%<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 100ms100\text{ms}100ms, 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 10ms10\text{ms}10ms, 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: NNN clients send a message →\rightarrow→ wait for response →\rightarrow→ 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?")

AI Benchmarking

The Delivery Failure

Zach Daniels discovered a shocking disparity in notification delivery under heavy load:

  • Rust Version: ≈1%\approx 1\%≈1% success rate (1100\frac{1}{100}1001​) out of 6,000–7,0006,000\text{--}7,0006,000–7,000 notifications.
  • Elixir Version: 100%100\%100% success rate out of ≈1,700\approx 1,700≈1,700 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? ←\leftarrow← Most Important