DEV Community

Cover image for The Birth and Death of JavaScript (2014): qué sigue siendo verdad y qué ya no
Juan Torchia
Juan Torchia Subscriber

Posted on • Originally published at juanchi.dev

The Birth and Death of JavaScript (2014): qué sigue siendo verdad y qué ya no

The Birth and Death of JavaScript (2014): qué sigue siendo verdad y qué ya no

JavaScript ejecuta en el browser con menos privilegios que cualquier otro runtime, y aún así terminó corriendo servidores, herramientas de build, bases de datos embebidas y pipelines de CI. Eso no fue un plan: fue una acumulación de parches sobre una decisión de 1995. Sí, leíste bien. Y entender por qué pasó eso —no solo que pasó— cambia cómo diseñás un stack hoy.

Mi tesis: la charla "The Birth and Death of JavaScript" de Gary Bernhardt (2014) no es nostalgia ni profecía fallida. Es un diagnóstico de fricción arquitectónica que sigue activo. Pero si la leés como receta, te vas a quemar. El valor está en el problema que señala, no en la solución que propone.

El problema real que señala la charla

Bernhardt describe un arco irónico: JavaScript nació como lenguaje de juguete, sobrevivió porque era el único lenguaje que corría en el browser, y esa posición de monopolio lo convirtió —contra toda lógica técnica— en el lenguaje más ejecutado del planeta.

La tensión central que identifica es esta: el browser necesita ejecutar código arbitrario de forma segura y rápida, y esas dos cosas están en conflicto permanente. Su apuesta en 2014 era que ASM.js (y luego WebAssembly, aunque no lo nombra así) eventualmente reemplazaría a JS como target de compilación, convirtiendo a JavaScript en un runtime de ensamblador que nadie escribe a mano.

Eso no pasó del todo. WebAssembly existe, es estable y tiene casos de uso reales —Figma, Google Earth, codecs de video—, pero no reemplazó a JavaScript como lenguaje de aplicación. Lo que sí pasó es más interesante: JavaScript mutó. TypeScript, bundlers, transpiladores y runtimes alternativos (Deno, Bun) son todos intentos de corregir las mismas fricciones que Bernhardt señalaba, sin abandonar el ecosistema.

Lo que conviene extraer de la charla en 2025 no es la predicción, sino la pregunta que la sostiene: ¿qué parte de mi stack existe porque es la mejor solución técnica, y qué parte existe porque es lo único que corría en ese contexto?

Qué conviene probar hoy con ese marco

El marco de Bernhardt —"el lenguaje correcto en el lugar correcto vs. el lenguaje que ganó por accidente"— es directamente aplicable a decisiones de arquitectura actuales. No como doctrina, sino como checklist de cuestionamiento.

Tomá un stack típico como el mío en juanchi.dev: Next.js, TypeScript, PostgreSQL, Railway. Hay decisiones técnicas ahí que sobreviven el escrutinio de Bernhardt y otras que no.

Checklist de fricción arquitectónica (Bernhardt-style):

# Preguntas para cada capa del stack

□ ¿Uso esta tecnología porque es la mejor herramienta para este problema?
□ ¿O porque es lo único que funciona en este contexto (browser, hosting, ecosistema)?
□ ¿El overhead de tipos/transpilación/bundling resuelve fricción real o la desplaza?
□ ¿Si pudiera elegir hoy desde cero, elegiría esto mismo?
□ ¿El límite de rendimiento de esta capa es aceptable para el caso de uso real?
Enter fullscreen mode Exit fullscreen mode

Ejemplo concreto y reproducible: TypeScript en el servidor (Node.js/Next.js API routes) sobrevive bien ese checklist. El overhead de compilación existe, pero el beneficio en contratos de tipos entre capas es medible —podés verlo en cualquier codebase donde Zod valida en runtime lo que TypeScript promete en compilación. Escribí sobre eso en Zod en el servidor y en el cliente: la fricción real no es el transpilador, es la brecha entre lo que el tipo dice y lo que llega por la red.

JavaScript en el servidor (Next.js App Router, por ejemplo) sobrevive con más condiciones. El modelo de caching es un contrato no obvio —lo detallé en Next.js App Router caching— y parte de esa complejidad existe porque el mismo runtime intenta ser cliente, servidor y edge a la vez. Eso es exactamente el tipo de tensión que Bernhardt describía: una tecnología expandiéndose hacia territorios donde no fue diseñada originalmente.

Dónde la gente se equivoca al leer esta charla

El error más común es usar "The Birth and Death of JavaScript" como argumento para no aprender JavaScript a fondo, o para justificar el salto a WebAssembly antes de tener un problema real de rendimiento.

El costo oculto de ese error:

# Escenario típico mal ejecutado

# 1. Dev lee que JS "va a morir" → no profundiza en el runtime
# 2. Escribe async/await sin entender el event loop
# 3. Bloquea el thread con operaciones síncronas pesadas
# 4. Culpa a JavaScript en lugar de al mal uso del runtime

# Diagnóstico reproducible:
node --prof mi-app.js
# Luego procesar con:
node --prof-process isolate-*.log | head -50
# Si ves "sync" dominando el tick profile, el problema no es el lenguaje
Enter fullscreen mode Exit fullscreen mode

El contraejemplo que más me interesa: Spring Boot, que viene de mi historia con Java, tiene sus propias fricciones heredadas —XML, verbosidad, arranque lento en contextos serverless. Pero no por eso se abandona Java. Se usan herramientas como GraalVM native image o se ajusta el caso de uso. La charla de Bernhardt aplica igual ahí: Java en un JAR gigante que arranca en 8 segundos existe porque resuelve un problema real de ecosistema empresarial, no porque sea técnicamente óptimo para una función serverless.

La receta incorrecta es: escuchar la crítica y tirarlo todo. La correcta es: entender qué fricción específica existe y si tiene solución dentro del stack actual o requiere un cambio de capa.

Para decisiones de autenticación, esa misma lógica aplica: JWT, Paseto y session tokens no se eligen por moda sino por fricción específica de cada contexto. Bernhardt habría aplaudido ese árbol de decisión.

Gotchas: dónde el análisis de 2014 tiene fecha de vencimiento

Tres puntos donde la charla envejece mal y conviene ser explícito:

1. WebAssembly no reemplazó a JavaScript como lenguaje de aplicación

Wasm es estable, tiene soporte en todos los browsers modernos y hay casos de uso sólidos. Pero el costo de interoperabilidad con el DOM sigue siendo alto. Escribir aplicaciones web en Wasm directamente —sin pasar por JS como glue code— es todavía un caso de uso de nicho. Bernhardt asumió que el peso del rendimiento iba a forzar la migración; en la práctica, los motores JS (V8, SpiderMonkey) mejoraron lo suficiente como para que esa presión no fuera crítica en la mayoría de los casos.

2. El ecosistema npm como factor de inercia

En 2014, npm tenía decenas de miles de paquetes. Hoy supera el millón. Esa masa crítica no existía en el análisis de Bernhardt. La fricción de migrar un ecosistema de esa escala es un argumento técnico real, no solo un argumento político. Deno lo intentó con un registry propio y tuvo que volver a ser compatible con npm para ganar adopción. Bun lo intentó priorizando compatibilidad desde el día uno.

3. TypeScript cambió la ecuación

La crítica de Bernhardt a JavaScript incluye implícitamente la falta de tipos. TypeScript —que en 2014 era versión 1.0, recién lanzado— no estaba en su radar como respuesta. Hoy es la respuesta mainstream a esa fricción específica. No perfecta —los edge cases de tipos en runtime los documenté en el post de Zod— pero suficiente para cambiar el análisis.

Checklist de validez temporal de la charla:

✅ Todavía válido:
   - La tensión entre seguridad de sandbox y rendimiento nativo
   - La pregunta sobre qué tecnologías existen por mérito vs. por monopolio
   - El valor de compilar a un target común en lugar de escribir para cada runtime

❌ Desactualizado:
   - ASM.js como solución (WebAssembly lo reemplazó y tiene su propio perfil de costos)
   - La predicción de muerte de JavaScript como lenguaje de aplicación
   - La subestimación del ecosistema npm como factor de inercia

⚠️ Requiere experimento propio:
   - Rendimiento de Wasm vs. JS para tu caso de uso específico
   - Costo real de interoperabilidad DOM en proyectos Wasm
   - Si TypeScript resuelve o solo desplaza las fricciones de tipos que Bernhardt señalaba
Enter fullscreen mode Exit fullscreen mode

Matriz de decisión: cuándo usar este análisis y cuándo ignorarlo

No toda decisión técnica necesita el marco de Bernhardt. Acá está la matriz de cuándo aplica y cuándo es distracción:

CUÁNDO VALE LA PENA EL ANÁLISIS:
┌─────────────────────────────────────────────────────────┐
│ ✅ Estás eligiendo runtime para un sistema nuevo        │
│ ✅ Tenés un cuello de botella de rendimiento real       │
│    (medido, no intuido)                                 │
│ ✅ Estás evaluando WebAssembly para una capa específica │
│ ✅ Querés cuestionar si JS en el servidor es la opción  │
│    correcta para tu caso (vs. Go, Java, Rust)          │
│ ✅ Estás diseñando la capa de tools/MCP donde el        │
│    runtime importa (ver post de MCP)                   │
└─────────────────────────────────────────────────────────┘

CUÁNDO ES DISTRACCIÓN:
┌─────────────────────────────────────────────────────────┐
│ ❌ Querés justificar no aprender el ecosistema JS       │
│ ❌ No tenés un benchmark real del problema              │
│ ❌ Estás en medio de un feature y esto no es el         │
│    cuello de botella                                   │
│ ❌ Ya tenés un equipo con expertise profundo en el      │
│    stack actual                                        │
│ ❌ El "problema de rendimiento" es < 100ms en p95       │
│    para tu caso de uso                                 │
└─────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Para herramientas como las que describe el post de MCP Model Context Protocol, el análisis de Bernhardt es relevante: estás eligiendo si el runtime de tu tool es TypeScript/Node, Python o algo compilado. Esa decisión tiene costos reales de portabilidad. Para una API CRUD con Prisma y PostgreSQL —como la que describo en Prisma query logging— el análisis de Bernhardt es ruido: el cuello de botella casi nunca es el runtime JS.

FAQ

¿Quién es Gary Bernhardt y por qué esta charla sigue circulando?

Gary Bernhardt es conocido principalmente por "Wat" (2012), una charla corta que exponía comportamientos extraños de JavaScript y Ruby con humor. "The Birth and Death of JavaScript" (2014) es diferente: es un análisis de varios minutos sobre la historia y la posible evolución del lenguaje, presentado en formato de charla satírica pero con argumentos técnicos reales. Sigue circulando porque la tensión que describe —monopolio de runtime vs. mérito técnico— no se resolvió, mutó.

¿WebAssembly terminó matando a JavaScript como predijo la charla?

No. Wasm tiene casos de uso reales y crecientes, pero coexiste con JavaScript; no lo reemplaza. La razón principal es la fricción de interoperabilidad con el DOM y el ecosistema npm. Compilar a Wasm agrega complejidad de toolchain que solo se justifica cuando hay un cuello de botella de rendimiento medido. Para la mayoría de aplicaciones web, V8 es suficientemente rápido.

¿Tiene sentido aprender WebAssembly si trabajo con TypeScript/Next.js?

Depende de qué querés hacer. Si escribís aplicaciones web estándar, no. Si trabajás con procesamiento de imágenes en el browser, codecs, simulaciones físicas o herramientas que originalmente eran binarios nativos, sí vale la pena entender Wasm como opción. El criterio no es "JS va a morir", sino "¿tengo un cuello de botella de CPU en el browser que JS no puede resolver?".

¿El análisis de Bernhardt aplica al backend también?

Parcialmente. En el backend, la elección de runtime es más libre —no hay monopolio del browser— así que la tensión que describe es menor. Java, Go, Rust, Python y Node.js compiten en igualdad de condiciones. Ahí el análisis de Bernhardt se convierte en una pregunta diferente: ¿elegís Node porque es el mejor tool para el problema o porque tu equipo ya sabe JS y querés compartir código con el frontend? Ambas son razones válidas, pero son razones distintas con costos distintos.

¿Por qué TypeScript no resuelve todos los problemas que señalaba Bernhardt?

TypeScript resuelve la fricción de tipos en tiempo de compilación. No resuelve la fricción de tipos en runtime —lo que llega por la red no respeta el esquema TypeScript a menos que lo validés explícitamente con algo como Zod. Tampoco resuelve los problemas de rendimiento del runtime JS, la complejidad del event loop o la falta de primitivas de concurrencia real. TypeScript es una mejora significativa sobre JavaScript sin tipos, pero es una mejora dentro del mismo runtime, no un cambio de paradigma.

¿Cuándo conviene realmente evaluar un cambio de runtime en lugar de seguir con JS/TS?

Cuando tenés evidencia medida, no intuición. Los criterios concretos: latencia p95 inaceptable después de optimizar el código JS, necesidad de paralelismo real (no concurrencia con event loop), integración con librerías nativas donde el overhead de FFI es un problema, o un equipo con expertise profundo en otro runtime que justifique el costo de cambio. Sin esas condiciones, cambiar de runtime es optimización prematura con costo de ecosistema.

Dónde me para esto como arquitecto

Lo honesto: la charla de Bernhardt me resulta más útil como ejercicio de cuestionamiento que como hoja de ruta. El marco —"¿esta tecnología está acá por mérito o por monopolio?"— es una pregunta que vale la pena hacerse para cada capa del stack. No para tirarlo todo, sino para saber exactamente qué estás pagando y por qué.

Lo que no compro de la lectura popular de la charla: que WebAssembly es el futuro inevitable y que JS es un zombie esperando ser reemplazado. Eso ignora el peso del ecosistema, la velocidad de los motores modernos y el hecho de que TypeScript cambió la ecuación de tipos de manera significativa.

Lo que sí compro: que hay fricciones heredadas en el stack JS/TS que no van a desaparecer con el próximo framework. El modelo de caching de Next.js App Router, la brecha entre tipos estáticos y validación en runtime, la complejidad del event loop en cargas mixtas —todo eso son fricciones reales que Bernhardt habría reconocido aunque no las haya nombrado.

Mi recomendación práctica: leé la charla una vez, usá el checklist de cuestionamiento para tu stack actual, medí antes de cambiar cualquier runtime, y no uses una predicción de 2014 para tomar una decisión de arquitectura en 2025 sin datos propios. La fricción que señala Bernhardt es real. La solución que propone ya tiene fecha de vencimiento.

El próximo paso concreto: agarrá una capa de tu stack donde sientas fricción y pasala por el checklist de arriba. No para cambiarlo, sino para saber si la fricción viene del problema o de la herramienta.


Este artículo fue publicado originalmente en juanchi.dev

Top comments (0)