LmCast :: Stay tuned in

From Rust to Ruby

Recorded: May 27, 2026, 2 a.m.

Original Summarized

From Rust to Ruby

Available for hire [about] · 
[resume].

home

2026-05-26

From Rust to Ruby
Who does things like that?!?
Apparently: me.
I have my own project, written in Rust. Not a big one, mind you, maybe approx. 30k lines of code in total. Rust is verbose so it’s not really that impressive. I’ve put it aside for some time and was toying with local inference, LLMs, writing agents and my attention was brought to Ruby.
It’s been a while. So I had to take a look around to remind myself what Ruby and Ruby on Rails are doing nowadays. They’re doing quite well. There are some typing initiatives (Sorbet), and the language itself is terse as ever.
And then I had this thought… But an introduction is in order first: In my Rust app I have an isolated crate that’s pretty much a webapp written with Tera and Axum. 14,943 lines of Rust code in total, around 10s of compilation time (maybe the code isn’t big, but it pulls the whole universe behind itself) and then quite heavy E2E tests involving setting up Playwright and (because of the near-impossibility of mocking) an isolated database namespace and mocking services (along with a very special internal-api crate that allows Playwright to interact with the app in headless mode…).
So I thought “hmm, I wonder if I can get my Local Qwen3.6 to do a oneshot conversion”. But before I did so I researched first. I asked a few instances to analyse the project in terms of gains of complexity, stability, testability, etc., and while (obviously) stability would drop (no types in Ruby) it’s not that awful (Sorbet has types in Ruby!).

┌─────────────────────────────────┬──────────────────┬───────┬────────────────┐
│ Area │ Rust/Axum/Diesel │ Rails │ Rails + Sorbet │
├─────────────────────────────────┼──────────────────┼───────┼────────────────┤
│ Suitability for solo dev │ 60 │ 90 │ 85 │
├─────────────────────────────────┼──────────────────┼───────┼────────────────┤
│ Development speed │ 40 │ 90 │ 75 │
├─────────────────────────────────┼──────────────────┼───────┼────────────────┤
│ Safety │ 95 │ 55 │ 80 │
├─────────────────────────────────┼──────────────────┼───────┼────────────────┤
│ Development complexity │ 70 │ 90 │ 75 │
├─────────────────────────────────┼──────────────────┼───────┼────────────────┤
│ Performance │ 95 │ 50 │ 50 │
├─────────────────────────────────┼──────────────────┼───────┼────────────────┤
│ Boilerplate │ 30 │ 85 │ 80 │
├─────────────────────────────────┼──────────────────┼───────┼────────────────┤
│ E2E testing testability │ 40 │ 75 │ 75 │
├─────────────────────────────────┼──────────────────┼───────┼────────────────┤
│ Unit testability │ 20 │ 90 │ 90 │
├─────────────────────────────────┼──────────────────┼───────┼────────────────┤
│ Integration testing testability │ 30 │ 85 │ 85 │
├─────────────────────────────────┼──────────────────┼───────┼────────────────┤
│ Sum │ 480 │ 710 │ 695 │
└─────────────────────────────────┴──────────────────┴───────┴────────────────┘
So in the end it seems I have (licks finger and turns to the wind) 1.47x better outcomes if the app were a Ruby on Rails app instead.

I have a local LLM running on my (bought it for gaming pre-AI craze) 4090 Ti1 - I’m a free man with unlimited tokens2. So I thought: BRING IT ON!
Since it is a relatively small project the conversion took ~30 minutes. I have no idea if it works or not because I haven’t yet tried running it. But there is one thing I checked, and stared at in horror:
$ fd . -e rs -uu | xargs cat | wc -l
14943
$ fd . -e rb -uu | xargs cat | wc -l
3322

That’s right folks! 77% decrease in line count; 4.49 lines of Rust code for each line of Ruby.
I browsed the Ruby code and it looks… fine. There are probably some bugs (no bunnies) but I must say it’s looking clean and idiomatic for my dated eye. I’m going to examine it further with some things in mind:

I can add types using Agents, so probably type safety can be alleviated

Ruby/Rails is pretty much batteries+kitchen sink included, which beats 3GiB of compiled deps.

Testing will be SO MUCH EASIER
VCR.use_cassette("llm_call") do
result = LlmClient.match(entry, data_list)
expect(result.results.size).to eq(data_list.size)
end
vs
#[derive(Debug)]
pub struct MockProvider {
responses: Arc<RwLock<Vec<Response>>>,
call_count: Arc<AtomicUsize>,
}

impl Default for MockProvider {
fn default() -> Self {
Self {
responses: Arc::new(RwLock::new(vec![Response::default()])),
call_count: Arc::new(AtomicUsize::new(0)),
}
}
}

impl MockProvider {
pub fn new(responses: Vec<Response>) -> Self {
Self {
responses: Arc::new(RwLock::new(responses)),
call_count: Arc::new(AtomicUsize::new(0)),
}
}
}

#[async_trait]
impl Provider for MockProvider {
async fn match(&self, entry: &Entry, data_list: &[Data]) -> Result<MatchResult> {
self.call_count.fetch_add(1, Ordering::SeqCst);
let responses = self.responses.read().await;
Ok(MatchResult { results: responses.clone() })
}
}

#[cfg(test)]
mod tests {
use super::*;

#[tokio::test]
async fn test_mock_provider_returns_expected_results() {
let expected = vec![Response::default()];
let provider = MockProvider::new(expected.clone());
let result = provider.match(&Entry::default(), &[]).await.unwrap();
assert_eq!(result.results, expected);
assert_eq!(provider.call_count.load(Ordering::SeqCst), 1);
}
}
…you get the vibe, right?

The benefits of working on your own project are, well, you can make crazy decisions, and I’ll be looking at this one very closely.

if you want to get jealous - I bought an MSI pre-built system with a 4090 Ti, an i9 and an HDR 144hz display for $2,300. ↩︎

Well, except for my electricity bills. ↩︎

Przemysław Alexander Kamiński
vel xlii vel exlee
cb | gl | gh | li | rss
Powered by hugo and hugo-theme-nostyleplease.

The author initiated a comparative study between a personal project developed in Rust and the Ruby on Rails ecosystem, driven by a desire to understand the contemporary utility of Ruby and its frameworks, especially in the context of developing tools like local inference and LLMs. The author's Rust project involved a substantial codebase, approximately thirty thousand lines of code, utilizing Rust with Axum and Diesel for a web application, complemented by complex end-to-end testing involving Playwright and isolated database namespaces. Before attempting a full conversion, the author investigated the trade-offs between the two approaches concerning complexity, stability, testability, and performance for a solo developer.

The initial research suggested that while Rust offered superior performance, development safety, and performance metrics, Ruby on Rails presented a different balance, particularly regarding development speed and overall system composition. A derived comparison indicated that the Ruby on Rails approach offered more favorable outcomes when considering factors like development speed, boilerplate reduction, and testing ease, ultimately suggesting that the Rails structure provided a 1.47 times better result for the overall application development experience.

To test this hypothesis, the author undertook a practical experiment by attempting a one-shot conversion of the Rust application to Ruby. This conversion resulted in a significant reduction in code volume, decreasing the line count from approximately fourteen thousand nine hundred forty-three lines in Rust to roughly three thousand three hundred twenty-two lines in Ruby, representing a seventy-seven percent decrease in code length. Although the author acknowledged the resulting Ruby code might contain bugs, they found the code to be clean and idiomatic.

The advantages observed in the Ruby context centered on system integration and testing. The author noted that Ruby and Rails provide a "batteries plus kitchen sink" approach, which potentially reduces the need to manage complex dependencies, contrasting with the need for three gigabytes of compiled dependencies in the Rust environment. Furthermore, the author discovered that testing methodology was significantly simplified. Instead of implementing complex mock provider structures and explicit atomic operations for state management in Rust, the Ruby environment allowed for the easier integration of testing utilities, such as VCR cassettes, to handle external interactions, making unit and integration testing substantially easier and more streamlined. Ultimately, the experience led the author to conclude that the perceived benefits of the Ruby on Rails architecture, particularly in terms of development velocity and testing ergonomics, outweighed the concerns about compile-time safety inherent in the Rust language for this specific project scope.