1 puntos por GN⁺ 21 시간 전 | 1 comentarios | Compartir por WhatsApp
  • 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 corta
      VCR.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 MockProvider que gestiona la lista de respuestas y el conteo de llamadas, luego se implementa match del trait Provider y en la prueba se verifican el resultado y la cantidad de llamadas
  • 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…”

    • Que ahora sea el puesto #1 en la portada significa que da igual si el proyecto realmente funciona. Total, los lectores tampoco deben haber leído bien el artículo
      Claro, a menos que lo hayan empujado bots
    • 2016: ¡Miren esta nueva librería de JavaScript!
      2026: ¡Miren este trabajo que yo no hice!
      2036: Cómo escribí 200 líneas en el antiguo latín llamado C
    • Más que “difícil de creer”, para 2026 esto se siente bastante normal
    • Pero sí lo revisó durante 5 horas. Así que ¯\(ツ)
  • 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

    • El mayor problema es que el autor ni siquiera lo verificó. He visto herramientas grandes que combinan varios modelos y ejecutan una conversión durante 6 horas; luego, al revisar manualmente cómo resolvieron cálculos o lógica complicada, aparecen stubs o retornos verdaderos hardcodeados
      Así que con solo correr pruebas de humo podría parecer que todo salió bien
    • El problema aún mayor es que así es como se perfila el futuro del desarrollo de software
      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
    • La clave no es si escribió el código directamente o no, sino que comparó diferencias y tomó decisiones que probablemente no eran óptimas
      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

    • Casi nunca he sido tan infeliz como cuando trabajé en proyectos de Rails. Veía un bug en el sitio, hacía grep para encontrar la vista que se renderizaba mal. Encontraba una llamada al método que renderizaba esa sección, y luego hacía grep por el nombre del método… y cero resultados
      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
    • En Elixir y Phoenix siento algo parecido, pero al menos no tienen el arma para dispararte en el pie llamada method_missing
    • Aunque no se vea atractivo en comunidades con gustos tipo Silicon Valley, también he trabajado bastante a gusto con frameworks de Java y .NET
      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
    • A lo mucho es felicidad de corto plazo, y casi a cambio de todas las demás características arquitectónicas: mantenibilidad, rendimiento, confiabilidad, escalabilidad, etc.
  • 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

    • Esto suena como una forma refinada de CRUD
      ¿La idea es que funcione tomando el modelo de dominio como base?
    • Se puede encontrar en rubygems, pero el enlace a GitHub que aparece ahí da 404
  • 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

    • Esto resume muy bien el artículo, y también es la razón por la que decidí compartirlo
  • 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

    • Normalmente me importaba más el uso de memoria que la velocidad. La mayoría de las aplicaciones Ruby están limitadas por la base de datos, no por Ruby
      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
    • No entiendo por qué alguien que piensa que Ruby es más lento que Rust tendría que sorprenderse de que sea más rápido que Python. ¿Qué tiene que ver una cosa con la otra?
  • 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

    • La comunidad de Rust es de lo más alejado posible de un “club de machos”
  • 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

    • Depende muchísimo de lo que estés construyendo. Una app SaaS con varios frontends llega a 30 mil líneas con facilidad