4 puntos por GN⁺ 2 시간 전 | Aún no hay comentarios. | Compartir por WhatsApp
  • 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 cargo tomaba 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, diff y 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 build y tsc mejoró 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

Aún no hay comentarios.

Aún no hay comentarios.