Lecciones del puerto Zig a Rust impulsado por IA de Bun: La verificación es el verdadero trabajo
Qué nos enseña el movimiento asistido por el agente de Bun, de Zig a Rust, sobre los conjuntos de pruebas como contratos, las guías de portabilidad, el código inseguro, y por qué la verificación limita ahora la programación con IA.
Fuentes: Bun Rewrote Its Runtime in Rust With AI. The Real Story Is Bigger Than the Code
La mayoría de los equipos de ingeniería evitan, en la medida de lo posible, reescribir el lenguaje de un sistema de producción a gran escala. Se informa que Bun, el entorno de ejecución y conjunto de herramientas para JavaScript y TypeScript, trasladó su implementación de Zig a Rust, utilizando agentes de programación por IA para realizar la mayor parte de la traducción, y las cifras son impresionantes. Sin embargo, lo más relevante no es cuánto código generaron los agentes, sino cómo el proceso asociado decidió qué parte de ese código era fiable. Este artículo explica dicho proceso y lo convierte en prácticas que puedes aplicar a tus propios procesos de refactorización y migración asistidos por IA.
Por qué normalmente se evitan las reescrituras
Reescribir un lenguaje conlleva una lista conocida de riesgos. Puede introducir regresiones sutiles que nadie nota durante meses, alterar el rendimiento en direcciones inesperadas, dañar una plataforma que solo utiliza un pequeño número de usuarios y absorber una gran parte de la capacidad del equipo. En el peor de los casos, el equipo acaba manteniendo la implementación antigua y la nueva lado a lado, pagando el doble por cada corrección de errores.
Por eso la migración a Bun llama la atención. En el momento de redactar este texto, el repositorio del proyecto describe a Bun como un entorno de ejecución de JavaScript basado en Rust y construido sobre JavaScriptCore, y Anthropic anunció que adquiriría Bun en diciembre de 2025. Los informes públicos indican que la portación consta aproximadamente en 535,000 líneas de código en Zig traducidas en unos 11 días, con agentes encargándose de gran parte del trabajo de implementación dentro de un proceso estrictamente definido.
Puede resultar tentador interpretar esto como otro titular del tipo “la IA escribió una montaña de código”. La pregunta más relevante es otra: ¿puede un sistema de IA realizar cambios significativos cuando los humanos crean mecanismos sólidos para verificar si el resultado es correcto? Esa pregunta es mucho más importante para la forma en que se desarrollará el software en los próximos años.
Una adaptación, no un rediseño
Bun se escribió originalmente en gran medida en Zig, un lenguaje de sistemas valorado por su control a nivel bajo y su rendimiento predecible. La migración no se concibió como un inicio desde cero; su objetivo era deliberadamente limitado: mantener la arquitectura y el comportamiento observable existentes, y cambiar únicamente el lenguaje en el que está escrita la implementación.
Esa forma de enmarcar el problema es la decisión de diseño clave. Una reescritura convencional invita a los ingenieros a rediseñar mientras avanzan, cambiando estructuras de datos, reorganizando módulos, añadiendo abstracciones y ajustando APIs. Cada una de esas opciones representa una nueva fuente de riesgo y algo nuevo que revisar. Al descartar la mayoría de ellas, el equipo de Bun convirtió una tarea creativa abierta en una mecánica con un punto de referencia claro: la implementación actual y las pruebas que la describen.
Cuentar líneas de código parece ser la parte difícil, pero no lo es. Generar código Rust sintácticamente válido es relativamente sencillo. Garantizar que el Rust se comporte exactamente como el Zig que reemplaza es donde radica toda la dificultad.
El conjunto de pruebas como contrato de comportamiento
Imagínese a un agente al que solo le dicen “reescriba esta base de código en Rust”. La instrucción deja abierta toda decisión importante: ¿qué partes pueden cambiar y cuáles deben permanecer idénticas? ¿Qué atajos son aceptables? ¿Cuáles comportamientos de memoria son cruciales? Y, sobre todo, ¿cómo sabría alguien si el resultado es correcto?
Bun ya tenía la respuesta a la última pregunta: un conjunto de pruebas amplio y maduro. Una vez que las pruebas se consideran como un contrato, el agente ya no inventa una implementación; simplemente reproduce un comportamiento que ya está definido de forma ejecutable, y cada archivo que genera cumple con ese contrato o no lo hace.
Qué pueden y qué no pueden prometer las pruebas
Un conjunto de pruebas no es una prueba de corrección. Solo verifica los comportamientos que alguien pensó en codificar, y un archivo traducido puede superar todas las pruebas sin dejar de diferir en una ruta que nadie utilizó. Lo que sí ofrecen las pruebas es un bucle de retroalimentación mucho más sólido que pedirle a un modelo que juzgue su propia salida. Sin ellas, una reescritura generada puede parecer completamente plausible mientras cambia silenciosamente la semántica. Con ellas, cada cambio generado debe ganarse su lugar.
El flujo de trabajo Bun reportado incluía varias etapas superpuestas: compilación, el conjunto de pruebas, verificaciones en las plataformas soportadas, revisión de código y validación adicional. El modelo generaba candidatos; el proceso de ingeniería decidía cuáles sobrevivían. Esa división del trabajo es la idea central a retener.
Por qué la guía de portabilidad era tan importante como el modelo
Antes de que los agentes accedieran a miles de archivos, se dice que el proyecto elaboró una guía detallada de portabilidad que describía cómo debían expresarse las construcciones de Zig en Rust. Incluía mapeos de tipos, convenciones de nomenclatura, patrones de traducción recurrentes, cómo manejar las dependencias y límites explícitos sobre lo que los agentes podían hacer.
El efecto es reemplazar una solicitud vaga (“escribir este archivo en Rust”) por una más específica: convertir el archivo utilizando los mapeos acordados, mantener su comportamiento observable idéntico y no alterar la arquitectura. Los agentes rinden mucho mejor en ese tipo de entorno porque el conjunto de respuestas aceptables es pequeño y bien definido.
Una guía también facilita la revisión. En lugar de la pregunta vaga “¿se ve bien?”, un revisor puede hacer dos preguntas concretas:
- ¿Este código en Rust se comporta de la misma manera que el Zig que reemplaza?
- ¿El agente siguió las reglas de traducción acordadas por el proyecto?
Ambas preguntas tienen respuestas que se pueden verificar, lo cual es exactamente lo que necesita un proceso de revisión cuando el volumen de cambios es enorme.
Qué contiene una guía útil para la portabilidad
Si planeas una migración similar, incluso a una escala mucho menor, una guía de este tipo suele detallar:
- cómo cada tipo de fuente, contenedor y mecanismo de errores se mapea al lenguaje destino
- reglas de nombramiento, para que el código generado sea consistente en todos los archivos y agentes
- qué dependencias se pueden introducir y cuáles están prohibidas
- patrones que el agente no debe usar, incluso si permitirían compilar un archivo
- qué se considera “terminado” para un archivo o módulo individual
La lección más amplia es aplicable mucho más allá de Bun: el sistema de instrucciones alrededor de un agente puede influir en la calidad del resultado casi tanto como la elección del modelo. Los equipos que ya mantienen archivos de instrucciones para agentes reconocerán este patrón; nuestra guía para escribir un archivo CLAUDE.md efectivo aborda el mismo principio para el trabajo diario.
Rust fiel no siempre es Rust idiomático
Cualquiera que espere que el código trasladado se lea como un diseño en Rust creado desde cero quedará decepcionado, y eso es intencional. Una adaptación fiel debe parecerse al código del cual fue trasladada.
Esto genera un compromiso visible. Zig puede expresar ciertas operaciones de bajo nivel de manera concisa, mientras que Rust tiende a ser explícito respecto a los tipos, los períodos de vida, la propiedad y los límites del código inseguro. Por lo tanto, una función compacta en Zig puede convertirse en una versión de Rust notablemente más larga. Esta longitud adicional es consecuencia de la optimización hacia la equivalencia de comportamiento en lugar de la elegancia, y no evidencia de que el resultado esté defectuoso.
Es útil distinguir entre dos afirmaciones:
- "Esto no es el Rust que yo habría escrito desde cero."
- "Este Rust es incorrecto."
La primera es un juicio de estilo; la segunda, un defecto. Tratarlas como lo mismo conduce a críticas infundadas hacia las adaptaciones mecánicas.
Primero la adaptación, luego la limpieza
El enfoque descrito trató la traducción mecánica y el posterior perfeccionamiento como fases separadas. Primero hacer que Rust se comporte como Bun; luego mejorar Rust. Dividir el trabajo de esta manera significa que el equipo nunca tiene que lidiar con dos cambios grandes e interactivos al mismo tiempo. Si aparece una regresión durante la limpieza, se puede atribuir a dicha limpieza, ya que la versión base ya había sido validada con las mismas pruebas.
Leer correctamente los bloques unsafe
Los observadores que examinaron la adaptación notaron una gran cantidad de bloques unsafe y lo consideraron un signo de una calidad deficiente en Rust. La reacción es comprensible, pero ignora cómo llegó el código a ese estado.
Rust exige que las operaciones potencialmente peligrosas, como la referencia a punteros sin formato o la llamada a funciones externas, se marquen explícitamente con unsafe. Zig no cuenta con un marcador equivalente. El código original de Bun está repleto de manipulaciones de memoria a bajo nivel, operaciones con punteros y llamadas a componentes en C y C++, por lo que una traducción fiel hace que gran parte de ello aparezca naturalmente como unsafe en Rust. Además, Bun integra JavaScriptCore y se comunica con otras bibliotecas nativas, y la frontera entre el código en Rust y C o C++ requiere operaciones inseguras por su propia naturaleza.
unsafe dice poco sobre la calidad del código. Preguntas más informativas son:
- ¿Por qué esta operación en particular necesita ser insegura?
- ¿Es la región insegura lo suficientemente pequeña como para poder analizarla?
Estas preguntas también ofrecen una hoja de ruta natural para la fase de limpieza: reducir las regiones inseguras, envolverlas en abstracciones seguras y documentar las invariantes en las que cada una se basa.
Cuando la traducción revela mejoras
Una adaptación literal puede seguir mostrando oportunidades que el diseño original ocultó. Un ejemplo reportado involucraba una ruta de código en la que la implementación en Zig accedía a C++ para obtener cierta información. En Rust, los datos locales al hilo relevantes podían accederse de manera más directa, evitando esa llamada intermedia.
La conclusión no es que Rust supere a Zig en velocidad; esa generalización no resistiría un análisis detallado. El punto es más específico y útil: trasladar código entre lenguajes obliga a alguien, o a algo, a examinar atentamente cada ruta de llamada, y ese escrutinio a veces revela trabajos que nunca fueron necesarios. Tales hallazgos merecen ser evaluados caso por caso, y no incorporarse a un debate sobre lenguajes.
Dónde se ha desplazado el esfuerzo de ingeniería
La migración no eliminó el trabajo de ingeniería; lo reubicó. En el desarrollo tradicional, una gran parte del tiempo humano se dedica a escribir el código de implementación. En el desarrollo asistido por agentes, más de ese tiempo se destina a:
- definir restricciones y reglas de traducción
- diseñar el flujo de trabajo en el que operan los agentes
- escribir y mejorar pruebas
- revisar los cambios generados
- diagnosticar fallos
El ingeniero pasa a ser responsable del sistema que genera y verifica el código, en lugar de de cada línea del propio código. En un proyecto grande, el ciclo se desarrolla más o menos así: especificación, luego generación, luego compilación, luego pruebas, luego revisión, luego corrección, y así sucesivamente. El modelo participa en varias de esas etapas, pero el diseño del ciclo determina si el resultado tiene algún valor. Ese flujo de trabajo, y no la cantidad de líneas, es la verdadera innovación.
Por qué Bun fue un buen candidato
No todos los repositorios de código se prestan a este enfoque, y Bun tenía varias ventajas:
- una implementación funcional que sirve como referencia
- una arquitectura conocida y estable
- comportamiento observable a través de un conjunto de pruebas existente
- un idioma de destino claramente elegido
- una base de código que pueda dividirse en archivos y componentes bastante independientes
- un objetivo único y medible: mantener el programa, cambiar el idioma
Compare eso con pedirle a un agente que diseñe un producto nuevo, donde no existe una implementación de referencia y lo que se considera “correcto” es cuestión de opinión. La regla general es que cuanto más precisamente se pueda especificar el comportamiento deseado, más de la implementación podrá automatizarse. Eso no significa que los agentes puedan asumir cualquier proyecto grande con seguridad; significa que la automatización funciona mejor cuando el destino puede medirse.
Lo que la migración no demuestra
Es fácil exagerar un resultado como este, por lo que vale la pena ser explícito sobre sus limitaciones. El puerto Bun no demuestra que la IA pueda reemplazar a los ingenieros de software. Tampoco muestra que todo código base pueda ser traducido de esta manera, que el código generado sea automáticamente mantenible o que Rust siempre sea más rápido que Zig. Y ciertamente no justifica la fusión de enormes solicitudes de integración generadas por IA en el código del producto habitual.
Si acaso, muestra lo contrario. El trabajo dependió de pruebas rigurosas, reglas explícitas, validación, revisión, depuración y supervisión humana continua. El análisis del flujo de trabajo publicado también señala que los agentes no se dejaron funcionar sin supervisión; las personas monitoreaban el proceso y lo ajustaban a medida que surgían problemas. El juicio técnico no perdió importancia; al contrario, se convirtió en la base sobre la cual descansaba todo lo demás.
La verificación se está convirtiendo en el cuello de botella
Durante décadas, escribir código se ha considerado el paso lento. Supongamos que un agente puede generar 10,000 líneas en el tiempo que antes un desarrollador necesitaba para 500. La reacción obvia es celebrar esta mayor velocidad, hasta que surge la siguiente pregunta: ¿quién va a revisar esas 10,000 líneas?
Generar código ahora es barato. Asegurarse de que sea correcto sigue siendo costoso, y el costo aumenta significativamente en los casos de entornos de ejecución, compiladores, bases de datos, sistemas operativos y código sensible a la seguridad, donde un pequeño error puede causar un gran fracaso. Esto indica que cobra cada vez más importancia todo aquello que verifica en lugar de generar: conjuntos de pruebas, capacidad de observabilidad, análisis estático, pruebas de fuzzing, métodos formales, entornos aislados y revisiones por parte de expertos en ataques. Los equipos que más se benefician de los agentes de programación podrían ser aquellos con los sistemas de verificación más sólidos, y no necesariamente los que tienen acceso al mejor modelo.
Prácticas que puedes adoptar para tus propios cambios asistidos por IA
No necesitas un proyecto del tamaño necesario para ejecutarlo a fin de aplicar estas lecciones. Funcionan igualmente bien al migrar un servicio entre frameworks o al convertir una base de código en JavaScript a TypeScript.
Implementa pruebas antes del cambio importante
Un conjunto de pruebas exhaustivo constituye una especificación ejecutable. Cuanto más comportamiento puedas verificar automáticamente, más trabajo de implementación podrás transferir con seguridad. Si la cobertura es escasa, reforzarla es el primer hito de la migración, no algo que se considere después.
Escribe las reglas por escrito
Evita instrucciones como “reescribe esto”. Especifica la arquitectura, la nomenclatura, las dependencias permitidas y prohibidas, los atajos que están fuera de límites, las expectativas de manejo de errores y los criterios de aceptación para cada unidad de trabajo.
Mantén separadas las fases de generación y revisión
Un agente nunca debe ser el único juez de su propio resultado. Un paso de revisión independiente, ya sea realizado por un humano, por otro agente con instrucciones diferentes o por ambos, aporta una perspectiva nueva y detecta problemas que el paso de generación pasa por alto.
Piloto en una pequeña muestra representativa
Ejecute el proceso primero con un puñado de archivos típicos. Las suposiciones erróneas son fáciles de corregir cuando afectan a diez archivos, pero resultan muy costosas si se han replicado en diez mil.
Trate el código generado como no fiable hasta que se demuestre lo contrario
El código que se compila no es necesariamente el código que funciona. La compilación es solo una de las etapas, no prueba de su corrección.
Haga que las personas rindan cuentas por las decisiones importantes
Los agentes pueden explorar rápidamente un enorme abanico de implementaciones posibles. Decidir qué debe hacer el sistema y qué riesgos son aceptables sigue siendo responsabilidad humana.
Más allá del debate Zig contra Rust
Es tentador presentar la situación como Zig frente a Rust, código escrito a mano contra inteligencia artificial o humanos contra agentes. Ninguna de estas perspectivas explica mucho. El puerto Bun debe entenderse mejor como un experimento sobre cómo se construye el software cuando producir código cuesta casi nada. Una vez que un sistema puede traducir cientos de miles de líneas, la pregunta central deja de ser “¿podemos implementar esto?” y pasa a ser “¿podemos crear un proceso fiable que nos indique si la implementación es correcta?”. Se trata de problemas de ingeniería muy diferentes.
Cómo deberse juzgar el resultado
La traducción inicial es solo la primera fase. Una base de código adaptada mecánicamente puede volverse más idiomática con el tiempo: se pueden auditar y reducir las regiones inseguras, mejorar las abstracciones, ajustar el rendimiento y eliminar las capas de compatibilidad. Al igual que cualquier sistema en producción, al final se juzgará por si funciona para los usuarios reales. El material de referencia indica que Bun continuó lanzando versiones después de la migración; consulte el repositorio y el registro de cambios del proyecto para conocer su estado actual.
La evaluación real provendrá de indicadores a largo plazo y no del tamaño de la reescritura:
- si el tiempo de ejecución se mantiene estable a lo largo de las versiones
- si el código se vuelve más fácil de mantener
- si el rendimiento sigue siendo competitivo
- si los errores relacionados con la memoria se vuelven menos frecuentes
- si los colaboradores pueden comprender y mejorar el nuevo código
Puntos clave
- El valor de la migración Bun radica en el ciclo de reglas, pruebas, revisión y corrección, no en el volumen de código generado.
- Enmarcar una reescritura como un puerto que preserva el comportamiento proporciona a los agentes un objetivo medible y a los revisores una lista de verificación concreta.
- Una guía de porteo reduce el rango de resultados aceptables y hace posible una revisión a gran escala.
- El uso excesivo de código Rust poco seguro en un puerto fiel suele ser el costo inevitable de la equivalencia; se debe juzgar al código inseguro por sus límites, no por su cantidad.
- A medida que la generación se vuelve más económica, la inversión en verificación será lo que diferencie a los equipos, y diseñar ese ciclo de retroalimentación se está convirtiendo cada vez más en el núcleo del trabajo de ingeniería.
Lecturas relacionadas
- Islas Resumibles en Bun: Lecciones de Diseño del Marco Stoneware — Cómo un marco orientado al servidor trata el HTML como valor por defecto, limita el uso de JavaScript a islas resumibles y gestiona la exportación estática, los errores, el CSP y el despliegue.
- De Objetivo a Acción Protegida: Bucles, Verificación y Permisos en Agentes de IA — Aprenda cómo los agentes de IA convierten un objetivo en llamadas a herramientas a través de un bucle de planificar, actuar y observar, y por qué la verificación, los niveles de autonomía y las capas de permisos determinan si son seguros.