# Novedades de Ruby 3.3: Prism y la era de los parsers

Seguimos la serie ([todas las entregas, en la etiqueta ruby](/search?tag=ruby)) con [Ruby 3.3](https://www.ruby-lang.org/en/news/2023/12/25/ruby-3-3-0-released/), publicado el 25 de diciembre de 2023. Sobre el papel parecía una versión gris: ni una feature de sintaxis vistosa, ni una clase nueva que enseñar en un tuit. Y sin embargo, con la perspectiva de 2026, **fue una de las versiones más importantes de la década**: la de la fontanería. Parsers, JITs y threads. Lo que no se ve, pero lo sostiene todo.

## Prism: por qué reescribir un parser importa

La estrella del anuncio fue **Prism**, el nuevo parser de Ruby, obra de Kevin Newton. Durante casi treinta años, la gramática de Ruby vivió en `parse.y`, un fichero generado con Bison que era terreno vedado: quienes se habían asomado ahí dentro volvían canosos. Prism es lo contrario: un **parser de descenso recursivo escrito a mano, portable, mantenible y tolerante a errores**.

Cada adjetivo importa:

 - **Portable**: pensado para que lo compartan todas las implementaciones —CRuby, TruffleRuby, JRuby— y todas las herramientas que necesitan parsear Ruby: RuboCop, Sorbet, los language servers de tu editor. Hasta 3.3, cada uno mantenía su propio parser con sus propias discrepancias.
 - **Tolerante a errores**: ante código a medio escribir devuelve un AST parcial en vez de rendirse. Justo lo que un LSP necesita para autocompletarte mientras aún te falta un `end`.

En 3.3 llegó como *default gem* (con su API `Prism.parse(source)`) y como parser alternativo del propio intérprete vía `ruby --parser=prism`, entonces solo para experimentar:

```ruby
require "prism"

result = Prism.parse("1 + calamar")
result.success?  # => true
result.value     # => el AST del programa
```

De regalo, otra jubilación histórica: **Bison fue sustituido por Lrama**, un generador de parsers LALR propio. Retrospectiva: la jugada salió redonda. **Ruby 3.4 hizo de Prism el parser por defecto**, y hoy medio ecosistema de herramientas se apoya en él. Reescribir un parser no da titulares, pero decide qué herramientas puedes construir los diez años siguientes.

## YJIT: la versión en la que lo enciendes y te olvidas

Si 3.2 fue el año en que YJIT llegó a producción, 3.3 fue el año en que **dejó de haber motivos para no activarlo**. Las notas oficiales presumían de **más del triple de velocidad que el intérprete en Optcarrot**, y de una lista de mejoras muy poco glamurosas y muy efectivas: menos memoria de metadatos, compilación de splats, argumentos opcionales y manejadores de excepciones, y —mi favorita— **los call sites megamórficos ya no escapan al intérprete**.

Pero el cambio que más consecuencias tuvo fue una simple API:

```ruby
RubyVM::YJIT.enable
```

Activar YJIT **en caliente, desde el propio código**, después del boot. Eso permitió a Rails 7.2 encenderlo por defecto para todo el mundo con Ruby 3.3+ (con `config.yjit = false` como puerta de salida). Piénsalo: la optimización pasó de "flag que conocen cuatro" a **valor por defecto del framework**. En mis aplicaciones fue la primera versión en la que el JIT no fue una decisión, sino un hecho. Detalle fino adicional: el umbral de compilación pasa solo de 30 a 120 llamadas cuando la aplicación supera los 40.000 ISEQs, es decir, YJIT se autorregula en apps grandes… como cualquier monolito Rails con solera.

## RJIT: un JIT escrito en Ruby (y por qué no debías usarlo)

Ruby 3.3 también estrenó **RJIT**, un JIT **escrito en Ruby puro** que sustituyó a MJIT (aquel que compilaba C en un proceso aparte). Solo x86-64 en Unix, sin necesidad de compilador de C en runtime y, según las propias notas, **"solo con fines experimentales"**: para producción, YJIT.

¿Un JIT en Ruby generando código máquina para ejecutar Ruby? Como artefacto intelectual es precioso, y como banco de pruebas para investigar ideas de JIT bajaba muchísimo la barrera de entrada. Retrospectiva honesta: **se quedó en anécdota**, exactamente como prometía. No conozco un solo despliegue serio con RJIT, y en las notas de 3.4 ya ni aparece. Pero cumplió su papel: mantener viva la experimentación mientras YJIT se llevaba la gloria.

## Threads M:N: fontanería para el futuro

La tercera pata invisible: un **scheduler M:N de threads**, que multiplexa M threads de Ruby sobre N threads nativos del sistema para abaratar su creación y gestión. En el Ractor principal viene desactivado (rompería compatibilidad con extensiones C) y se enciende con `RUBY_MN_THREADS=1`; el número de threads nativos se controla con `RUBY_MAX_CPU` (por defecto, 8).

¿Lo notó alguien en una app Rails con Puma? **Nadie que yo conozca.** Es infraestructura pensada para el mundo que viene: Ractors baratos, miles de threads ligeros, concurrencia de verdad. En 2026 sigue siendo más cimiento que edificio, y lo cuento igualmente porque estas versiones "aburridas" son las que hacen posible lo que luego sí sale en los titulares.

## IRB por fin da gusto

Mientras tanto, en la superficie, **IRB dio el estirón definitivo**: integración avanzada con el debugger vía `irb:rdbg`, paginador para `ls`, `show_source` y `show_cmds`, autocompletado experimental basado en análisis de tipos y colores configurables con `Reline::Face`. 

Traducción para rails-eros: la `rails console` de serie alcanzó (y para mí superó) lo que durante una década nos hizo instalar pry en cada Gemfile. Mi ritual de `gem "pry-rails"` en todo proyecto nuevo murió, en la práctica, con Ruby 3.3.

## Detalles que suman

Dos apuntes más que me gusta rescatar. El GC aprendió a promocionar objetos jóvenes con más criterio, **reduciendo significativamente la frecuencia de GC mayores** —en una app Rails eso es latencia p99, aunque nadie lo celebre—, y `defined?(@ivar)` se optimizó con *object shapes*. Y una miga con historia: usar `it` a secas dentro de un bloque empezó a emitir warning, **preparando el terreno para el `it` como parámetro implícito que llegaría en 3.4**. Las deprecaciones bien hechas también son ingeniería.

## Retrospectiva desde 2026

Ruby 3.3 es la versión que peor se vendía en una charla relámpago y la que mejor ha envejecido en la sala de máquinas. **Lo que importó**: Prism (hoy parser por defecto y columna vertebral del tooling), el YJIT que Rails enciende solo, un IRB de primera. **Lo que se quedó en anécdota**: RJIT, con honores, y un scheduler M:N que sigue esperando su momento.

Si la serie demuestra algo es que las versiones de Ruby que cambian tu día a día no siempre son las que estrenan sintaxis. Las notas oficiales, [aquí](https://www.ruby-lang.org/en/news/2023/12/25/ruby-3-3-0-released/); el desglose minucioso, en [rubychanges](https://rubyreferences.github.io/rubychanges/3.3.html).
