De Rust a Ruby
(xlii.space)- Un experimento personal para migrar un crate de aplicación web en Rust a Ruby on Rails, con una base objetivo de 14,943 líneas de código basada en Tera y Axum
- La configuración existente en Rust requiere Playwright E2E, espacios de nombres de base de datos aislados, servicios simulados y hasta un crate interno de API, por lo que el costo de las pruebas es alto
- En una comparación entre LLM, Rails obtuvo una puntuación total de 710 frente a 480 de Rust/Axum/Diesel, y se evaluó que sus puntos fuertes son la velocidad de desarrollo y la facilidad para hacer pruebas unitarias
- La conversión de una sola pasada con Local Qwen3.6 tomó unos 30 minutos y el código Ruby se redujo a 3,322 líneas, aunque todavía no se ha verificado su ejecución
- Rails destaca por sus funciones integradas y sus pruebas concisas, mientras que la falta de seguridad de tipos en Ruby puede compensarse con Sorbet o con la adición de tipos basada en agentes
Contexto del experimento de migración
- Se eligió como objetivo de migración un crate de aplicación web en Rust que forma parte de un proyecto personal
- El proyecto completo tiene unas 30 mil líneas, y el crate a migrar es más cercano a una aplicación web escrita con Tera y Axum
- El código Rust objetivo de la migración suma 14,943 líneas en total, y compila en unos 10 segundos
- El código en sí no es grande, pero tiene una estructura acompañada de muchas dependencias
- La configuración existente en Rust tiene un costo alto de pruebas
- Las pruebas E2E requieren configuración de Playwright
- Como el mocking es difícil, se necesitan espacios de nombres de base de datos aislados y servicios simulados
- También se necesita un crate interno de API separado para que Playwright interactúe con la app en modo headless
- Ruby y Ruby on Rails se consideraron como una alternativa más concisa
- Ruby no tiene tipos y su estabilidad puede ser menor que la de Rust
- Con Sorbet, es posible compensar parcialmente la seguridad de tipos también en Ruby
- Al comparar complejidad, estabilidad y facilidad de prueba con varias instancias de LLM, Rails obtuvo una puntuación más alta
- El total de Rust/Axum/Diesel fue de 480, Rails de 710 y Rails + Sorbet de 695
- Rails recibió una evaluación alta en adecuación para desarrolladores individuales con 90, velocidad de desarrollo con 90 y facilidad de pruebas unitarias con 90
- Rust/Axum/Diesel tuvo una evaluación alta en seguridad con 95 y rendimiento con 95, pero baja en facilidad de pruebas unitarias con 20 y facilidad de pruebas de integración con 30
- Con base en la suma simple, se consideró que una app en Rails podría dar un resultado 1.47 veces mejor
Resultado de la conversión y puntos a revisar
- Se hizo una conversión de una sola pasada de un proyecto relativamente pequeño con Local Qwen3.6
- La conversión tomó alrededor de 30 minutos
- Aún no se ha ejecutado, por lo que no se ha confirmado si realmente funciona
- El cambio más grande fue la reducción en líneas de código
- Total de líneas en archivos Rust: 14,943
- Total de líneas en archivos Ruby: 3,322
- Las líneas se redujeron en 77%, y 1 línea de Ruby equivale a unas 4.49 líneas de Rust
- El código Ruby convertido, dentro de lo que se revisó por encima, parece limpio e idiomático
- Sigue existiendo la posibilidad de errores
- Se planea revisarlo con más detalle después
- Otros puntos a revisar son la compensación de tipos, las funciones integradas de Rails y la simplificación de las pruebas
- Si se agregan tipos mediante agentes, se puede mitigar el problema de la seguridad de tipos
- Se evalúa que Ruby/Rails se acerca más a “batteries + kitchen sink included” y que eso es mejor que 3GiB de dependencias compiladas
- Se espera que las pruebas sean mucho más fáciles
- Un ejemplo de prueba en Ruby usa
VCR.use_cassette("llm_call")para envolver una llamada a LLM y verificar el tamaño del resultado en una forma cortaVCR.use_cassette("llm_call") do result = LlmClient.match(entry, data_list) expect(result.results.size).to eq(data_list.size) end - Un ejemplo de prueba en Rust adopta una forma más larga en la que se debe implementar directamente un proveedor simulado
- Usa
Arc<RwLock<Vec<Response>>>,AtomicUsize,async_trait,tokio::test, entre otros - Se crea un
MockProviderque gestiona la lista de respuestas y el conteo de llamadas, luego se implementamatchdel traitProvidery en la prueba se verifican el resultado y la cantidad de llamadas
- Usa
- Como se trata de un proyecto personal, es posible tomar decisiones más atrevidas, y la migración de Rust a Ruby será revisada cuidadosamente en adelante
1 comentarios
Comentarios de Hacker News
Cuesta creerlo. Suena a algo como: “Había una comezón técnica que quería rascar, y una IA local terminó el trabajo en 30 minutos. Ni siquiera presioné Start para ver si corría, pero sí escribí la entrada del blog…”
Claro, a menos que lo hayan empujado bots
2026: ¡Miren este trabajo que yo no hice!
2036: Cómo escribí 200 líneas en el antiguo latín llamado C
Al principio pensé que sería un artículo interesante, pero en cuanto mencionó que usó un LLM para la conversión, perdí el interés de inmediato. Es parecido a decir: “Quería hacer esto, así que se lo encargué a un subordinado, y ahora les contaré la historia”
Como no hizo la conversión él mismo ni reflexionó mucho sobre ella, no parece haber mucho motivo para leerlo
Así que con solo correr pruebas de humo podría parecer que todo salió bien
Salvo en la programación artesanal, el lenguaje de programación en sí va a importar cada vez menos
A medida que los LLM mejoren, al final solo se tratará de decidir en qué tipo de lenguaje generar la especificación
La gente de UML y RUP finalmente consiguió su venganza
Como ya dijeron otros comentarios, sí hizo una revisión bastante amplia. No era un proyecto grande, y salvo algunas partes picantes, en su mayoría era una web app
Me parece injusto decir que “no pensó”. No fue solo presionar un botón y YOLO
Investigó los trade-offs y los resultados; la diferencia entre fragmentos de código en Rust y Rails sí era real, y la capacidad de prueba de la app en Rust era algo sobre lo que llevaba 2 meses pensando
Como les gusta decir a los fanáticos de los LLM, el contexto importa ;)
No sé si haya otro lenguaje y framework que prioricen tanto la felicidad del desarrollador como Ruby on Rails
Era algo compuesto en alguna parte, imposible de ubicar, y al final tenía que detener lo que estaba haciendo para leer documentación durante una hora. Tal vez esté bien para quien usa Rails todo el día, pero convención sobre configuración para mí es un antipatrón enorme
La felicidad no siempre se traduce en rendimiento. Me recuerda al famoso caso del fail whale de Twitter antes de migrar a la JVM y Scala
Ruby on Rails se hizo famoso, pero ya había experiencias similares con AOLServer y Vignette basados en Tcl
En una startup portuguesa incluso hicieron una variante propia, y luego sus fundadores crearon OutSystems. Fue una de las primeras herramientas gráficas RAD para desarrollar sitios web y sistemas distribuidos, de tipo low-code/no-code, apuntando a infraestructura JVM o CLR
Aun así, da gusto ver que ahora CRuby tiene JIT por defecto
Ya hice un paquete de gems llamado propel_rails que lleva el código ya conciso de Ruby on Rails todavía más al extremo. Genera clases base de alto nivel como controladores API y concerns, y desde ahí produce recursos RESTful completos (modelo, controlador, serializador, pruebas unitarias y pruebas E2E) con cero boilerplate
Al final los controladores solo contienen la lista de atributos permitidos por la API, porque las acciones RESTful se generan automáticamente. Es un poco difícil explicarlo por completo, pero el poder de la metaprogramación en Ruby realmente permite hacer cosas asombrosas con facilidad
¿La idea es que funcione tomando el modelo de dominio como base?
Estoy en una situación parecida
Me gustan Go y Rust, y ambos son grandes lenguajes con sus ventajas y desventajas. Pero por desgracia no he podido construir una app SaaS con ninguno de los dos. Es como tratar de meter una estaca cuadrada en un agujero redondo
Tal vez esté muy equivocado, pero las herramientas SaaS traen muchísimas cosas alrededor y no quiero reinventarlas
RoR es “lo suficientemente bueno”. Puedes introducir tipos cuando quieras, construir rápido, y el tooling está bastante bien
Mi primer trabajo profesional fue con PHP y tenía demasiadas trampas. Ruby me pareció interesante porque está un poco más orientado al comercio electrónico, y si le basta a Shopify, entonces me parece bien
Si hay una situación en la que tiene sentido pasar de Rust a Ruby, entonces haber elegido Rust al principio fue un error
Quienes creen que Ruby es más lento que Rust se sorprenderían al saber que Ruby ahora en realidad es más rápido que Python, aunque sigue siendo más lento que Go o Rust
Pero cuando empiezas a tener varios workers en segundo plano y cada uno consume más de 2 GB de memoria, eso se acumula muy rápido
Escribí un servicio de Rust para producción, y más que la velocidad, lo que me impresionó fue que corría con 30 MB de memoria
Es un comentario provocador, pero aunque quizá sirva para presumirle un IQ de 900+ a la plebe, muchos desarrolladores inteligentes y talentosos no disfrutan mucho Rust. Algunos prefieren crear su propio lenguaje de programación y compilador antes que escribir una sola línea de Rust
Me vienen a la mente Jai de Jonathan Blow y Odin de Ginger Bill
Hay muchos más con creatividad y profundidad demostradas, que han creado librerías y frameworks hermosos y ampliamente usados, pero no quiero desperdiciar espacio aquí
Eso sí, Rust tiene un gran club de machos y una fraternidad muy cerrada
Claude funciona muy bien en apps de Rails. Como señaló el autor de este artículo, Ruby te permite hacer mucho con poco código, y Rails usa convención sobre configuración, así que las apps de Rails quedan más concisas
Una hipótesis de por qué Claude escribe tan bien apps de Rails es la eficiencia de tokens
Antes vi este proyecto que intentaba medir y comparar la eficiencia de tokens por proyecto, y Rails salía bastante bien parado
https://felipemrvieira.github.io/SyntaxTax/dashboard/
A veces me sorprende el tamaño de algunos proyectos. ¿Un codebase de 30 mil líneas es pequeño? Ya sé que el techo puede ser muy alto, pero 30 mil líneas pueden contener una cantidad enorme de información y sutilezas de comportamiento
Quizá también tenga que ver con mi trayectoria, más enfocada en backend/redes con Go. Cuando pasas de 10 mil o 15 mil líneas, normalmente ya se me hace bastante difícil mantener en la cabeza un modelo completo del codebase