Reescribir Bun en Rust
(bun.com)- Bun, que comenzó con Zig, creció hasta convertirse en un runtime con más de 22 millones de descargas al mes, pero los problemas recurrentes de estabilidad provocados por la combinación de un motor de JavaScript basado en GC y la gestión manual de memoria llevaron a iniciar la migración a Rust
- En lugar de dedicar un año de trabajo humano a trasladar 535,496 líneas de código Zig, ejecutaron en paralelo durante 11 días unas 50 workflows dinámicas de Claude Code y hasta 64 instancias de Claude
- El port se validó con
PORTING.md,LIFETIMES.tsv, 1 implementador y al menos 2 revisores adversariales, además del test suite existente en TypeScript, y logró pasar al 100% en CI sobre 6 plataformas - Tras el cambio a Rust, Bun v1.4.0 corrige 128 bugs reproducibles en v1.3.14, elimina todas las fugas de memoria instrumentables y reduce aproximadamente un 20% el tamaño de los binarios en Linux y Windows
- Bun v1.3.14 es la última versión en Zig y v1.4.0 es la primera versión en Rust, disponible en canary; el equipo usa borrow checker, Miri, LeakSanitizer y fuzzing con cobertura 24/7 como herramientas para mejorar la estabilidad
Bun comenzó con Zig y sus problemas de estabilidad
- Bun arrancó como un proyecto que hizo un port línea por línea del transpiler de JavaScript y TypeScript de esbuild, de Go a Zig
- El primer código en Zig se escribió el 16 de abril de 2021, y el control de bajo nivel de Zig junto con su diseño orientado al rendimiento hicieron posible la implementación inicial de Bun
- El Bun inicial fue escrito en Zig por una sola persona durante un año, y su alcance era muy amplio
- Transpiler, minifier y bundler de JavaScript, TypeScript y CSS
- Gestor de paquetes compatible con npm
- Test runner similar a Jest
- Resolución de módulos compatible con Node.js y TypeScript
- Clientes HTTP/1.1 y WebSocket
- Implementación de APIs de Node.js como
fs,netytls
- Actualmente, Bun CLI supera las 22 millones de descargas mensuales, y Claude Code y OpenCode lo usan como runtime, mientras que Vercel, Railway y DigitalOcean ofrecen soporte de 1st-party
Bugs repetidos de seguridad de memoria
- Entre los ejemplos de bugs corregidos en Bun v1.3.14 se incluyen use-after-free, double-free, fugas de memoria, accesos out-of-bounds y race conditions
node:zlib: heap-use-after-free al llamar.reset()durante una.write()asíncronanode:http2: un callback de JS reentrante provocaba un rehash del hashmap y dejaba inválidos punteros internos de streams, causando un use-after-free- En
UDPSocket.send()ysendMany(), callbacks devalueOf()otoString()podían hacer detach deArrayBuffer - En
Buffer#copyyBuffer#fill, coerciones de argumentos que hacían detach o resize deArrayBufferprovocaban crashes y lecturas out-of-bounds - Fugas de memoria relacionadas con
crypto.scrypt,tlsSocket.setSession()yfs.watch() - Double-free al procesar vendor prefixes y backgrounds multicapa en el parser de CSS
- Crashes por race condition de
MessageEventdurante acceso concurrente aBroadcastChanneloMessagePort
- Ya usaban varios mecanismos para reforzar la estabilidad
- Parchearon el compilador Zig para dar soporte a Address Sanitizer y ejecutaban el test suite con ASAN en cada commit
- En Windows distribuían builds ReleaseSafe de Zig con verificaciones de seguridad
- Hacían fuzzing 24/7 de la API runtime de Bun con Fuzzilli
- Operaban múltiples pruebas end-to-end de fugas de memoria
- No sostienen que Zig sea el problema en sí; la principal fuente de los problemas de estabilidad era la necesidad de manejar al mismo tiempo valores con GC y memoria gestionada manualmente
Por qué eligieron Rust
- JavaScript es un lenguaje con GC, y motores como JavaScriptCore y V8 tienen reglas estrictas para el manejo de excepciones y GC
- Zig, al igual que C, no administra memoria automáticamente; no tiene constructores ni destructores, y la limpieza normalmente debe declararse en cada call site con
defer - En Bun, manejar correctamente el lifetime de los valores con GC y de los valores gestionados manualmente era una fuente importante de problemas de estabilidad
- Había que verificar dónde se liberaban los bytes asignados
- Había que garantizar que solo se liberaran una vez
- Había que comprobar correctamente el manejo de excepciones de JavaScript
- Había que asegurar que los punteros de GC fueran visibles para el conservative stack scanner
- El enfoque de limpieza en Zig usa
deferyerrdeferexplícitos; C++ usa destructors y move, y Rust usaDrop - En el código Zig existente de Bun había una mezcla de lifetime de arena, reference counting y revisiones minuciosas
- Se pueden imponer reglas de ownership con guías de estilo y code review, pero en código seguro de Rust, use-after-free, double-free y free omitidos en rutas de error se convierten en errores de compilación
- Aproximadamente el 20% del código de Bun está en C++ e integra varias bibliotecas C/C++
- JavaScriptCore
- uWebSockets y usockets
- lshpack y lsquic
- BoringSSL
- SQLite
- C++ también podía haber sido una opción, pero seguiría dependiendo de guías de estilo y code review, y aun con ASAN pueden ocurrir corrupciones de memoria y fugas
Estrategia de reescritura: de una sola vez y de forma mecánica
- El código Zig existente de Bun tenía 535,496 líneas sin contar comentarios, y se estimó que una reescritura tradicional tomaría alrededor de un año a un pequeño equipo de ingeniería
- Como no podían detener durante un año las correcciones de bugs y seguridad ni el desarrollo de funciones, eligieron como enfoque de menor riesgo un port mecánico que minimizara los cambios en el comportamiento para los usuarios
- El test suite de Bun estaba escrito en TypeScript, por lo que no dependía del lenguaje de implementación del runtime
- Consideraron que una reescritura incremental sería dolorosa a corto y mediano plazo, porque obliga a crear código temporal con la esperanza de eliminarlo después, así que trasladaron todo de una sola vez
- Decidieron escribir el código Rust para que pareciera código Zig transpilado, y después de Bun v1.4 ir reduciendo gradualmente
unsafey refactorizando hacia un Rust más idiomático
Workflow dinámica de Claude Code
- Para la reescritura en Rust, en Claude Code se ejecutaron de forma continua durante 11 días unas 50 workflows dinámicas
- Las workflows abarcaron desde redactar la guía de porting hasta convertir archivos, corregir errores de compilación, restaurar subcommands, pasar todas las pruebas y hacer limpiezas a gran escala
- Crear una guía de porting que mapeaba patrones y tipos de Zig a patrones y tipos de Rust
- Hacer el port mecánico de todos los archivos
.ziga.rssiguiendoPORTING.mdyLIFETIMES.tsv - Corregir errores del compilador por crate
- Restaurar el funcionamiento de subcommands como
bun testybun build - Lograr que pasara el test suite completo
- Realizar refactorizaciones y cleanup a gran escala
- Durante la mayor parte del tiempo, una persona leía la salida de las workflows, verificaba problemas y bugs, y ajustaba los prompts para que Claude corrigiera el loop
- Como trabajo previo, dedicaron unas 3 horas a discutir con Claude cómo mapear los patrones del codebase Zig a Rust, y el resultado se serializó en
PORTING.md - Para añadir lifetimes de Rust al código con gestión manual de memoria, ejecutaron un workflow que analizaba los lifetimes de todos los fields de los structs
- Identificar fields con lifetimes complejos
- Proponer lifetimes
- Revisarlos con 2 agents de revisión adversarial
- Incorporar el feedback y guardarlo en
LIFETIMES.tsv
Método de revisión adversarial
- Cada implementación de Claude se emparejó con un Claude revisor adversarial en una ventana de contexto separada, y se le indicó al revisor que buscara bugs asumiendo que el código estaba mal teniendo solo el diff
- La estructura básica consistía en 1 implementador, 2 o más revisores adversariales y 1 fixer
- Todos los bugs que los revisores detectaron compilaban correctamente, pero tenían problemas de comportamiento
uv_closees asíncrono, peroBox<uv::Pipe>se destruía al final del match arm, lo que hacía que libuv conservara memoria ya liberada, causando un use-after-free y double-free- Error de
timespecdonde usartrunc()en un file time negativo no entero generabansecnegativos - Error donde
unwrap_orevaluaba sus argumentos de forma eager y provocaba un panic cuando se omitía el porcentaje encolor-mix()
- Igual que en una revisión humana, se separó el contexto del autor y del revisor para reducir el sesgo que puede surgir cuando el implementador quiere hacer merge
Ejecución del porting a gran escala y paralelización
- Antes de mover los 1,448 archivos
.zig, primero validaron el procedimiento con 3 archivos- 1 implementador escribía el archivo
.rs - 2 revisores verificaban que coincidiera con el comportamiento de
.zigy que siguieraPORTING.mdyLIFETIMES.tsv - 1 fixer aplicaba las sugerencias
- 1 implementador escribía el archivo
- Al inicio del port completo, varios Claude ejecutaban
git stash,git stash popygit reset HEAD --hard, interfiriendo entre sí - Después añadieron reglas al workflow que prohibían
git stash,git reset, comandos degitque no fueran commits de archivos específicos y comandos lentos comocargo - Al final usaron 4 workflow shards y 4 worktrees, y en cada shard 16 Claude hacían commit y push de archivos
- Gracias a la paralelización y a la preparación previa, en el pico Claude escribió unas 1,300 líneas de código por minuto
- La rama del port tuvo 6,502 commits excluyendo merges, la hora pico fue de 695 commits, y el diff final integrado fue de +1,009,272 líneas
- También hubo un problema porque no aumentaron los IOPS por defecto de la instancia EC2: un solo
greplento podía detener la lectura y escritura del disco durante varios minutos
Errores de compilación y separación en crates
- Después de escribir todo el código, el workflow de Claude corrigió los errores del compilador
- La base de código Zig era en la práctica una sola compilation unit, y el código Rust se quería dividir en unos 100 crates para compilar más rápido
- La categoría de errores más complicada fue la de las dependencias circulares
- El PR de separación en crates justo antes de la reescritura en Rust no fue suficiente
- Un workflow aparte clasificó y registró dónde debía colocarse el código con dependencias circulares
- Otro workflow realizó ese refactor
- Tras resolver las dependencias circulares, quedaron expuestos unos 16,000 errores del compilador
- Esos errores se procesaron en paralelo por crate
- En cada crate se ejecutaba
cargo check - La salida se agrupaba y guardaba por archivo
- Se corregían los errores de compilación de ese crate
- 2 revisores adversariales revisaban los cambios
- 1 fixer aplicaba las correcciones
- En cada crate se ejecutaba
- También hubo un falso arranque en el que Claude interpretó “hagamos que todos los crates compilen” como generar stubs de funciones
- Cuando empezó a aparecer el patrón de justificar workarounds con comentarios explicativos largos, se añadió una regla de revisión: “si hace falta un comentario del tamaño de un párrafo, el código está mal y hay que arreglar el código”
El camino hasta pasar las pruebas
- Después de que
cargo checkpasó, fueron resolviendo en orden los errores de linkeo, panic al iniciar,bun --versiony la ejecución debun test <file> - Usaron un workflow que guardaba en archivos los stack traces de fallas por subcomando del CLI y los corregía con un loop de implementador, revisor y fixer
- El workflow de archivos de prueba dividía unos 100 archivos de test aleatorios en 4 worktrees, guardaba los stack traces y errores de cada fallo, y luego los corregía
- La suite de pruebas incluía pruebas de memory leaks e integración que podían agotar el tiempo en debug build
- Una prueba que ejecutaba
next devy verificaba que hot module reloading detectara 100 cambios - Una stress test que agotaba el número máximo de TCP sockets
- Pruebas de lectura y escritura de disco a escala de gigabytes
- Una prueba que hacía spawn de unos 10 mil procesos
- Una prueba que ejecutaba
- Para aislarlas, limitaron el uso de memoria y CPU con
systemd-runy cgroups, y separaron el pid namespace - Aun así, la máquina se cayó varias veces por falta de espacio en disco
- Dos días después de la primera ejecución de CI, los archivos de prueba fallando bajaron de 972 a 23, y un día y medio después Linux quedó completamente en verde
- Al final pasó toda la suite de CI en 6 plataformas
- macOS x64
- macOS arm64
- Linux x64
- Linux arm64
- Windows x64
- Windows arm64
- Tras llegar al 100% de pruebas aprobadas, una persona verificó manualmente que las pruebas realmente se ejecutaran y no hubieran sido omitidas, y entonces hizo el merge
- El momento en que se hizo merge a
mainno fue un release versionado; todavía no había suficiente confianza para lanzarlo, pero sí la suficiente para comprometerse con la reescritura
Escala de pruebas y costo
- Durante 11 días, desde el 3 de mayo hasta el merge del 14 de mayo, se generaron 6,778 commits
- Las pruebas no se eliminaron ni se marcaron como skip
- La escala de pruebas por plataforma fue la siguiente
- Debian 13 x64:
expect()1,386,826 veces, 60,624 pruebas, 4,174 archivos - macOS 14 arm64:
expect()1,259,953 veces, 58,850 pruebas, 4,175 archivos - Windows 2019 x64:
expect()1,007,544 veces, 57,337 pruebas, 4,173 archivos
- Debian 13 x64:
- El trabajo previo al merge usó 5.9 mil millones de uncached input tokens, 690 millones de output tokens y 72 mil millones de cached input tokens read
- El costo según precios de API fue de aproximadamente 165 mil dólares
- Se estima que, si lo hubieran hecho personas directamente, 3 ingenieros con contexto de toda la base de código habrían tardado alrededor de 1 año
- El modelo usado fue la pre-release Claude Fable 5, y se incluyó una divulgación de que Bun fue adquirida por Anthropic en diciembre de 2025
Revisión de seguridad, fuzzing y estado de unsafe
- Después del merge del port a Rust, completaron 11 rondas de revisión de seguridad con Claude Code Security y atendieron los hallazgos
- Se añadió fuzzing continuo 24/7 basado en cobertura para todos los parsers de Bun
- JavaScript
- TypeScript
- JSX
- CSS
- JSON5
- JSONC
- TOML
- YAML
- Markdown
- INI
- scripts de Bun Shell
- rangos de semver
- archivos
.patch - colores CSS
- El fuzzer envía a Claude los bugs que encuentra para que presente PRs con reproducción y corrección, y luego una persona revisa esos PR
- Hasta ahora, la ejecución de parsers llegó a 100 mil millones de veces y derivó en unos 15 PR
- Al momento de escribir esto, alrededor del 4% del código Rust está dentro de bloques
unsafe- Unos 13,000
unsafekeywords - Unas 27,000 líneas / de un total de unas 780,000 líneas
- El 78% de los bloques
unsafeson de una sola línea y provienen de punteros heredados de C++ o llamadas a librerías C
- Unos 13,000
- Aclaran que, como siguen usando librerías C/C++ como JavaScriptCore, siempre habrá más
unsafeque en un proyecto de Rust puro
Regresiones encontradas tras el cambio a Rust
- La reescritura en Rust fue un cambio a gran escala y creó 19 regresiones conocidas; todas ya fueron corregidas
- La mayoría surgió de código cuya sintaxis es parecida en ambos lenguajes, pero cuyo significado es distinto
-
Side effects dentro de
debug_assert!- El
assertde Zig es una función, así que sus argumentos se ejecutan en todos los builds debug_assert!de Rust es una macro, así que en los builds de release se elimina la expresión completa- La llamada a
insert_staledesapareció en los builds de release y rompió cierto caso de HMR en proyectos de rutas HTML que usan React - Issue relacionado: #30678
- El
-
Slices de longitud impar
- El helper de Zig de Bun
reinterpretSlice(u16, bytes)usaba@divTrunc, por lo que ignoraba el byte impar sobrante al final bytemuck::cast_slicede Rust hace panic cuando la longitud es impar- Hubo una regresión donde
Blob.text()con un byte impar después del BOM UTF-16 hacía panic en el proceso en vez de devolver una cadena - La corrección volvió a ignorar el byte impar usando
&buf[..buf.len() & !1] - Issue relacionado: #31188
- El helper de Zig de Bun
-
Bounds checks
- El código Zig en macOS y Linux se compilaba con
ReleaseFast, por lo que se eliminaban los bounds checks, mientras que los builds release de Rust los conservan - El tamaño del bloque de overflow del module resolver de Bun quedó con el placeholder
64, lo que bajó el techo de 8.4 millones de nombres de archivo internados a 270,272 - El off-by-one portado de
ptrs[4095]pasó a ser alcanzable en proyectos reales, y Rust hizo panic en vez de escribir fuera de límites - Issue relacionado: #31503
- El código Zig en macOS y Linux se compilaba con
-
Cadenas de formato
comptime- En Zig,
Output.prettytienefmtcomocomptime, así que los marcadores de color<r>y<d>se convierten en escapes ANSI antes de sustituir los argumentos - La función de Rust no tenía un parámetro comptime, así que procesaba los marcadores en la cadena ya terminada y también reescribía mal los argumentos
- En
bun update -i, la terminación del hipervínculo OSC 8 y el marcador final<r>entraban en conflicto, por lo querse imprimía como texto - En Rust se necesitaba la macro
bun_core::pretty!("<r>{}<r>", hyperlink) - Issue relacionado: #30693
- En Zig,
Bugs corregidos y fugas de memoria
- Bun v1.4.0 corrige 128 bugs reproducibles en v1.3.14
- El alcance incluye fugas de memoria, crashes y hasta texto de ayuda coloreado incorrectamente
Dropen Rust llama automáticamente a la funcióndropcuando un valor sale de scope- En Zig, había que agregar
deferen cada call site, por lo que era fácil omitir limpieza o duplicarla Dropen Rust es una elección que acepta el hidden control flow a cambio de reducir footguns comunesDropcorrigió varias fugas de memoria relacionadas con rutas de archivo en el código de manejo de errores- Se mejoró la integración de Bun con LeakSanitizer para rastrear todas las asignaciones de memoria de código nativo
- Todas las fugas de memoria instrumentables fueron corregidas
-
Mejoras a la fuga de
Bun.build()- En Bun v1.3.14, en cada llamada in-process a
Bun.build(), el texto fuente parseado y la tabla de símbolos del AST sobrevivían más que la vida útil del build, filtrando varios MB cada vez - En una prueba que empaquetaba el mismo proyecto de 60 módulos 2,000 veces en un solo proceso, v1.3.14 seguía filtrando alrededor de 3 MB por build
- En Bun v1.4.0, el uso de memoria se estabiliza
- | Builds | Bun v1.3.14 | Bun v1.4.0 |
- | --- | ---: | ---: |
- | 500 | 1,914 MB | 526 MB |
- | 1,000 | 3,506 MB | 586 MB |
- | 1,500 | 5,097 MB | 608 MB |
- | 2,000 | 6,745 MB | 609 MB |
- En Bun v1.3.14, en cada llamada in-process a
Tamaño del binario, uso de stack y rendimiento
- El tamaño del binario se redujo solo con los cambios iniciales de la reescritura en Rust
- Windows: 3.8 MB menos
- macOS: 5.5 MB menos
- Linux: 6.8 MB menos
- La causa principal fue el uso excesivo de
comptimeen el código Zig - Después también se aplicaron identical code folding, eliminación de datos no usados de ICU y descompresión diferida de parte de libicu con un diccionario zstd
- Si se combinan la reescritura en Rust, los cambios en ICU e identical code folding, el tamaño del binario de Bun se reduce alrededor de 20% en Linux y Windows
| Version | Platform | Size |
|---|---|---|
| Bun v1.4.0 canary | Windows | 76 MB |
| Bun v1.3.14 | Windows | 94 MB |
| Bun v1.4.0 canary | Linux | 70 MB |
| Bun v1.3.14 | Linux | 88 MB |
- El parser de TOML y los parsers recursive-descent de Bun ahora usan menos espacio de stack
- El codegen de LLVM IR de Rust emite las intrinsics
llvm.lifetime.startyllvm.lifetime.endpara variables de stack, lo que permite a LLVM reutilizar stack slots - Antes, se refactorizaban manualmente funciones especialmente grandes en varias más pequeñas para esquivar un issue abierto de Zig
- Rust soporta link-time optimization entre C/C++ y Rust, lo que permite inlining entre lenguajes
-
Benchmark de Linux x64
- Se comparó Bun v1.3.14 con Bun v1.4.0 en un EC2 Xeon Platinum 8488C con Linux x64
- El throughput HTTP se midió con oha y la carga de trabajo de la app con hyperfine
- | server | Bun v1.3.14 | Bun v1.4.0 | Δ |
- | --- | ---: | ---: | ---: |
- | Bun.serve | 169.6k req/s | 177.7k req/s | +4.8% |
- | node:http | 103.8k req/s | 108.5k req/s | +4.5% |
- | Elysia | 158.9k req/s | 163.3k req/s | +2.8% |
- | express | 64.5k req/s | 66.6k req/s | +3.2% |
- | fastify | 91.5k req/s | 95.9k req/s | +4.8% |
- | workload | Bun v1.3.14 | Bun v1.4.0 | Δ |
- | --- | ---: | ---: | ---: |
- | next build | 13.62 s | 13.03 s | +4.5% |
- | vite build | 1.69 s | 1.65 s | +2.2% |
- |
tsc -b --force| 0.94 s | 0.89 s | +4.7% |
Casos de uso reales y estado de lanzamiento
- Prisma lanzó la beta pública de Prisma Compute sobre la reescritura de Bun en Rust
- Según Prisma, probaron en la reescritura en Rust un pool de conexiones que no se recuperaba después de pause/resume de la VM y un modo de fallo por memory leak, y afirmaron que ese modo de fallo se manejó bien
- Claude Code v2.1.181 y las versiones lanzadas después del 17 de junio usan Bun porteado a Rust
- El arranque de Claude Code en Linux fue 10% más rápido, y por lo demás la mayoría de los usuarios casi no notó cambios
- Bun v1.3.14 fue la última versión de Bun escrita en Zig
- Bun v1.4.0 es la primera versión de Bun escrita en Rust y se ofrece como canary
Herramientas que obtuvo el equipo y trabajo pendiente
- La nueva base de código en Rust mantiene una forma muy similar a la base de código existente en Zig
- Se escribió de modo que quienes entienden el código original en Zig también puedan entender el código en Rust traducido mecánicamente
- La revisión del PR de la reescritura en Rust se realizó verificando que un agente de revisión hostil detectara correctamente discrepancias entre Zig y Rust, la guía de porting y el cumplimiento de la guía de lifetimes, mientras que las personas leían mucho código lado a lado
- Bun v1.4 hace a Bun más rápido y pequeño, reduce el uso de memoria y proporciona herramientas para mejorar la estabilidad
- Rust borrow checker
- Miri
- LeakSanitizer
- fuzzing guiado por cobertura 24/7 para el parser
- Aún quedan partes por refactorizar, y se enlaza bun-unsafe-audit
- Un ingeniero monitoreó de cerca Fable y Claude Code, y se llegó en solo 11 días al punto en que toda la suite de pruebas pasaba en todas las plataformas
2 comentarios
Opiniones de Hacker News
Este artículo muestra bastante bien que hubo disciplina y cuidado, además de intervención humana, en la reescritura automática, y da la impresión de que se hizo con toda la seriedad posible usando IA.
Por separado, no entiendo muy bien por qué en 2026 alguien no querría usar un lenguaje con seguridad de memoria y, si es posible, también seguro frente a condiciones de carrera.
Rust ofrece eso con rendimiento incluido, así que aunque no te gusten la recolección de basura o la inmutabilidad por motivos de rendimiento, existe la opción de Rust.
Entiendo los casos en que se baja a C++ porque se necesita absolutamente el máximo rendimiento, o por preferencia personal, pero fuera de eso parece casi una elección obvia.
Además, en muchos problemas un recolector de basura elimina muchos defectos sin fricción adicional, incluidos los defectos mencionados en el artículo, mientras que Rust te exige pensar desde la perspectiva de la propiedad.
Por ejemplo, muchos desarrolladores de juegos prefieren iterar en un lenguaje de baja fricción. El código de juegos puede ser tan delicado como el código de UI frontend, el proceso de build también puede ser particular, y muchas veces se itera con hot reloading.
Decirles que usen un compilador lento implica aceptar más latencia en ese ciclo de iteración. Claro, no creo que estas razones apliquen a todo el mundo.
Rust no es perfecto, pero resuelve muchas de las trampas de C++. No solo el comportamiento indefinido, sino también cosas como la gestión de paquetes, los horribles archivos de CMake y los errores del linker.
Zig, Odin, C3 e incluso el viejo C al menos tienen ventajas que ni Rust ni C++ ofrecen. Aunque sea solo la velocidad de compilación.
Lo importante es que este enfoque es muchísimo más barato que contratar a un equipo de ingeniería de software. Incluso si me hubieran contratado por 200 mil dólares, no creo que hubiera podido hacer esto en un año.
No tenía contexto, no sabía Zig ni Rust, y aunque podría haberlos aprendido en un mes, habría sido muy lento.
Incluso dejando de lado predicciones tipo singularidad, con la IA actual será difícil justificar contratar a un ingeniero de software de 200 mil dólares.
En la capa más alta de empresas centradas en software como Google o Anthropic, seguirán contratando ingenieros excelentes para crear software nuevo que la IA todavía no hace bien, pero para empresas como Walmart o Target, donde el software es simplemente un centro de costos, o para compañías que ya externalizaban el desarrollo o usaban mano de obra H1B barata, aparece una alternativa de IA mucho mejor que el ingeniero promedio.
Es muy probable que los cerca de 1.6 millones de desarrolladores de software en EE. UU. se reduzcan mucho. Los de nivel L6 en FAANG estarán bien, pero el desarrollador promedio en un banco sin nombre o quien hace el sitio web de McDonalds tendrá que aprender otra cosa, o podría perder pronto su trabajo.
Hace un año no habría hecho esta predicción, pero ahora parece claro que este cambio va a ocurrir.
https://en.wikipedia.org/wiki/Jevons_paradox
También es discutible si en este trabajo realmente se creó valor. Cualquiera que haya usado unsafe Rust y también Zig sabrá que unsafe Rust es mucho más peligroso en comparación.
Tampoco reemplazó un empleo real y, al final, para lograrlo hubo que contratar a un ingeniero caro.
Creo que es más probable que genere más cambios de código que menos contrataciones.
Ahora probablemente habrá más ganas de contratar ingenieros senior que realmente puedan llevar a cabo cambios como este.
No solo el tiempo y el costo invertidos fueron menores de lo que mucha gente esperaba, sino que ahora se puede anticipar que pronto estas interacciones humanas, errores, tiempo y costos se reduzcan 2x, 5x e incluso 10x o más.
También hay que tener en cuenta que el propio post del blog funciona como material promocional de Claude y de los LLM en general.
Incluso sin debatir el proyecto Bun en sí ni la naturaleza de la reescritura, si una reescritura ingenua para salir de Zig por sí sola arregló fugas de memoria, mejoró la estabilidad, redujo el tamaño del binario en un 20% y aumentó el rendimiento en un 5%, eso no puede verse bien para Zig.
Si hubiera corrido 165 mil dólares de Fable sobre la versión en Zig, es muy probable que también hubiera obtenido una mejora de rendimiento del 5%.
Desde hace bastante tiempo se viene diciendo que Rust y los LLM son una buena combinación. Muchas fricciones del lenguaje se suavizan con la programación asistida por LLM.
No me molesta en sí que hayan usado IA para reescribir Bun en Rust. Aunque la 1.4 no sea lo suficientemente buena, es muy probable que mejore con el tiempo.
Lo que me empujó de vuelta a Node fue que el proceso de transición se manejó de una forma demasiado amateur.
No hay soporte LTS para la versión en Zig por CVE y cosas similares; dejaron en la versión en Zig bugs grandes como la fuga de memoria de 3 MB mencionada en el blog, obligando en la práctica a pasar a la versión en Rust para arreglar apps de producción; y no hubo ningún involucramiento con la comunidad de Bun en una decisión tan importante.
Un día era “no hagan drama, solo estoy jugando con esto”, y unos días después se convirtió en “YOLO, merge a main”.
Jarred en la práctica sigue operando como un hacker solitario que trabaja en su proyecto personal.
Por ejemplo, como el caso del problema de memoria del blog. Desde el punto de vista de seguridad, es mejor pensar que la versión en Zig no existe. Úsenla bajo su propia responsabilidad.
Sobre que Jarred lo maneje como un proyecto personal, bueno, tienen derecho a hacerlo. Sobre todo si es propiedad de una empresa, es algo esperable.
No veo nada que pueda considerarse descuidado.
De hecho, él puso dos instancias de Claude como revisores adversariales para cada cambio de código, para cada línea. Salvo quizá la gente que escribió el software del transbordador espacial, no conozco ningún equipo humano que haga dos revisiones independientes de cada línea.
También corrigieron la fuga de memoria. ¿Por qué importa en qué lenguaje está escrito? Al final, la gente ejecuta código TypeScript y demás con Bun.
¿Cuánto le importa a un usuario de Bun que Bun esté escrito en Zig? A mí, nada. Uso Bun desde hace 2 años y a Zig apenas lo busqué una vez. Simplemente no es relevante.
¿Se volvió más estable? Sí. ¿Se volvió más chico? Sí. ¿Se volvió más rápido? Sí. ¿Hay evidencia de que se volvió menos seguro? No. El código estuvo público durante dos meses, y a estas alturas los detractores ya deberían haber encontrado una prueba decisiva, pero no la encontraron.
¿Cuánta más evidencia se necesita para aceptar que esto fue un gran éxito?
Esta es la nueva realidad. Los agentes ya son lo bastante buenos como para entrar en el terreno donde proyectos así son posibles, y eso es interesante.
“Esta reescritura en Rust habría sido un trabajo de 1 año para un equipo de ingenieros con todo el contexto del codebase. Con Fable y 1 ingeniero monitoreando de cerca Claude Code, tomó 11 días desde el inicio hasta que el 100% de la suite de tests pasara en todas las plataformas”
Técnicamente es impresionante, pero pasa por alto el hecho de que, si Bun no fuera parte de Anthropic, habría costado 165 mil dólares en tokens
La comparación no es del todo justa. Si tomas el costo adicional como 0 dólares, significa que a un equipo pequeño le tomaría 1 año hacer el port
Me gustaría comparar gastar 165 mil dólares en Claude durante 11 días contra repartir ese dinero entre 50 personas para que reescriban el código Zig línea por línea. Creo que Claude sería más rápido y por eso más barato, pero quizá la diferencia no sea enorme
Son 1,200 dólares por día
50 personas * 11 días son 660 mil dólares, 4 veces el costo de Claude
Y además eso supone que esas 50 personas trabajan sin trabarse, sin pisarse entre sí y sin problemas de coordinación. Solo la complejidad de coordinar ya sería enorme
No me encanta, pero aquí Claude gana fácil. Ni siquiera es una diferencia pequeña
Aunque no sea la respuesta correcta hoy, ¿no puede verse al menos como una señal de un futuro posible?
Pista: esas 50 personas tienen que coordinarse
Aproximadamente, los costos serían DeepSeek v4 Pro & Mimo v2.5 Pro 3,426 dólares, Tencent HY3 3,892 dólares, GLM 5.2 30,016 dólares, Qwen 3.7 Max 37,925 dólares, Claude Opus 4.8 & GPT 5.5 xhigh 82,750 dólares
Esto se basa en 5.9 mil millones de tokens de entrada no cacheados, 690 millones de tokens de salida y 72 mil millones de lecturas de tokens de entrada cacheados
Este es el poder de una suite de tests sólida. Los LLM destacan cuando hay una recompensa verificable
Creo que veremos muchos más proyectos reescritos en Rust. Rust también es un objetivo ideal para este tipo de reescrituras porque ofrece mucha verificación mediante su sistema de tipos y tiene bajo overhead sin garbage collection
En la era de la codificación con agentes, cada vez hay menos razones para usar lenguajes con garbage collection
Veo a Rust como un óptimo local para la codificación con LLM. En el futuro podrían aparecer lenguajes mejores, pero creo que Rust dominará durante bastante tiempo
Una vez migré un proyecto personal de más de 300 mil líneas de Python a TypeScript, y la razón por la que no pude usar Rust fue claramente el tiempo de iteración
Me pregunto de qué manera usa Anthropic a Bun. Sé que se usa como “runtime” de Claude Code, pero si iban a mover 1 millón de líneas de Zig a Rust, ¿no podrían portar Claude Code a Rust y dejar de empaquetar por completo un runtime de JS?
¿Anthropic usa Bun para algo más? Podría ser para cosas como llamadas a herramientas que ejecutan JS dentro de las respuestas de Claude
Por qué no simplemente migraron Claude Code
Si tuviera que especular, quizá su suite de tests no sea tan robusta
Esto podría animarlos a hacerlo
La idea de que “históricamente, las reescrituras son una idea terrible” cambió para mí en los últimos 5 años
El primer caso fue cuando me uní a una empresa que tenía un producto de software que casi no funcionaba. Hicimos refactorización incremental y reescrituras tradicionales, pero al final aprendimos que el núcleo estaba tan podrido que lo mejor era reescribir desde primeros principios
La lección que saqué es que la sabiduría convencional probablemente solo aplica al caso de reescribir sistemas complejos pero funcionales
Después, en la era de la codificación con agentes, pasé por varias situaciones. Entre mi trabajo principal y proyectos hobby, reprodujimos grandes partes de software complejo como Salesforce, Gmail y Pioneer Rekordbox con equipos muy pequeños
Igual que en el post del blog, la clave es construir un excelente bucle de verificación alrededor del comportamiento central, con compilador, linter, arnés de pruebas y suite de tests
Cada vez más, el verdadero trabajo se siente como diseñar e implementar arneses de pruebas integrales. Una vez que tienes eso, solo dejas que el LLM cocine
En ese caso, cosas como el arnés de pruebas, los linters y los workflows serían nuestros perros pastores
Lo que me pareció interesante es que la mayor parte de la discusión en torno a este tema se dio bajo el supuesto de que la reescritura no se hizo con Fable, sino con modelos como Opus.
Ese supuesto se usó, al menos en parte, como argumento para decir que la reescritura no era posible o que no era una buena idea.
El modelo en sí no cambia por completo la narrativa, pero personalmente sentí que debería ser más cuidadoso al estimar las capacidades de los modelos que usan internamente Anthropic y las organizaciones relacionadas.
Lo que no dijo fue que estaba usando Fable antes de su lanzamiento. [1]
La próxima señal podría ser que una empresa propiedad de Anthropic desactive el tráiler Claude Co-Authored-By. [2] Si es un año de IPO, deberían aprovechar cualquier oportunidad para promocionar Claude, y la única excepción en ese mayo sería si se trataba de algo como Fable que no podían revelar.
[1]: https://xcancel.com/jarredsumner/status/2060050586024743376#...
[2]: https://github.com/oven-sh/bun/commit/23427dbc12fdcff30c23a9...
Cada vez que reescribí un proyecto grande, lo hice más pequeño y más rápido, y corregí todos los bugs grandes y la mayoría de los pequeños.
El equipo actual tuvo una experiencia similar. Me pregunto qué impacto habría tenido en la calidad una reescritura de Zig a Zig de la misma escala.
Opiniones en Lobste.rs
extern "C", pero seguirían dependiendo de una guía de estilo impuesta mediante revisión de código y, aun con ASAN, seguirían apareciendo corrupciones y fugas de memoria.Curiosamente, Node.js funciona bien incluso en C++, pero nunca he visto a Bun como un proyecto serio. Ahora parece un banco de pruebas del departamento de marketing de Anthropic, así que pienso seguir manteniéndome lejos.
Spoiler: en la práctica, no son tan cuidadosos y sí cometen errores.
unsafe, y el 78% de ellos son de una sola línea” parece una forma de tranquilizar, pero no importa si un bloqueunsafetiene una sola línea o no. Si dentro de él se rompen las garantías de seguridad, todo el código fuera del bloque también puede quedar potencialmente sin soundness.La integración inicial del port a Rust de Bun contenía unsoundness evidente de este tipo: https://github.com/oven-sh/bun/issues/30719
Ese issue se abordó cuando los mantenedores activaron en CI la herramienta Miri de Rust, y en la sección “What's Next” del artículo también aparece Miri (which runs for a growing chunk of code in CI), así que parece bien que estén trabajando en eso.
Siendo justos, incluso Rust con violaciones de seguridad puede ser más fácil de mantener dependiendo de la calidad del código Zig al que reemplazó. Aun así, las líneas de código por bloque unsafe no son un indicador de calidad, sobre todo si esos bloques no tenían otras prácticas de codificación, experiencia ni verificaciones automáticas.
unsafeno surgieron del proceso de porting, sino de los requisitos del proyecto. Si llamas a una biblioteca en C, necesitas bloquesunsafe, y no hay forma de eliminarlos solo con refactorización.Claro, sería posible si también reescribieran esa biblioteca en C, pero eso quizá puedan evaluarlo más adelante.
En el anuncio “Bun is joining Anthropic”, Jarred dijo que contratarían más ingenieros para trabajar en Bun, pero viendo solo GitHub, el equipo de Bun más bien parece haberse reducido. No sé bien qué conclusión sacar de eso, salvo que “Bun es un equipo pequeño”.
El método en sí es interesante, pero el artículo se lee como una pieza de marketing. También falta análisis sobre cuánto costó, no se mencionan los riesgos de una reescritura en Rust y, para empezar, tampoco hay una explicación concreta de por qué ocurrió la reescritura. Si tuviera que especular, podría ser por la política de no IA de Zig, o porque dentro de Anthropic había una política de enfocarse en Rust.
Y creo que el artículo cuenta una historia bastante coherente sobre por qué lo reescribieron. Bun tomó medidas para detectar problemas, pero seguía crasheando, y los desarrolladores querían una forma más sistemática de evitar estos problemas.
El plan inicial era imponer con más rigor cierto estilo de codificación e introducir smart pointers, pero Jared consideró que sus smart pointers propios tenían peor usabilidad que Rust y no ofrecían garantías. Entonces pasó a ser “¿qué tal si probamos durante una semana si el nuevo modelo de Anthropic puede reescribir Bun en Rust?”, y cuando la tasa de aprobación de la suite de pruebas subió, el flujo parece haber pasado de “vale la pena intentarlo” a “lo vamos a integrar”.
Es decir, no parece que hayan decidido desde el inicio reescribir en Rust, sino algo más cercano a: “Rust parece dar una solución al problema, pero no podemos hacerlo por el costo de reescritura. Pero al probar un port con LLM, parece posible. Entonces vayamos con una reescritura mediante LLM”.
Felicitaciones a Jarred, al equipo de Bun y a Anthropic por haberlo logrado.