Saltar al contenido

Bun pasó de Zig a Rust en 11 días con Claude Fable 5 y 64 agentes en paralelo

Jarred Sumner reescribió Bun de Zig a Rust en 11 días con Claude Fable 5 y unos 165 mil dólares en tokens.

por Dilis Salazar
Bun pasó de Zig a Rust en 11 días con Claude Fable 5 y 64 agentes en paralelo
Photo by Mohammad Rahmani / Unsplash

TL;DR:

  • Jarred Sumner portó las 535,496 líneas de Zig de Bun a Rust en 11 días, con cerca de 50 flujos de trabajo dinámicos en Claude Code.
  • El diff final fue de 1,009,272 líneas repartidas en 6,778 commits, y el consumo de tokens equivale a unos 165,000 dólares a precio de API.
  • Bun v1.4.0 corrige 128 bugs de la versión anterior, pesa 20% menos y corre entre 2% y 5% más rápido, pero sigue en canary.

Jarred Sumner, creador de Bun, reescribió el runtime completo de JavaScript de Zig a Rust en 11 días, apoyado en una versión pre-release de Claude Fable 5 y en unos 64 agentes de Claude Code trabajando al mismo tiempo. Lo contó el 8 de julio de 2026 en el blog de Bun, y abrió el texto con una advertencia en la primera línea: Anthropic compró Bun en diciembre de 2025 y él trabaja ahí. El saldo son 6,778 commits, un diff final de 1,009,272 líneas y un gasto de tokens que Sumner calcula en unos 165,000 dólares a precio de API. Su estimación es que tres ingenieros con contexto completo del código habrían necesitado cerca de un año para hacer lo mismo a mano.

Bun es un runtime de JavaScript y TypeScript que además funciona como bundler, gestor de paquetes y corredor de pruebas. Su CLI pasa de los 22 millones de descargas mensuales, y herramientas como Claude Code y OpenCode corren encima de él. Nada de eso lo libraba de la lista con la que arranca el post: crashes de use-after-free en node:zlib, fugas de memoria en fs.watch(), un double-free en el parser de CSS cuando background-clip traía prefijos de proveedor.

El problema nunca fue Zig, sino mezclar recolector de basura con memoria manual

Sumner no le pasa la factura al lenguaje. Al contrario: dice que sin Zig, Bun jamás habría existido, y que otros proyectos escritos en Zig no cargan con estos bugs. El choque está en otro lado. JavaScriptCore, el motor que Bun embebe, tiene recolector de basura. Zig no tiene constructores ni destructores, y espera que cada limpieza se escriba explícitamente en cada punto de llamada con defer.

Así, cada asignación de memoria pasaba por un interrogatorio: dónde se libera, cómo se garantiza que se libere una sola vez, si ese puntero es visible para el escáner conservador de la pila. Buena parte de los bugs cae en tres cajones: use-after-free, double-free y olvidar liberar en una ruta de error. En Rust seguro, los tres son errores de compilación, y Drop corre la limpieza solo. Un compilador que grita es mejor bucle de retroalimentación que una guía de estilo que alguien tiene que recordar en el code review.

"Hasta hace muy poco, la elección del lenguaje de programación era una decisión sin retorno para un proyecto como Bun."
Jarred Sumner, creador de Bun

Ahí está el nervio del texto. Simon Willison, que reseñó el post el mismo día, lo conecta con el clásico de Joel Spolsky de abril del 2000, aquel que puso la reescritura desde cero en la lista de cosas que nunca deberías hacer. Los agentes de codificación mueven esa aritmética.

La suite de pruebas en TypeScript fue el seguro de vida del experimento

Aquí está el detalle que casi nadie subraya y que Willison identifica como el factor habilitante: la suite de pruebas de Bun está escrita en TypeScript, no en Zig. Es independiente del lenguaje de implementación, así que funcionó como banco de conformidad y el arnés de agentes pudo correr contra ella sin tocarla.

Los números de esa red de seguridad, medidos en Debian 13 x64: 1,386,826 llamadas a expect(), 60,624 pruebas y 4,174 archivos. Cero pruebas saltadas, cero pruebas borradas. Sumner dice que verificó a mano que las pruebas efectivamente corrieran y no estuvieran siendo omitidas antes de fusionar.

Un Claude escribe, dos Claudes buscan por qué está mal

El método se llama revisión adversaria y es lo más transferible de todo el post. El implementador ve el archivo .zig original, el plan de porteo y su propio razonamiento. El revisor recibe únicamente el diff, sin acceso al razonamiento del que escribió, y una instrucción: asume que el código está mal y encuentra por qué. Un implementador, dos o más revisores. El que implementa no revisa, el que revisa no implementa.

Tres bugs que los revisores atraparon antes del merge, y los tres compilaban limpio:

  • Un Box<uv::Pipe> se destruía al terminar el brazo del match mientras libuv seguía apuntando a esa memoria hasta el siguiente tick del loop. Use-after-free y, acto seguido, double-free.
  • Al partir segundos en un timespec, trunc() redondeaba hacia cero y producía nanosegundos negativos para archivos con fecha de modificación anterior a 1970. floor() lo arregla.
  • Dentro de color-mix(), un unwrap_or() evaluaba su argumento de forma ansiosa y hacía panic justo antes de llegar a ignorarlo.

El trabajo se organizó en unos 50 flujos dinámicos que hacían, en orden, esto:

  • generar una guía de porteo que mapeara patrones y tipos de Zig contra sus equivalentes en Rust
  • portar mecánicamente cada uno de los 1,448 archivos .zig a un .rs, siguiendo esa guía y un archivo LIFETIMES.tsv con los tiempos de vida propuestos para cada campo de cada struct
  • arreglar los errores de compilación crate por crate
  • hacer funcionar subcomandos como bun test o bun build
  • pasar la suite completa, primero local y después en CI
  • varios refactores grandes y pasadas de limpieza

Los falsos arranques dan más información que los éxitos. A los dos minutos del primer intento masivo, un Claude corrió git stash y otro git stash pop: se estaban pisando. Sumner reescribió el flujo para prohibir cualquier comando de git que no fuera un commit de archivo específico. Después, Claude interpretó "hagamos que compilen todos los crates" como "deja en stub las funciones que dan error", y empezó a escribir comentarios kilométricos justificando cada workaround. Sumner agregó entonces una regla para los revisores adversarios: si un workaround necesita un párrafo entero de comentario para justificarse, el código está mal y hay que arreglar el código. Un ajuste al prompt, unas horas, y el patrón desapareció.

La escala habla sola. Cuatro worktrees, 16 Claudes por flujo, unos 64 agentes simultáneos en el pico. Ahí la máquina escribía cerca de 1,300 líneas por minuto y llegó a 695 commits en una sola hora. Cuando la separación del monolito en cerca de 100 crates destapó unos 16,000 errores de compilación, esos errores se convirtieron en la cola de trabajo: cargo check los volcaba a un archivo, agrupados por crate, y los agentes se los repartían.

Z.ai lanza ZCode para GLM-5.2 y desafía a Claude Code, Cursor y Copilot con precios más bajos
Z.ai lanza ZCode, su entorno con IA para GLM-5.2: gratis, multiplataforma y desde 16.20 dólares al mes.
black flat screen computer monitor turned on near blue and white sky
Photo by David Schultz / Unsplash

Bun v1.4.0 pesa 20% menos, corre un poco más rápido y arrastra 4% de código unsafe

Los resultados que publica el equipo, con sus asteriscos incluidos:

  • 128 bugs que se reproducen en la v1.3.14 quedaron corregidos en la v1.4.0, desde fugas de memoria hasta texto de ayuda mal coloreado.
  • El binario adelgaza cerca de 20% en Linux y Windows, sumando la reescritura, cambios en ICU y optimizaciones del linker: Windows pasa de 94 a 76 MB, Linux de 88 a 70 MB.
  • La memoria deja de crecer sin control. Dos mil llamadas seguidas a Bun.build() consumían 6,745 MB en la v1.3.14 y se estabilizan en 609 MB en la v1.4.0.
  • El rendimiento sube entre 2% y 5%: Bun.serve gana 4.8% en peticiones por segundo, express 3.2%, y next build recorta 4.5% del tiempo.
  • Hubo 19 regresiones conocidas, todas ya corregidas. Casi todas nacieron de código que se ve idéntico en los dos lenguajes y se comporta distinto: un debug_assert! de Rust es macro y borra su argumento en builds de release, mientras que el assert de Zig es función y lo ejecuta siempre.
  • Quedan unas 13,000 apariciones de unsafe, alrededor del 4% del código Rust. El 78% de esos bloques ocupa una sola línea: un puntero que viene de C++ o una llamada a una librería en C.
  • Desde el merge, el equipo cerró 11 rondas de revisión de seguridad con Claude Code Security y montó fuzzing permanente de todos los parsers, con 100,000 millones de ejecuciones y unos 15 PRs de por medio.

La prueba de fuego llegó en producción. Claude Code corre sobre el Bun de Rust desde su versión 2.1.181, publicada el 17 de junio de 2026, con un arranque 10% más rápido en Linux (de 517 a 464 milisegundos, mediana). Y Prisma lanzó la beta pública de Prisma Compute encima de la reescritura.

"Nos topamos con fugas de memoria y con un pool de conexiones que no lograba recuperarse después de pausar y reanudar una máquina virtual. Cuando apareció la reescritura en Rust, la probamos contra los mismos modos de falla. Los manejó a la perfección."
Alexey Orlenko, citado en el blog de Bun sobre el lanzamiento de Prisma Compute

Los 165,000 dólares y el modelo que nadie más tenía

Antes de fusionar, el proceso quemó 5,900 millones de tokens de entrada sin caché, 690 millones de tokens de salida y 72,000 millones de lecturas cacheadas. Willison señala la trampa aritmética con una sola frase: una ventaja de trabajar en Anthropic es que uno no paga sus propios tokens, algo cómodo cuando el costo estimado son 165,000 dólares.

El otro asterisco es el modelo. Sumner usó una versión pre-release de Claude Fable 5, de clase Mythos. Anthropic lanzó Fable 5 y Mythos 5 al público el 9 de junio de 2026, casi un mes después de que la reescritura ya estuviera fusionada. Tres días más tarde, el gobierno de Estados Unidos aplicó controles de exportación y la compañía suspendió el acceso a ambos modelos para todos los usuarios; los controles se levantaron el 30 de junio y Fable 5 volvió el 1 de julio. Anthropic lo cobra a 10 dólares por millón de tokens de entrada y 50 por millón de salida.

Conviene separar dos fechas que el titular fusiona. Sumner fusionó la rama a main el 14 de mayo y él mismo aclara que en ese momento tenía confianza para comprometerse con la reescritura, no para publicarla. Bun v1.3.14 fue la última versión escrita en Zig, y la v1.4.0 todavía vive en canary, a la espera de que los reportes de usuarios saquen a la luz lo que 60,624 pruebas no vieron.

Preguntas rápidas sobre la reescritura de Bun en Rust

¿Ya puedo usar la versión de Bun escrita en Rust?

Sí, en el canal canary. Bun v1.4.0 es la primera versión escrita en Rust y se instala con el comando bun upgrade --canary; la v1.3.14 fue la última en Zig. Claude Code la usa en producción desde su versión 2.1.181, publicada el 17 de junio de 2026, con un arranque 10% más rápido en Linux.

¿Cuánto costó reescribir Bun de Zig a Rust?

Antes de fusionar, el proceso consumió 5,900 millones de tokens de entrada sin caché, 690 millones de salida y 72,000 millones de lecturas cacheadas: unos 165,000 dólares a precio de API, según Jarred Sumner. La cifra corresponde al precio de lista y no incluye el tiempo del ingeniero ni la infraestructura.

Lo que queda para cualquier equipo que lea esto con envidia no es el modelo ni el presupuesto: es la suite de pruebas. Sin un millón de aserciones escritas en un lenguaje ajeno a la implementación, ninguna revisión adversaria alcanza, porque nadie puede leer un PR de un millón de líneas. Bun tenía ese arnés armado desde años antes de que existiera Claude Code. Esa parte todavía se construye a mano.

Fuentes: 1, 2, 3

Dilis Salazar imagen de perfil
por Dilis Salazar

Leer más de Tecnología y Ciencia