- Los desarrolladores de Anthropic migraron durante el último mes 10 paquetes de decenas a cientos de miles de líneas usando Claude Fable 5, Claude Opus 4.8 y flujos de trabajo dinámicos, y en lugar de corregir código individual, mejoraron el proceso iterativo que genera el código
- La migración de Bun de Zig→Rust generó 1 millón de líneas en menos de 2 semanas y pasó el 100% de las pruebas existentes antes del merge; un proyecto de Python→TypeScript movió 165,000 líneas durante un fin de semana usando cientos de agentes, 8 compuertas por etapas y 3 revisiones adversariales
- Las migraciones a gran escala permiten paralelizar el trabajo, el código existente actúa como especificación y respuesta correcta, y las fallas de compilación y pruebas generan automáticamente la siguiente cola de trabajo, por lo que es ideal para construir un bucle de validación objetiva
- El proceso avanza por etapas desde preparar criterios de evaluación hasta elaborar un reglamento, mapa de dependencias e inventario de diferencias, probar el reglamento bajo estrés, traducir todo, compilar, ejecutar y comparar comportamiento; cuando un error se repite, no se corrige archivo por archivo, sino que se ajusta la regla superior y se regenera
- El costo sigue siendo de decenas o cientos de miles de dólares o más, pero es posible descartar ramas fallidas e intentarlo de nuevo; la migración de Bun consumió unos 165,000 dólares según precios de API y logró reducir el uso de memoria, recortar 19% el tamaño del binario y mejorar entre 2 y 5% el rendimiento en cargas reales
Mejorar el bucle de generación, no el código
- La migración de código con IA consiste en que agentes trasladan una base de código de producción a un nuevo lenguaje o framework
- En vez de que ingenieros traduzcan archivos directamente, escriben reglas de migración y bucles de validación
- Los agentes repiten traducir, compilar y probar hasta que el comportamiento del código nuevo coincide con el original
- Proyectos que antes tomaban varios años pueden acortarse a unas cuantas semanas
- En Anthropic, usaron Claude Fable 5, Claude Opus 4.8 y flujos de trabajo dinámicos para migrar durante un mes 10 paquetes de código de decenas a cientos de miles de líneas
- El principio operativo clave es no retocar directamente el código generado, sino modificar el bucle que produjo ese código
Casos reales de migración
-
Migración de Bun de Zig→Rust
- Jarred Sumner migró Bun de Zig a Rust con Claude Code
- Generó 1 millón de líneas de código en menos de 2 semanas
- Antes del merge, pasó el 100% de la suite de pruebas existente de Bun en CI
- Las 19 regresiones detectadas después del merge ya fueron corregidas
- El port a Rust se incorporó a Claude Code en junio
- Bun supera los 10 millones de descargas mensuales y también se usa ampliamente dentro de Claude Code
-
Migración de Python→TypeScript
- Mike Krieger migró durante un fin de semana una base de código en Python a 165,000 líneas de TypeScript
- Usó cientos de agentes, 8 compuertas por etapas y 3 revisiones adversariales
- Realizó una verificación final de equivalencia comparando la salida de todos los comandos contra el original en Python
- Repitió el proceso de descartar todo el resultado de la migración y ajustar reglas y flujo de trabajo, y adoptó el resultado de la tercera ejecución
Cuándo volver a considerar una migración de lenguaje
- Si el entorno tecnológico cambió desde el desarrollo inicial y los compromisos previos ahora se volvieron una limitación, apareció un mejor enfoque o el ecosistema original se redujo, puede valer la pena reconsiderar una migración
- Zig ofrecía rendimiento a nivel de C y simplicidad, por lo que era adecuado para la etapa inicial en la que Bun era desarrollado por una sola persona, pero esa simplicidad tenía compromisos conocidos
- Antes, una migración de lenguaje requería detener el roadmap y dedicar recursos de varios trimestres
- Podía ser necesario mantener en paralelo dos bases de código durante varios trimestres o años
- Si la coincidencia final de comportamiento se quedaba en 90%, podía surgir un problema de mantenimiento peor que antes de empezar
- Ahora existe la opción de borrar una rama fallida y volver a ejecutar
- Una migración de 1 millón de líneas ya no necesariamente exige 3 a 4 millones de dólares en costos de ingeniería durante 4 años, pero todavía puede costar decenas o cientos de miles de dólares o más
- La migración de Bun consumió 5.9 mil millones de tokens de entrada no cacheados y 690 millones de tokens de salida
- El costo según precios de API fue de unos 165,000 dólares
- En el port de Mike, la parte central usó 27 millones de tokens
- La justificación de negocio para una migración ya no tiene que ser existencial; incluso un año corrigiendo bugs de memoria repetitivos o un cuello de botella crónico puede bastar
-
Resolver un cuello de botella de build en Python
- La herramienta interna de Mike se entregaba a usuarios como un solo binario, pero generar binarios por plataforma con la toolchain de Python tomaba unos 8 minutos
- En toda la matriz de build había que esperar unos 30 minutos por cada release
- Tras la migración a TypeScript, la compilación bajó a unos 2 segundos, el arranque del binario fue 6 veces más rápido y también se eliminó una canalización de despliegue separada
Por qué los agentes de IA son adecuados para migraciones
- Permiten trabajo en paralelo
- Es posible dividir en miles de unidades independientes como archivos y crates, y hacer que múltiples agentes trabajen al mismo tiempo
- El código existente funciona como una especificación clara y completa
- También puede usarse como referencia clave para crear instrucciones para los agentes de traducción
- La suite de pruebas actúa como juez integrado
- Si la validación es objetiva, el modelo puede iterar durante días con la respuesta correcta como referencia sin que una persona tenga que arbitrar constantemente la calidad
- Como las fallas de compilación o pruebas se convierten automáticamente en el siguiente elemento de trabajo, disminuye la necesidad de escribir una cola de tareas separada
- La consistencia y el manejo de excepciones pueden incorporarse al bucle
- Los revisores vinculan cada problema con la regla violada
- La forma de resolver una excepción se convierte en una regla que luego siguen todos los agentes
- En lugar de discrepancias silenciosas de comportamiento, las violaciones de reglas se vuelven tareas explícitas
- Fable y Opus 4.8 se usan para delegar, dirigir y validar el trabajo en paralelo de subagentes, y para encontrar múltiples rutas hacia la meta
- El patrón de consulta que combina varios niveles de modelos optimiza el consumo de tokens
Requisito previo: juzgar como equivalentes el original y el port
- Antes de comenzar la migración, se necesita un juez sólido que evalúe con el mismo criterio el código original y el de destino
- Sin juez, tampoco hay criterio de éxito ni condición de cierre
- Las pruebas que dependen de funciones internas del lenguaje fuente pueden no ejecutarse igual en el código de destino
- Las pruebas existentes se clasifican entre las que pueden expresarse como llamadas externas y las que dependen de implementaciones internas que no se portarán
- Las pruebas de comportamiento externo se reescriben como assertions que puedan ejecutarse tanto en el original como en el port
- Un agente adversarial verifica que las assertions no se hayan debilitado durante la reescritura
- El juez se ejecuta sobre el código original para confirmar que pasa, y luego también se comprueba que falle con código roto a propósito
- Jarred ya contaba con una gran suite de pruebas escrita en un tercer lenguaje, TypeScript
- Mike creó un arness de equivalencia con 7 escenarios de uso real y trató cualquier cambio de comportamiento como un bug a corregir
Etapa 1: reglamento, mapa de dependencias e inventario de diferencias
- Los entregables base no son solo el resultado traducido, sino una lista de lugares a refactorizar, un reglamento de cómo traducir y un mapa de dependencias para definir el orden del trabajo
- El orden de elaboración importa
- Primero hay que fijar los valores por defecto del reglamento para poder definir como inventario de diferencias lo que no puede resolverse con esos valores
- El reglamento y el inventario de diferencias se validan juntos mediante una auditoría conjunta
-
Reglamento
- La forma del reglamento cambia según si el código nuevo conservará la estructura existente o se rediseñará por completo
- Si se mantiene la estructura, como en el caso de Jarred, el centro es una tabla de correspondencias entre tipos e idiomatismos de ambos lenguajes, y los componentes difíciles de traducir remiten al inventario de diferencias
- Si se rediseña, como en el caso de Mike, el reglamento cumple la función de documento de diseño
- Jarred fue definiendo políticas al conversar con Claude para cada zona ambigua y configuró 8 subagentes para revisar por separado 8 categorías de errores previstas
-
Mapa de dependencias
- En una migración paralela, hay que identificar dependencias entre archivos para decidir cuáles migrar primero y cuáles agrupar en el mismo lote
- En código legado sin manifiesto explícito y en bases de código como C/C++ o Python, las dependencias deben descubrirse y mapearse manualmente
- Los agentes de Claude Code pueden generar el mapa mediante un bucle de escribir, ejecutar, revisar y corregir scripts deterministas
- Puede verse un ejemplo general en el prompt de mapa de dependencias
-
Inventario de diferencias del lenguaje y revisor escéptico
- El inventario de diferencias registra conocimiento que está implícito en el código existente pero debe hacerse explícito en el lenguaje de destino
- En Zig→Rust, la principal diferencia fue el manejo de memoria
- En Zig, el hecho de que quien llama deba liberar un buffer puede existir solo en comentarios, compilar igual si se omite y la fuga descubrirse en ejecución
- En Rust, la propiedad se transfiere a quien llama, la memoria se libera automáticamente y el uso después del move o la doble liberación no compilan
- En Python→TypeScript, la principal diferencia fueron las interfaces y contratos
- En Python no hace falta declarar la forma de los objetos recibidos ni de los valores devueltos
- En TypeScript hay que escribir contratos de métodos, argumentos y formas de retorno para que compile
- Jarred listó las diferencias antes de traducir, mientras Mike tradujo primero y construyó la lista durante la auditoría, así que ambos enfoques pueden servir según el proyecto
- Puede verse un ejemplo general en el prompt para generar inventario de diferencias
Etapa 2: prueba de estrés del reglamento
- Antes de la migración completa, se sacude el reglamento con una migración piloto pequeña para encontrar problemas
- Jarred comparó tres tareas de agentes
- El primer agente tradujo 3 archivos según el reglamento
- El segundo agente tradujo la misma escala como si fuera un ingeniero experto en Rust
- El tercer agente redactó nuevas reglas de traducción basadas en las diferencias entre ambos resultados
- En este proceso se detectaron 2 problemas críticos antes de propagarlos a los 1,448 archivos completos
- Este método solo funciona en migraciones que preservan estructura, donde se pueden comparar línea por línea dos traducciones del mismo archivo
- Si se rediseña, como hizo Mike, los revisores adversariales atacan el documento de diseño y la validación debe hacerse con ejecuciones end-to-end descartables
- Todos los archivos traducidos en la prueba deben desecharse; el objetivo no es avanzar gradualmente en el código, sino mejorar las reglas
- Puede verse un ejemplo general en el prompt de prueba de estrés
Etapa 3: traducción de todo el código
- Las etapas posteriores usan todas un bucle multiagente de implementación→revisión→corrección
- La implementación masiva puede encargarse a modelos pequeños y la revisión a modelos grandes
- Mike usó 12 subagentes Claude Sonnet para paralelizar la migración principal
- La cola de trabajo se gestiona mecánicamente
- Un script por lotes determina el estado de completado según la existencia en disco de los archivos traducidos
- Divide los archivos restantes en lotes para los agentes de implementación
- En cada ejecución reconstruye la cola a partir del estado del disco, por lo que básicamente puede reanudarse tras una interrupción
- Si los agentes son demasiado cautelosos y avanzan poco, se les puede instruir de forma más directa con el contexto de que el compilador detectará errores en la siguiente etapa
- Lo que no puedan resolver con suficiente confianza se marca como
// TODO(port): <reason>y se atiende en la etapa 4 - Las listas de trabajo posteriores se generan automáticamente a partir de errores de compilación, choques en smoke tests y fallas de pruebas
-
Revisión adversarial y actualización de reglas
- Dos revisores adversariales con contexto independiente evalúan el resultado de implementación, y si discrepan, un tercer agente decide
- Si el mismo error se repite en varios archivos, no se corrige uno por uno
- Se agrega una sola línea al reglamento y se regeneran los lotes afectados
- Incluso durante la etapa de traducción, el reglamento sigue expandiéndose y no se parchea a mano el código que no lo cumple
-
Dónde entra el compilador
- Si compilar toma poco tiempo, puede incluirse dentro del bucle de traducción
- Mike ejecutaba la compilación de TypeScript en todos los bucles porque terminaba en segundos por unidad
- Si compilar toma más tiempo, se deja para la etapa siguiente
- Jarred prohibió usar el compilador en el bucle de traducción porque ejecutar
cargotomaba minutos - A partir de esta etapa, los prompts se vuelven más cortos; puede verse un ejemplo en el prompt de arranque de traducción
Etapas 4~6: compilar, ejecutar y comparar comportamiento
- Las tres etapas comparten la misma estructura de bucle, y mientras más avanzan, menos juicio humano hace falta
- Según el lenguaje y el tamaño del proyecto, la etapa de compilación puede absorberse dentro de la etapa de traducción completa
-
Etapa 4: compilación
- Jarred configuró un script orquestador para ejecutar el compilador una sola vez sobre todo el workspace
- Los agentes de corrección procesan en paralelo la lista de errores, pasan por revisión adversarial y vuelven a compilar, repitiendo el ciclo
- La revisión de la lista de errores se usa no para cada error aislado, sino para descubrir problemas sistémicos
- Tras corregir imports circulares permitidos por la compilación diferida de Zig, aparecieron miles de errores de módulos en Rust
- Se agregó al bucle lógica para clasificar qué dependencias eliminar o mover y cómo reconfigurar los límites
-
Etapa 5: ejecución y smoke tests
- Los choques detectados en smoke tests cumplen el mismo papel de respuesta mecánica correcta que la lista de errores de compilación
- En lugar de tratar cada problema individualmente, se agrupan por causa raíz y luego subagentes adversariales los revisan
-
Etapa 6: comparar comportamiento con el original
- El código que ya pasó traducción, compilación y smoke tests se divide y se ejecuta la suite de pruebas preparada en etapas previas tanto sobre el original como sobre el port
- Los agentes de corrección revisan en conjunto las pruebas fallidas y ambas bases de código, y revisores adversariales verifican las correcciones
- Solo el daemon de build puede volver a compilar los binarios
- Los agentes de corrección escriben parches, y el daemon los reúne para reconstruir solo una vez
- Luego vuelve a ejecutar las pruebas afectadas y devuelve el resultado
- El trabajo se serializa para que múltiples agentes no ejecuten por separado builds costosos
- Si la misma falla se repite en varias pruebas, se corrige la regla superior que generó el bug y solo se regeneran los archivos afectados por esa regla
-
Cuando no hay suite de pruebas
- Mike hizo que Claude generara un pequeño script que ejecutaba 7 escenarios reales tanto en el Python original como en el nuevo port y comparaba resultados
- Asignó un agente de corrección distinto a cada escenario fallido y repitió hasta que los 7 pasaron
- Claude también diseñó su propia suite end-to-end y la ejecutó de forma autónoma durante la noche
- Durante cuatro noches repitió el ciclo de corregir errores y volver a ejecutar
- La lista de escenarios preparada de antemano permitió detectar incluso problemas menores de usabilidad difíciles de prever
- Aunque no existan pruebas previas, Claude puede usar la base de código original como respuesta correcta para construir el juez
Principios operativos observados en ejecuciones repetidas
- En lugar de seguir una guía al pie de la letra, conviene planear la migración junto con Claude según las características del proyecto antes de arrancar
- Hay que dejar que los agentes de corrección resuelvan fallas individuales, mientras las personas se concentran en patrones de error repetidos
- La revisión debe ser adversarial y la validación, mecánica
- La revisión adversarial es útil en trabajos prolongados y puede valer el costo adicional de tokens
- Scripts como el compilador,
diffy la suite de pruebas deben usarse como juez final
- No se usa siempre el modelo más grande para todas las tareas
- Los modelos pequeños se aprovechan para paralelizar implementación masiva
- Los modelos más grandes se concentran en revisar y escribir reglas que seguirán otros agentes
- El tiempo de trabajo humano debe invertirse al inicio en el reglamento y la prueba de estrés; después, la mayor parte del proceso consiste en vaciar la cola de trabajo
- El estado de completado debe poder determinarse mecánicamente, por ejemplo, con “el archivo de salida existe en disco”, y la cola debe poder reanudarse
Resultados y limitaciones de la migración de Bun
- El port de Bun a Rust ya opera en producción, pero alrededor de 4% del código Rust está dentro de bloques
unsafe- La mayor parte son operaciones de punteros de una sola línea en límites con C/C++
- Se corrigieron todas las fugas de memoria detectables con herramientas
- En un benchmark con 2,000 repeticiones del build, el uso de memoria bajó de 6,745MB a 609MB
- El tamaño de los binarios en Linux y Windows se redujo 19%
- Gracias a optimizaciones entre lenguajes, el rendimiento en cargas reales como servicios HTTP,
next buildytscmejoró entre 2 y 5% - En migraciones grandes, hay que revisar más los resultados que produce el bucle y los patrones repetidos que cada línea individual de código generado
Material relacionado
- Migration starter kit: plantilla que generaliza el procedimiento del artículo; los dos ports reales no se ejecutaron exactamente con este kit
- Code-modernization plugin: plugin para modernización de sistemas legados y upgrades de frameworks, no para ports de lenguaje
- Dynamic workflows in Claude Code: introducción a los flujos de trabajo dinámicos en Claude Code
Aún no hay comentarios.