Daniel Murillo
Índice

Arquitectura · 28 de octubre de 2026 · Daniel Murillo

Node.js vs Bun vs Deno en 2026: una decisión de arquitectura

El mejor runtime es el que cumple tus requisitos con el equilibrio correcto de rendimiento, compatibilidad, seguridad y costo operativo.

El mejor runtime es el que cumple los requisitos de tu aplicación con el equilibrio correcto de rendimiento, compatibilidad, seguridad, complejidad y costo operativo.

Introducción: tres runtimes, distintas prioridades

Node.js, Bun y Deno ejecutan JavaScript, pero no persiguen exactamente la misma propuesta.

  • Node.js apuesta por un ecosistema amplio, compatibilidad y una plataforma operativa madura.
  • Bun combina un runtime basado en JavaScriptCore con herramientas integradas y busca un alto rendimiento en múltiples escenarios.
  • Deno pone énfasis en permisos explícitos, estándares web y herramientas integradas, mientras amplía la compatibilidad con Node y npm.

La comparación se vuelve confusa cuando se pregunta simplemente cuál es «el mejor». Un runtime puede ser excelente para scripts y CLIs, otro puede reducir el riesgo de una plataforma empresarial, y otro puede encajar mejor cuando los permisos y las APIs web son prioridades.

La decisión no debería ser un concurso de popularidad ni una tabla ordenada por requests por segundo. Debe partir de la carga, las dependencias y las capacidades operativas que el proyecto necesita.

1. Resumen ejecutivo

DimensiónNode.jsBunDeno
MotorV8JavaScriptCoreV8
PrioridadCompatibilidad y ecosistemaRendimiento y herramientas integradasSeguridad, estándares web y tooling
TypeScriptSoporte integrado para sintaxis común en versiones recientes; type-check separadoEjecución/transpilación integrada; type-check separadoEjecución y deno check integrados
Paquetesnpm, pnpm, Yarnbun install y paquetes npmnpm, JSR y lockfiles de varios gestores
APIs de NodeReferencia principalCompatibilidad amplia, no totalCompatibilidad creciente, no total
Permisos de I/ONormalmente según el usuario del proceso y el sistema operativoNo es el diferenciador centralPermisos explícitos para archivos, red, entorno y procesos
Frameworks representativosExpress, Fastify, NestJS, AdonisJS, HonoElysia, Hono; también muchas apps Node compatiblesHono, Fresh, Oak; muchas dependencias npm con validación
Fortaleza típicaDependencias maduras y operación conocidaArranque, tooling y ciertos workloads de rendimientoModelo de permisos y experiencia integrada
Principal cautelaTooling fragmentado y necesidad de gobernar dependenciasDiferencias de compatibilidad y observabilidadCompatibilidad concreta de dependencias y permisos operativos

La tabla es una guía inicial, no una clasificación absoluta. Las capacidades evolucionan con cada versión.

2. No mezclemos las capas

Para comparar con rigor, hay que separar cuatro capas:

  1. Motor JavaScript: V8 o JavaScriptCore.
  2. Runtime: APIs de red, archivos, procesos, módulos, timers y herramientas del sistema.
  3. Framework: routing, middleware, validación, inyección de dependencias y convenciones.
  4. Aplicación: lógica de negocio, consultas a base de datos, autenticación, caché, logging y llamadas a terceros.

Un benchmark de Bun.serve() no es una comparación justa contra una aplicación NestJS. Tampoco una prueba de Express con TLS permite concluir cuál runtime será mejor en un servicio que pasa la mayor parte del tiempo esperando PostgreSQL.

La comparación debe indicar qué capa está midiendo.

3. Benchmarks publicados: evidencia útil, no sentencia final

Los benchmarks de los proyectos permiten identificar hipótesis. No deben mezclarse como si todos provinieran de una misma máquina y metodología.

Express sobre HTTPS

Bun publica una prueba de Express 5.2.1 con TLS 1.3, bombardier, Linux x64 y AMD EPYC. Los datos son medianas de tres ejecuciones:

RuntimeVersiónRequests/sRAM máxima
Bun1.448 243105 MB
Node.js26.7.025 181142 MB
Deno2.9.519 243176 MB

En esta prueba, Bun logra cerca de 1,9× el throughput de Node y 2,5× el de Deno. Eso es relevante para investigar el comportamiento de Express sobre esos runtimes, pero no prueba que cualquier API de negocio vaya a mejorar en la misma proporción.

Fuente: Bun — benchmarks

PostgreSQL: cuidado con comparar drivers diferentes

La misma página compara una carga de PostgreSQL con 100 filas por consulta, 100 consultas en vuelo y 100 000 consultas totales. Bun utiliza su API SQL integrada, mientras Node y Deno usan postgres.js 3.4.9.

Runtime / driverConsultas/sRAM máxima
Bun 1.4 (Bun.sql)20 24380 MB
Deno 2.9.5 (postgres.js)10 406181 MB
Node.js 26.7.0 (postgres.js)10 000168 MB

No es una comparación pura del runtime. Cambia también el cliente de base de datos, por lo que el resultado representa el conjunto de implementación y configuración. Para una comparación controlada, usa el mismo driver si está disponible y verifica que cada versión haga exactamente el mismo trabajo.

Fuente: Bun — benchmarks

Deno 2.9 frente a Deno 2.8

Deno reportó estas mejoras en su servidor Deno.serve, con concurrencia 100, en una máquina Linux x86_64 dedicada:

MétricaDeno 2.8Deno 2.9
Cold start34,2 ms17,3 ms
HTTP realworld56,8k req/s72,4k req/s
HTTP texto77,0k req/s85,6k req/s
Respuesta de 1 MiB1 617 req/s1 907 req/s
RSS, realworld142 MB64 MB

Es una mejora entre versiones de Deno, no una comparación directa con Bun o Node. La carga «realworld» del benchmark recibe JSON con un bearer token y lo devuelve como JSON, pero sigue siendo una aplicación simplificada.

Fuente: Deno 2.9 — performance

Streams: optimizar una API concreta importa

Bun 1.4 publicó una prueba de Response.clone() con un cuerpo de 64 MB. En la configuración descrita por el proyecto, Bun reporta 220 MB de pico de memoria y 96 ms, Node.js 26 reporta 382 MB y 230 ms, y Deno 2.9 reporta 297 MB y 134 ms.

El artículo de Bun explica que una optimización evita copiar el cuerpo completo en la segunda rama. Es una ventaja importante para esa operación, no evidencia de que toda manipulación de streams sea siempre más rápida.

Fuente: Bun 1.4 — performance

¿Qué conclusión se puede extraer?

  • Bun muestra ventajas importantes en varios escenarios publicados por su equipo.
  • Deno 2.9 mejoró de forma considerable frente a Deno 2.8 en las pruebas que publicó.
  • Node.js sigue siendo una opción competitiva, pero su ventaja más amplia suele estar en el ecosistema y la compatibilidad, no en ganar cada microbenchmark.
  • No se puede construir un ranking universal mezclando tablas con diferentes cargas.
  • Una aplicación real debe medirse con sus dependencias, configuración, límites y SLO.

4. ¿Qué runtime elegir según el proyecto?

Plataforma empresarial con muchas dependencias

Primera opción a evaluar: Node.js.

Es especialmente razonable cuando la aplicación depende de SDKs cloud, módulos nativos, drivers, agentes APM y librerías cuyo soporte principal es Node. El valor está en reducir incertidumbre, facilitar diagnóstico y aprovechar la experiencia del equipo.

Bun y Deno siguen siendo opciones posibles, pero habría que validar cada integración crítica y comparar el costo de mantener las diferencias.

API nueva en TypeScript

Evaluaría los tres.

  • Node.js con Fastify o NestJS ofrece una base madura.
  • Bun con Bun.serve() o Elysia puede ser atractivo si el rendimiento o el tooling integrado importan.
  • Deno con Hono puede resultar interesante si se valoran APIs web, permisos y herramientas integradas.

La elección final depende de las dependencias, el estilo arquitectónico del equipo y los resultados de una prueba representativa.

CLI, scripts y automatización

Bun y Deno merecen especial atención.

Sus herramientas integradas pueden simplificar el desarrollo y el arranque. Deno aporta un modelo de permisos útil para scripts que no deberían tener acceso irrestricto a todo el sistema. Bun puede ser atractivo por su velocidad y su integración de gestor de paquetes, test runner y bundler.

Node sigue siendo una buena elección cuando el script depende de paquetes o APIs específicas de Node.

Servicio con requisitos de seguridad por permisos

Evaluaría Deno.

Su modelo de permisos explícitos permite limitar acceso a archivos, red, entorno y procesos. La ventaja es especialmente útil cuando esos límites forman parte del diseño. No reemplaza el aislamiento del sistema operativo, la revisión de dependencias ni los controles de despliegue.

WebSockets, streaming o alto throughput

No elijas por el nombre del runtime.

Bun publica resultados interesantes de WebSockets y streams; Deno ha mejorado su servidor HTTP; Node dispone de un ecosistema maduro. Pero las diferencias dependen del patrón de mensajes, tamaño de payload, fan-out, TLS, backpressure, serialización y confirmación de entrega.

Haz una prueba de carga con la topología real: número de conexiones, mensajes por segundo, tamaño de mensajes, percentiles de latencia, memoria y reconexiones.

5. Frameworks: elegirlos por separado del runtime

FrameworkEntorno habitualPor qué evaluarlo
ExpressNode.jsEcosistema extenso y simplicidad
FastifyNode.jsValidación, serialización, plugins y bajo overhead
NestJSNode.jsEstructura modular, inyección de dependencias y convenciones
ElysiaBunDiseño estrechamente integrado con Bun y TypeScript
HonoNode.js, Bun, Deno y otrosAPIs web y portabilidad
FreshDenoAplicaciones web con renderizado en servidor e islas
OakDenoMiddleware framework con estilo inspirado en Koa

Las columnas no significan que un framework no pueda ejecutarse fuera de su entorno habitual. La compatibilidad real depende de las APIs, plugins y dependencias que use el proyecto.

Si la portabilidad importa, usar estándares web y aislar APIs de runtime puede reducir el acoplamiento. Si se requiere un framework con un ecosistema muy específico, prioriza la compatibilidad y la experiencia operativa sobre una abstracción teórica.

6. Una metodología justa de benchmarking

Un benchmark útil debe contestar una pregunta concreta. Antes de ejecutar pruebas, define la hipótesis: ¿quieres reducir costo por request, mejorar p99, bajar memoria por réplica, acelerar cold starts o reducir el tiempo de CI?

Condiciones controladas

  1. Fija las versiones exactas de runtime, framework y dependencias.
  2. Usa el mismo hardware, sistema operativo y límites de contenedor.
  3. Ejecuta el mismo trabajo de negocio y, cuando sea posible, el mismo framework.
  4. Usa el mismo dataset, pool de conexiones, caché y configuración de TLS.
  5. Verifica que el generador de carga no sea el cuello de botella.
  6. Repite las ejecuciones y registra variabilidad.
  7. Separa arranque en frío de rendimiento sostenido.
  8. Documenta código y configuración para que otro equipo pueda reproducir el resultado.

Métricas

  • Throughput: solicitudes completadas por segundo.
  • Latencia p50, p95 y p99.
  • CPU por solicitud.
  • Memoria RSS y evolución del heap.
  • Tiempo de arranque.
  • Errores, timeouts y conexiones rechazadas.
  • Estabilidad bajo carga sostenida y durante picos.
  • Costo de infraestructura para cumplir el SLO.

No reportes solo requests por segundo. Un servidor puede producir más throughput a costa de una cola de latencia más larga o de un consumo de memoria que obliga a escalar más réplicas.

Tres escenarios

A. Endpoint mínimo: permite aproximarse al techo del servidor.

B. API representativa: routing, validación, autenticación, serialización y acceso a datos.

C. Simulación de producción: TLS, tracing, logs, límites de contenedor, carga sostenida y fallos de dependencias.

La prueba B o C debería pesar más en la decisión que la prueba A.

7. Costos que no aparecen en una tabla de rendimiento

Costo de compatibilidad

Una dependencia que no funciona puede costar más que una mejora de throughput. Cuenta las integraciones críticas, las APIs incompatibles, los parches y las horas de mantenimiento.

Costo operativo

Evalúa el soporte en plataformas de despliegue, imágenes base, health checks, apagado ordenado, workers, observabilidad, profiling y respuesta a incidentes.

Costo de aprendizaje

Un equipo experto en Node puede entregar más rápido con Node aunque otro runtime sea superior en un benchmark. Un equipo nuevo puede preferir el tooling integrado de Bun o Deno. El costo de aprendizaje es parte de la decisión.

Costo de infraestructura

Si un runtime reduce memoria o CPU en una carga real, podría disminuir el número de réplicas. Pero esa mejora debe mantenerse con las dependencias, logs y límites de producción. Calcula el costo por volumen de tráfico o por tarea completada, no solo la velocidad máxima.

Costo de migración

Migrar un sistema en funcionamiento implica pruebas, rollout, observabilidad, rollback y capacitación. Una mejora marginal en un endpoint no suele justificar ese costo.

8. Matriz de decisión ponderada

Puntúa cada criterio para tu proyecto del 1 al 5 y asigna un peso. No uses una puntuación universal para todos los equipos.

CriterioPeso inicial sugeridoNode.jsBunDeno
Compatibilidad con dependencias críticas5Suele ser la referenciaValidar APIs y módulos nativosValidar APIs y módulos nativos
Rendimiento en la carga real5MedirMedirMedir
Seguridad por permisos de runtime4Requiere controles adicionalesNo es su diferenciador principalDiferenciador central
Tooling integrado3Ecosistema modularIntegradoIntegrado
Observabilidad y soporte operacional5Ecosistema muy amplioProbar APM y profilingProbar APM y profiling
Experiencia del equipo4Depende del equipoDepende del equipoDepende del equipo
Tiempo de arranque2–5 según el casoMedirMedirMedir
Costo por carga atendida5MedirMedirMedir
Portabilidad entre runtimes2–5 según el casoAPIs de NodeMejor si se usan estándares webMejor si se usan estándares web

Los pesos son un punto de partida, no una recomendación inmutable. En una CLI, el arranque puede tener peso 5; en una API de negocio dominada por SQL, la velocidad de arranque quizá tenga poco impacto.

9. Estrategia de adopción con riesgo controlado

No es obligatorio estandarizar toda una organización en un solo runtime desde el primer día. Una estrategia razonable es:

  1. Mantener el runtime actual como baseline.
  2. Seleccionar un servicio pequeño con dependencias acotadas.
  3. Automatizar tests funcionales y de compatibilidad.
  4. Comparar rendimiento con condiciones controladas.
  5. Probar observabilidad, contenedores, health checks y apagado.
  6. Desplegar gradualmente con métricas y rollback.
  7. Ampliar el uso solo si los beneficios son sostenibles.

Evita terminar con tres runtimes por moda. La diversidad puede ser útil, pero añade versiones, pipelines, documentación y habilidades que mantener. Introduce una nueva plataforma cuando resuelva un problema concreto.

10. Recomendación final

Elige Node.js cuando la compatibilidad, el ecosistema y la operación conocida sean prioritarios, especialmente en sistemas grandes con muchas dependencias e integraciones.

Elige Bun cuando el tooling integrado, el arranque o el rendimiento medido en tu carga aporten un beneficio concreto y puedas validar las dependencias críticas.

Elige Deno cuando el modelo de permisos, las APIs web y el flujo de desarrollo integrado encajen con los requisitos de seguridad y arquitectura.

No hay que convertir estas tendencias en reglas absolutas. Node.js puede ser más apropiado para una API nueva; Bun puede ser una gran elección para un sistema empresarial; Deno puede ser una opción válida aunque el equipo use algunas librerías npm. Lo determinante es el ajuste entre requisitos y capacidades.

La mejor decisión no es el runtime que gana un benchmark publicado por su propio equipo. Es el que cumple los objetivos de latencia, costo, seguridad y mantenibilidad con un nivel de riesgo que el equipo puede sostener.

Mide tu aplicación. Revisa las dependencias. Decide según el problema.

En esta serie

  1. Node.js: por qué sigue siendo una apuesta sólida
  2. Bun: ¿realmente es más rápido que Node.js?
  3. Deno: seguridad, TypeScript y herramientas
  4. Comparativa final (esta nota)

Lecturas relacionadas

Referencias

Los benchmarks se atribuyen a los proyectos que los publican. Los resultados no forman una clasificación independiente y universal: varían por versión, carga, hardware, framework, driver y configuración. Antes de tomar una decisión de producción, reproduce las pruebas relevantes y añade una prueba representativa de tu aplicación.