Novedades de Ruby 3.1: YJIT entra en escena
Segunda entrega de la serie Novedades de Ruby, un repaso versión a versión por cada minor desde la 3.0, con la ventaja de escribir en 2026 y saber ya cómo acabó cada apuesta. En la entrega anterior vimos la 3.0 y su promesa del 3x3; hoy toca la versión que empezó a cumplirla de verdad.
El 25 de diciembre de 2021 salió Ruby 3.1. Sobre el papel parecía una versión de transición: ni la ambición conceptual de la 3.0 ni grandes cambios de sintaxis. Y sin embargo, con la perspectiva de 2026, defiendo que fue una de las versiones más importantes de la década de Ruby. Porque en ella debutó una cosa llamada YJIT.
YJIT: el JIT que sí entendía Rails
En la entrega anterior conté que MJIT triplicaba benchmarks de emuladores de NES pero no movía la aguja en una aplicación Rails. YJIT vino a arreglar exactamente eso. Lo desarrolló el equipo de Shopify —gente que ejecuta uno de los monolitos Rails más grandes del planeta y a la que el rendimiento le duele en la factura—, y llegó a la 3.1 como experimental, desactivado por defecto y solo para x86-64 en sistemas Unix.
Los números anunciados eran modestos comparados con el marketing del 3x3, y precisamente por eso creíbles: hasta un 22% de mejora en railsbench y un 39% en renderizado de plantillas Liquid. La diferencia con MJIT no era de grado sino de enfoque: YJIT usa Lazy Basic Block Versioning, compila incrementalmente bloques pequeños según los tipos que realmente pasan por ahí, y eso encaja con el código despachado y polimórfico de un Rails real, no solo con bucles numéricos.
Activarlo era trivial:
ruby --yjit app.rb
RubyVM::YJIT.enabled? # => true
Recuerdo probarlo en staging con más curiosidad que fe, y ver caer los tiempos de respuesta un 15% sin tocar una línea. Después de años de promesas de JIT, aquello fue la primera vez que un Ruby más rápido se notaba gratis. La retrospectiva es conocida: YJIT maduró versión a versión, dejó de ser experimental, Rails acabó activándolo por defecto y en 2026 correr Rails sin YJIT es tan raro como desplegarlo sin gzip. La 3.1 fue el punto de inflexión: el momento en que el 3x3 dejó de ser un eslogan y se convirtió en una línea de trabajo con resultados trimestrales.
debug gem: adiós, byebug
La 3.1 jubiló el vetusto lib/debug.rb y estrenó el nuevo debug gem, reescrito desde cero: depuración remota, integración con VS Code y Chrome, soporte multi-proceso y multi-hilo, y hasta record & replay para rebobinar la ejecución.
require "debug"
def checkout(cart)
binding.break # o su alias corto: binding.b
# ...
end
Para los que llevábamos una década escribiendo binding.pry o dependiendo de byebug, el cambio no fue instantáneo —los dedos tienen memoria—, pero el rumbo quedó claro: el depurador dejó de ser una gema de terceros para ser parte del lenguaje, con un mantenimiento y una integración con editores que byebug ya no podía dar. Hoy es simplemente el debugger de Ruby, y byebug es una palabra que solo pronuncian los Gemfiles heredados.
error_highlight: el subrayado que ahorra minutos
Pequeña, humilde y de las que más calidad de vida dan: error_highlight, activada por defecto, empezó a señalar con precisión quirúrgica qué parte de la línea causó un NameError:
undefined method `name' for nil:NilClass (NoMethodError)
puts user.profile.name
^^^^^
En una línea con tres llamadas encadenadas, saber cuál de ellas devolvió nil sin abrir la consola es de esas mejoras que no salen en ninguna keynote y ahorran horas al año. Es mi ejemplo favorito de una constante de las versiones 3.x: el esfuerzo sistemático en mensajes de error para humanos, una tradición que luego continuaría (y de la que hablaremos en próximas entregas).
IRB con autocompletado
En la misma línea de pulir la experiencia diaria, IRB estrenó autocompletado con diálogo de candidatos —Tab para navegar, y documentación integrada pulsando Alt+d—. De golpe, la consola de rails c se pareció más a un editor moderno que a un prompt de los noventa.
Seré honesto: la primera versión del diálogo era mejorable —colores discutibles, algún parpadeo en terminales lentas, más de un compañero lo desactivó la primera semana—. Pero marcó el camino, e IRB no ha dejado de mejorar desde entonces hasta volver irrelevantes a las consolas alternativas que todos instalábamos antes.
El shorthand de hashes: {x:, y:}
El único cambio de sintaxis notable fue el hash literal shorthand: si la clave coincide con el nombre de la variable, puedes omitir el valor.
x = 1
y = 2
{ x:, y: } # => { x: 1, y: 2 }
# Donde brilla: serializar sin repetirte
def as_json
{ id:, name:, email: }
end
Sí, es la envidia sana del object shorthand de JavaScript. Al principio chirriaba —parecía un hash a medio escribir—, y tenía su letra pequeña: en llamadas a métodos sin paréntesis podía generar ambigüedades, así que con paréntesis siempre. Cuatro años después escribo { id:, name: } sin pensarlo y me molesta el Ruby antiguo donde no puedo. De anécdota sintáctica a hábito: el ciclo de vida de las buenas ideas pequeñas.
Las gemas que se bajaron del barco (y el susto del net/smtp)
La 3.1 continuó la limpieza de la librería estándar: net-ftp, net-imap, net-pop, net-smtp, matrix y prime pasaron de default gems a bundled gems. Traducción práctica: si tu proyecto usa Bundler, ya no venían de serie; había que declararlas en el Gemfile.
Y aquí llegó el rito de iniciación que compartimos media comunidad: actualizar una app Rails a 3.1, lanzar los tests y encontrarse el famoso cannot load such file -- net/smtp porque ActionMailer las necesitaba y nadie las había declarado todavía. Se arreglaba con una línea en el Gemfile, y las versiones siguientes de la gema mail lo resolvieron declarando sus dependencias, pero fue el recordatorio de que "viene con Ruby" es un contrato más frágil de lo que parece. Mención de honor al otro tropiezo de la temporada: Psych 4 hizo que YAML.load se comportara como safe_load por defecto, y más de un database.yml con aliases dejó de cargar hasta que Rails se adaptó.
Retrospectiva
Ruby 3.1 no intentó deslumbrar y por eso envejeció tan bien. Su legado es doble: por arriba, YJIT convirtió el rendimiento de Ruby en una historia de progreso real y sostenido que seguimos disfrutando en 2026; por abajo, debug gem, error_highlight y el nuevo IRB arreglaron la experiencia cotidiana de desarrollar, que es donde de verdad vivimos. Si la 3.0 preparó el terreno, la 3.1 plantó lo que después dio cosecha.
En la próxima entrega: Ruby 3.2, o el año en que YJIT se hizo mayor. Todas las entregas de la serie, bajo la etiqueta ruby.