¿Necesitamos más runtimes de Javascript?

Hace unos pocos días salió la primera versión estable de Bun, un nuevo runtime de Javascript. Una semana antes veía la luz Node 20, que introduce varios cambios muy esperados. Y a lo lejos (pero no tanto) asoma el cuello Deno, que desde su debut en 2018 hasta ahora fue adoptando usuarios de forma lenta pero segura. En el ecosistema dinámico y siempre cambiante de Javascript ¿Necesitamos más runtimes?

Vamos a averiguarlo, pero primero:

¿Qué es un runtime?

A diferencia de lenguajes compilados como C o C++ que generan un programa ejecutable e independiente, Javascript es un lenguaje interpretado y necesita un entorno de ejecución para poder, valga la redundancia, ejecutarse.

Esto significa que para realizar cualquier acción no alcanza con nuestro código, vamos a necesitar que “viva” en algún lugar. Tradicionalmente Javascript cumple todo su ciclo de vida en el navegador y se conecta a través de este con diferentes APIs, el event loop y demás mecanismos que le permiten funcionar. El navegador le provee un ecosistema donde existir y se ocupa de que el código se transforme en instrucciones que la computadora pueda entender.

Este contexto no es nada más ni nada menos que un entorno de ejecución, un runtime.

Si queremos ejecutar Javascript fuera del navegador seguimos necesitando este contexto, y ahí es donde entran en juego Bun, Deno y por supuesto, Node.js.

¿Cómo funciona? Veamos un ejemplo del proceso de Node.js pero que puede aplicarse, detalles más, detalles menos, a los demás.

Node.js compila nuestro código JavaScript al momento de su ejecución, utilizando el motor V8, el mismo que usa Chrome. Este proceso se conoce generalmente como compilación Just-In-Time y consta de los siguientes pasos:

Parsing: Cuando ejecutamos un archivo JavaScript en Node.js, el motor lo analiza y crea un Abstract Syntax Tree (Árbol de Sintaxis Abstracta)

Intérprete: Este AST se interpreta para generar código de bytes, la representación del código fuente pero a un nivel más bajo, que puede ser entendido por cualquier plataforma

Compilador de optimización: V8 cuenta con un compilador que optimiza este código de bytes a un código de máquina más eficiente. Esto se hace especialmente para las funciones o secciones del código que se acceden con frecuencia para que se ejecuten de forma más eficiente

Des-optimización: Si más adelante ciertas suposiciones hechas durante el proceso de optimización resultan incorrectas (por ejemplo, si cambian los tipos de las variables), el motor puede retroceder el código de máquina a una forma menos optimizada, incluso hasta el código de bytes si es necesario.

Este proceso de compilación permite que Node.js ejecute código Javascript a velocidades comparables con otros lenguajes compilados. Combina las ventajas del intérprete y del compilador para lograr un mejor rendimiento.

Entonces, Node.js compila JavaScript en tiempo de ejecución, lo que le permite ejecutar código de forma eficiente manteniendo al mismo tiempo la flexibilidad y dinamismo que caracterizan a Javascript pero por fuera del navegador.

Pero, si Node.js hace todo esto…

¿Por qué necesitamos otro runtime?

Bueno, ¿Por qué no? Si bien parece que todos hacen lo mismo la realidad es que cada runtime fue implementado de forma diferente. NodeJS se publicó en 2009, tiene más de una década de existencia. En esos catorce años pasó de ser una especie de experimento a usarse en código productivo de empresas enormes como Netflix o Linkedin. Con este tipo de uso el ecosistema de desarrollo mutó y maduró lo cual llevó a encontrar varios puntos de mejora entre ellos rendimiento, experiencia de desarrollo y seguridad.

En esta tabla podemos ver una pequeña comparación entre los tres runtimes actuales:

RuntimeNodeDenoBun
Año de publicación200920202023
MotorV8V8JavaScriptCore
LenguajesC++, CRustZig
TS integradoNoSiSi
Testing integradoSiSiSi
SoporteComunidad, voluntario, JS FoundationVC, For-profitVC, For-profit
LicenciaOpen sourceOpen sourceOpen source

¿Cuales son las fortalezas y debilidades de cada uno?

Node.js

Es la opción longeva y robusta, adoptada por miles de proyectos grandes y pequeños. Al tener más de diez años sus errores son evidentes (muchos de ellos fueron mencionados por su propio creador en esta charla) pero también la cantidad de herramientas y recursos que existen nos proveen una gran cantidad de opciones a la hora de comenzar un proyecto, lo cual lo mantiene viable a pesar de las alternativas.

Node.Js presenta los peores números en comparaciones de velocidad de ejecución y manejo de requests, con un poco de suerte la competencia traiga mejoras en estos y otros aspectos.

Deno

Deno es una iniciativa del creador de Node, en un intento de subsanar lo que él considera los peores errores de diseño de su predecesor.

Deno no pretende ser un reemplazo mejorado de NodeJS sino una alternativa para casos de uso específicos y basa su propuesta de valor en una experiencia de desarrollo enfocada en seguridad. A diferencia de Node, Deno exige que seamos explícitos en los permisos de acceso que le damos al código y las librerías que utilizamos. También intenta alejarse del tipado dinámico permitiendo transpilar proyectos de typescript directamente sin ningún tipo de configuración adicional.

Otra característica particular es el soporte de algunas APIs que podemos encontrar en el navegador pero nunca fueron implementadas en node como Fetch.

Pero Deno tiene un gran problema de adopción: sus paquetes no son directamente compatibles con Node, esto hace que migrar proyectos existentes requiera una inversión de tiempo que no todo el mundo está dispuesto a hacer. También aleja a los desarrolladores que prefieren mantenerse en el dominio de sus herramientas conocidas.

Detrás de Deno se encuentra Deno Land Inc, que entre otras cosas provee un servicio de deploy para Deno con una filosofía similar a Next.js y Vercel.

Bun

Bun es el nuevo del que todos hablan, y llega en una tormenta de marketing basada en pruebas de perfomance donde destroza a la competencia. No de dejen engañar por el logo amigable, Bun lo deja bien claro: su propósito es reemplazar a NodeJS y correr la mayor parte del código Javascript de toda la web. Al menos es sincero.

Al igual que Deno ya viene preparado para transpilar Typescript de forma nativa, y a esto le suma también JSX. Con estas decisiones se empieza a desdibujar la linea entre javascript en el frontend y backend, pero como dijimos antes: lo importante es correr la mayor parte del código javascript en la web, y nadie dijo que se limite al backend.

Al querer ser un reemplazo directo Bun presenta cambios pero también se siente similar en muchos aspectos, es compatible con npm y node_modules y la configuración de los proyectos es muy similar a lo que solemos ver en node.

Bun está programado en Zig, un lenguaje de programación que se presenta como la alternativa moderna a C, y al igual que su sucesor hace mucho énfasis en el manejo manual de memoria. Al mismo tiempo la implementación de este runtime se desvía del tradicional V8 y elige como motor a JavascriptCore. Bun es particularmente rápido en todo lo que hace. Se instala, corre, hace requests a servers y bases de datos todo a una velocidad impresionante, o eso es lo que vemos en las pruebas.

Detrás de Bun se encuentra Oven, una empresa privada que ya levantó 7M en capital y va a ofrecer servicios cloud para ejecutar aplicaciones javascript de punta a punta.

Conclusiones

El ecosistema de Javascript lleva un tiempo tendiendo a la modalidad Vercel: empresas que usan parte de su presupuesto en desarrollar y mantener herramientas para desarrolladores y montan su negocio en la infraesctructura para llevarlas a producción. Esto abre el juego a la competencia, porque seamos sinceros, es posible comenzar este tipo de proyectos de forma independiente pero es mucho más fácil hacerlo con recursos en la espalda.

Para el ecosistema siempre es bueno que surjan alternativas y que los desarrolladores tengamos más herramientas para elegir, sobre todo si cada alternativa pone su foco en diferentes casos de uso.

En este panorama Deno se presenta todavía como una alternativa lejana, casi de nicho. La falta de compatibilidad directa hace que la adopción sea lenta. A pesar de esto, creo que tiene suficientes ventajas como para brillar en proyectos con foco en seguridad.

El enfoque de Bun de destronar a Node me parece ambicioso y un poco peligroso. El tiempo dirá si la adopción de la comunidad y sobre todo, de las grandes empresas, lo convierten en el sucesor o en un competidor más.

Existe valor en tener proyectos que nos quieran dar todo servido porque mucho del trabajo del programador implica configurar boilerplate y replicar recetas existentes. En estos casos los toolkits ahorran tiempo y mejoran la experiencia de desarrollo. Pero no hay que caer en la trampa de enamorarse de una herramienta porque como dice el refrán “para quien tiene un martillo todos los problemas son clavos”. No queremos , en pos de usar una tecnología, forzar soluciones cuando la alternativa (robusta y probada) es mejor.

También es importante destacar que cuando las herramientas responden a intereses económicos de empresas muchas veces su roadmap de desarrollo se ve afectado por esos intereses. En ese sentido es vital para el ecosistema que existan alternativas que no estén atadas a una mesa de inversores.

Unos días después de que empecé a escribir ester articulo James M Snell, que además de contribuir en Node es parte del comité que decide futuras mejoras dijo lo siguiente en un post.

“Node.js no es un producto. No tiene roadmap. No tiene una estrategia de marketing. No tiene clientes. No tiene inversores ni partes interesadas a quienes responde, no tiene un mercado que defender contra su competencia. No existe para generar ganancias. No le pertenece a nadie.”

Mejor resumen, imposible. Mientras todos se pelean por reemplazarlo Node.js es Mariah Carey diciendo “no la conozco”.

En la industria del software no somos extraños al concepto de usar software propietario ni tampoco al open source. Ambas modalidades funcionan en una simbiosis que le permite al ecosistema hacer dinero sin convertirse en un entorno completamente restrictivo.

Pero a veces no alcanza con que un proyecto sea meramente open source, las motivaciones para su creación también importan. No es lo mismo un proyecto enteramente mantenido por la comunidad donde las decisiones se toman en función del ecosistema de desarrollo, que un proyecto open-source pero con un board de inversores esperando ver algún tipo de ganancia por detrás.

Y si no me creen, vayan a preguntarle a su programador java favorito por que todavía usa Java 8.

En un mundo donde el open source está más vigente que nunca pero se perdió un poco la filosofía a su alrededor es fácil dejarse seducir por las lineas verdes de un benchmark bien armado, pero como yo lo veo Node.js no se va a ninguna parte. Los cambios de tecnología, incluso cuando la migración es simple, suelen ser costosos a nivel humano y económico, con lo cual es probable que si vemos adopción de otros runtimes sea primero en nuevos proyectos. Si eso funciona quizás empecemos a ver migración de código productivo… pero no esperen que pase de un día para el otro. De todos modos recomiendo siempre explorar tanto Bun como Deno y mantenerse al día con el estado de Javascript.

La buena noticia para nosotros es que si sabemos Javascript movernos entre de runtime no debería ser problema. Por lo demás, recuerden que ustedes eligen a sus herramientas y no al revés.

Sigamos en contacto: suscribite a Sin códigos, mi newsletter quincenal. También podés seguirme en redes para estar al tanto de todo mi nuevo contenido