La reescritura que nadie se animaba: lo que la hizo posible no fue el modelo

Render editorial de una balanza de precisión con dos engranajes casi idénticos en cada platillo, uno de metal viejo y oxidado y otro nuevo y pulido, perfectamente equilibrados
0 0 votos
Valora la Publicación

Resumen de la publicación

Bun pasó sus 535.496 líneas de Zig a Rust con agentes de IA: la traducción llevó 11 días y la primera versión en Rust salió en agosto, contra un año estimado a mano. Google reescribió giflib de C a Rust con Gemini y validó que se comportara igual con fuzzing diferencial y 30 millones de GIF reales.

Mi lectura es que lo que hizo posibles esas dos reescrituras no fue el modelo, fue tener un oráculo: una forma automática y confiable de saber si el código nuevo hace lo mismo que el viejo. Si tu sistema legacy no tiene eso, la IA no te salva, y por ahí es por donde conviene empezar.

Todos tenemos un sistema que nadie se anima a reescribir. El que «funciona», que tiene quince años, que escribió alguien que ya no está, y que cada vez que se toca rompe algo en un lugar inesperado.

La reescritura siempre se descarta por la misma cuenta: un año de equipo sin entregar nada nuevo, para terminar con algo que hace casi lo mismo.

En las últimas semanas InfoQ juntó dos casos que cambian esa cuenta, y los leí con la pregunta que me hago siempre con estas noticias: ¿qué parte de esto es replicable por un equipo normal, y qué parte depende de algo que la mayoría no tiene?

Dos reescrituras que nadie habría aprobado hace un año

Bun: medio millón de líneas de Zig a Rust

Bun es el runtime de JavaScript que compite con Node.js, y desde diciembre de 2025 es de Anthropic (corre debajo de Claude Code). Estaba escrito en Zig y Jarred Sumner, su creador, contó en julio cómo lo pasó a Rust.

Los números del post original son estos:

  • 535.496 líneas de Zig (sin contar comentarios) traducidas en 11 días, del 3 al 14 de mayo, con 6.502 commits.
  • Unas 64 instancias de Claude en paralelo (4 copias de trabajo con 16 agentes cada una), usando una versión previa de Claude Fable 5.
  • Unos 165.000 dólares en tokens a precio de API.
  • La estimación a mano: tres ingenieros con todo el contexto del código, durante un año, sin poder hacer nada más.

La primera versión escrita en Rust, Bun 1.4, salió el 20 de agosto. De ahí sale el «cuatro meses en vez de un año» del titular de InfoQ: la traducción fue lo rápido, el resto fue estabilizar.

El motivo no fue la moda. Sumner dice que gran parte de sus bugs eran de manejo de memoria (usar memoria ya liberada, liberarla dos veces u olvidarse de liberarla) y que en Rust seguro eso pasa a ser un error de compilación. Y aclara, con honestidad, que no culpa a Zig: mezclar recolector de basura con memoria manejada a mano es un caso raro.

Google: giflib, chiquita y peligrosa

El otro caso es el opuesto en tamaño. giflib es una librería de C de unas 3.000 líneas que decodifica GIF, muchas veces de usuarios desconocidos. El equipo de seguridad de Google la reescribió en Rust con Gemini y publicó el resultado como giflib-rs, un reemplazo que respeta la misma interfaz binaria que la versión en C.

El proceso, según la cobertura de InfoQ, tuvo tres etapas:

  • Un único prompt a Gemini para traducir toda la lógica de C a Rust.
  • Revisión humana de la frontera con C: quién es dueño de cada puntero y cuánto vive.
  • Fuzzing diferencial: el mismo input a las dos versiones, durante seis días y 200 millones de iteraciones, más una regresión contra más de 30 millones de GIF reales.

Lo más interesante es lo que encontró de paso: una escritura fuera de límites en la versión en C, en la función EGifGCBToExtension. Es la que después se publicó como CVE-2026-26740, y para ese momento Google ya corría la versión en Rust, que ante el mismo input devolvía un error en vez de pisar memoria.

El modelo tradujo; el que aprobó fue el oráculo

La lectura fácil de estas dos historias es «la IA ya reescribe sistemas enteros». Yo creo que es la lectura equivocada.

En testing se llama oráculo a lo que te dice si un resultado es correcto. Y lo que tienen en común Bun y giflib no es el modelo (uno usó Claude, el otro Gemini), es que los dos tenían un oráculo antes de escribir la primera línea nueva.

El de Bun es su suite de tests. Tiene 60.624 tests y casi 1,4 millones de verificaciones, y la frase clave del post es casi una nota al pie: los tests están escritos en TypeScript, así que no dependen del lenguaje en que está hecho el runtime.

O sea, la vara de medir quedó afuera de lo que se estaba reescribiendo. Se tradujo el motor entero y la vara siguió siendo la misma, con cero tests salteados o borrados.

El de Google es todavía más puro: la versión vieja en C es el oráculo. No hace falta escribir qué tiene que pasar con cada GIF, alcanza con pedir que las dos implementaciones den lo mismo y dejar que una máquina busque durante seis días el input donde no coinciden.

Con eso a mano, el agente puede equivocarse todo lo que quiera. Cada error aparece como un test en rojo o una diferencia en el fuzzer, vuelve al modelo y se corrige. Sin eso, cada error es una sorpresa en producción.

Por eso digo que el modelo es la parte reemplazable. El oráculo, no.

Lo que el oráculo no ve

Bun reconoce 19 regresiones que la suite no atrapó y que se corrigieron después. Son del tipo que un test difícilmente cubre, como cadenas UTF-16 de largo impar o diferencias en cómo cada lenguaje chequea límites según el modo de compilación.

Además, cerca del 4% del código Rust de Bun sigue dentro de bloques unsafe, casi siempre en la frontera con C++ o con librerías de C. La seguridad de memoria no llegó a todos lados, llegó a donde el lenguaje la puede garantizar.

La crítica más dura vino de Andrew Kelley, el creador de Zig, en su respuesta. Su argumento, más o menos: si la suite es tan buena como para aprobar un millón de líneas que nadie revisó línea por línea, ¿por qué el código en Zig tenía tantos bugs?

No tiene respuesta cómoda. Un oráculo te dice que el comportamiento observable no cambió, no que el código nuevo sea mantenible, ni que los que lo van a mantener lo entiendan.

Ya lo vimos con los agentes: los evals pueden dar verde y el sistema fallar igual. Un oráculo flojo es peor que no tener ninguno, porque te da la confianza sin el respaldo.

En el caso de Google hay dos detalles igual de sobrios. La revisión de punteros y tiempos de vida en la frontera con C la hicieron personas, y el propio README de giflib-rs lista diferencias conocidas con el original: algunas son correcciones deliberadas de bugs de C y otra es que, si se queda sin memoria, Rust entra en pánico donde C devolvía un código de error.

Equivalencia perfecta no existe. Lo que existe es equivalencia medida, con las diferencias escritas.

Si tu legacy no tiene tests, esta opción no es para vos (todavía)

Acá viene la parte incómoda. Pensá en el sistema que tenés en la cabeza desde el primer párrafo: el ERP en .NET Framework 4.x, el monolito Java 8 con Struts, el servicio en C que procesa archivos de un banco desde 2009.

¿Qué oráculo tiene? En la mayoría de los casos que conozco, ninguno automatizado. El oráculo es una persona («preguntale a Carlos, él sabe qué tiene que dar») o es producción misma, que te avisa con un incidente.

Con eso, poner agentes a traducir es generar más rápido código que nadie puede verificar. Vas a tener el sistema nuevo en semanas y el año siguiente descubriendo en qué se diferencia del viejo.

Lo que muestran estos dos casos es el orden correcto: primero el oráculo, después la traducción. Y la IA también sirve, y mucho, para la primera parte.

Por dónde empezaría yo con un sistema propio

Si mañana me dieran un sistema legacy para modernizar, antes de pensar en qué lenguaje quiero terminar haría esto:

  • Tests de caracterización antes que tests de especificación. No se trata de escribir qué debería hacer el sistema, sino de capturar lo que hace hoy (incluidos sus bugs) y congelarlo. Es la idea de Google: el viejo es la verdad hasta que decidas lo contrario.
  • Probar desde afuera del lenguaje. Como la suite en TypeScript de Bun: tests contra la API HTTP, contra los archivos que genera, contra lo que escribe en la base. Si los tests usan el mismo framework que querés abandonar, se reescriben junto con el código y perdés la vara.
  • Grabar tráfico real y reproducirlo contra las dos versiones. Es el fuzzing diferencial del pobre, y para sistemas de negocio muchas veces rinde más que el fuzzing de verdad: los 30 millones de GIF de Google eran eso, datos reales.
  • Elegir una pieza chica y peligrosa. giflib son 3.000 líneas que tocan input no confiable. Buscá tu equivalente: el parser de archivos, el cálculo crítico, la librería de C que nadie quiere tocar.
  • Documentar las diferencias como decisión, no como sorpresa. El README de giflib-rs es un buen modelo: cada diferencia con el original está escrita y justificada.

Nada de esto es nuevo. Los tests de caracterización y el «golden master» existen hace décadas, y siempre los postergamos porque eran caros de escribir y no entregaban nada visible.

Lo que cambió es el retorno. Antes, armar el oráculo era la mitad del costo de una reescritura que igual no se iba a aprobar. Hoy, armar el oráculo es la mayor parte del trabajo y la traducción se volvió barata.

No creo que todos tengan que salir a reescribir su legacy en Rust, y tampoco creo que la reescritura con IA sea una trampa. Creo que la pregunta cambió: ya no es «¿nos animamos a reescribirlo?», es «¿podemos demostrar que lo que reescribimos hace lo mismo?».

Si la respuesta es no, ya sabés cuál es el primer proyecto. ¿Tu sistema legacy tiene hoy algo que pueda hacer de oráculo? Te leo en los comentarios.

Escrito por

Pablo Ariel Di Loreto

Profesor. Informático. Fanático del helado de dulce de leche. Director de Ingeniería en MODO, y Secretario del Microsoft Users Group Asociación Civil. Además, soy owner de iniciativas como ConoSurTech y Aprender IT.

Ver todas las entradas de Pablo Ariel Di Loreto →
Suscribirse
Notificarme de
guest

0 Comentarios
Viejos
Nuevos Más votados
Scroll al inicio
0
Nos encantaría conocer tu opinión: ¡comenta!x