Por
Este artículo apareció originalmente en el blog de Kiros. Enlace al final del artículo.
En nuestro artículo anterior, describimos cómo tres ingenieros y un equipo de agentes fusionaron 1.000 solicitudes de cambios en una semana. La pregunta más común que recibimos fue cómo evitamos que eso rompa todo. Pero creemos que hay una pregunta mejor que hacer. ¿Puede un humano ser responsable de cada PR que envió, lo que incluye código que no escribió y no leyó línea por línea? ¿Podrías hablar con seguridad sobre cualquiera de esos PR si un líder técnico te preguntara al respecto?
Un agente puede escribir un cambio de código en minutos, así que la velocidad es la parte fácil. Mantener un alto estándar de calidad a esa velocidad es difícil, porque debemos saber que cada cambio es correcto, seguro y vale la pena fusionar sin que un humano lea cada línea.
De dónde viene la confianza
Antes de la IA, la confianza no provenía solo de que alguien leyera y aprobara cada if/else en la revisión. La revisión manual era necesaria, pero no suficiente. Fue seguida por muchas más pruebas, y generalmente por alguien ejecutando el cambio manualmente en un entorno real o simulado. Las pruebas fueron de donde vino la confianza.
Pero las pruebas solo realizadas por humanos no son suficientes. Una persona solo puede probar unos pocos casos, generalmente el flujo feliz o los casos extremos obvios. Por eso, en un servicio en producción que uno de nuestros tres ingenieros ejecutaba antes, cada cambio aún pasaba por pruebas automatizadas en la canalización de lanzamiento después de pruebas manuales cuidadosas. Muestra que los humanos confiaban en harnesses de pruebas automatizadas para obtener una alta confianza incluso antes de la IA.
Entonces, «¿cómo puede un humano ser responsable del código escrito por IA?» se convierte en una pregunta diferente. ¿Cómo construimos pruebas y verificaciones automáticas lo suficientemente sólidas para confiar en ellas más que en nuestra propia lectura? Un humano no puede leer cada cambio, pero una canalización puede probar todos ellos, cada vez, de la misma manera. Por eso, en el equipo Kiro Crew, dedicamos alrededor del 20% de nuestro tiempo a la infraestructura que nos permite automatizar más.
Esto también cambia lo que significa «conocer un PR». Si alguien nos entrega un número de PR, no recitamos el diff de memoria. Preguntamos a nuestro equipo y obtenemos un resumen en lenguaje natural en un minuto, fundamentado en el código, en evidencia de pruebas como capturas de pantalla, videos y datos, y en el registro de revisión. El registro de revisión es el rastro que CI (integración continua) escribe en cada solicitud de cambios: cada hallazgo del revisor, cómo se corrigió o se respondió con evidencia, cada resultado de verificación y el veredicto final de preparación. Muestra que cada cambio pasó por las mismas revisiones, pruebas y puertas. Ese registro es de lo que somos responsables.
Esto hace que la calidad sea un problema de CI/CD y pruebas. Empujamos la cobertura de pruebas por encima del 90% y planeamos alcanzar el 100%, protegidos contra regresiones en el conjunto de pruebas, y agregamos pruebas agentivas, donde un agente ejecuta la aplicación real como lo haría un usuario.
Cómo llegamos aquí
La calidad aumentó en cinco etapas junto con nuestra velocidad de codificación. No planeamos todo el sistema de antemano. Cada etapa trabajó hacia los mismos dos objetivos: mayor calidad y más automatización, y resolver un problema generalmente reveló el siguiente. Ninguna de las etapas está terminada, y seguimos mejorando cada una cada semana. Si ejecutas agentes de codificación en una base de código real, probablemente encontrarás las mismas paredes en el mismo orden.
Figura 1: Cada etapa agregó verificaciones para mantenerse al ritmo del código más rápido.
Antes de las etapas: revisión humana
A principios de marzo, el equipo configuró un mecanismo de revisión de código básico e intentó revisar cada cambio manualmente, un enfoque que duró menos de un día. Kiro Crew era solo un proyecto secundario con muchos menos cambios en ese momento, y aún era demasiado para la revisión humana. Eso nos obligó a automatizar la revisión.
Etapa 1: un revisor automatizado, una canalización humana
Kiro Crew comenzó a través de nuestro sistema interno de construcción y revisión, que ejecuta un bot de revisión automatizado en cada cambio. Agregamos una regla estricta: cada comentario de IA, sin importar su prioridad, tenía que resolverse antes de la fusión. La regla sonaba como calidad, pero se convirtió en fricción, y tres problemas aparecieron rápidamente.
- Sin prueba real de extremo a extremo. La canalización no tenía pruebas de extremo a extremo para Kiro Crew y no tenía uso real del producto, así que un cambio podía romper la aplicación incluso después de que cada comentario fuera resuelto. Un humano tenía que probar todo manualmente y adjuntar capturas de pantalla como prueba, y lo que se escapaba regresaba como una corrección rápida.
- Los comentarios llegaban ronda tras ronda. Solucionabas cinco comentarios, hacías push, y obtenías cuatro nuevos en código que no habías tocado, luego tres más. Porque cada comentario tenía que resolverse, un cambio pequeño podía tomar un día de ida y vuelta. Peor aún, cada corrección a menudo añadía código nuevo, como una verificación adicional o una ruta de código, que atraía comentarios propios. Una pequeña corrección podía crecer en un cambio mucho más grande de lo que nadie planeó.
- Control limitado sobre el revisor. Podíamos agregar nuestras propias reglas, pero no podíamos ver si una regla se disparo, por qué, o por qué se perdió. Sin esa retroalimentación, no podíamos ajustar el revisor a los problemas que seguíamos encontrando.
Lo que aprendimos. Una regla estricta solo ayuda cuando el revisor proporciona retroalimentación completa y ajustable. Los cambios también necesitan ser probados de extremo a extremo en el producto real. Sin ambos, la revisión se convirtió en rotación, y los errores reales aún llegaron a los usuarios.
Etapa 2: niveles de severidad y un segundo revisor
Primero categorizamos los comentarios por severidad. Los hallazgos bloqueantes y las preocupaciones reales deben arreglarse en el mismo PR. Los comentarios menores y los retoques de estilo se convierten en problemas de seguimiento para PR futuros. Esto mantiene cada PR pequeño y mantiene la revisión enfocada en el cambio que está en frente.
La categorización puede parecer una baja de la barra, y no detiene el bucle donde cada corrección atrae nuevos comentarios. Así que también agregamos nuestro propio agente de revisión junto al bot existente. Escanea el cambio en varios pases, a cada uno se le dice que omita lo que los pases anteriores encontraron. El objetivo es mostrar cada comentario real en una ronda, para que un colaborador corrija una vez en lugar de varias veces. Leer esas revisiones cuidadosamente durante esta etapa también nos sorprendió. Los revisores de IA encontraron problemas más profundos de lo que un lector humano generalmente hubiera capturado. Este hallazgo también nos lleva a dejar de revisar código línea por línea manualmente.
El revisor de IA adicional ayudó, pero un humano aún dirigía cada ronda de corrección. El equipo ejecutándose localmente puede hacer la corrección real basada en los comentarios de revisión, pero un humano aún necesita hacer push del código actualizado para la revisión en línea, luego esperar un tiempo para traer los nuevos comentarios de vuelta al equipo local para la siguiente corrección. Este progreso puede tomar múltiples rondas y gastar mucho tiempo, así que algunos colaboradores dejaron de contribuir debido a estos pasos tediosos después de algunos bucles. Para cerrar esa brecha, enviamos habilidades integradas que permiten a cualquiera ejecutar la prueba y la misma revisión de IA localmente, para que puedan obtener los comentarios necesarios para hacer la corrección rápidamente en la máquina local antes de enviar el PR.
Lo que aprendimos. Una revisión estricta ayuda a construir buen software, pero un bucle largo dirigido por humanos puede alejar a los colaboradores. La automatización es la solución, y aún nos faltaba el CI para ejecutarla.
Etapa 3: código abierto, CI real, y muchos revisores
Mover Kiro Crew a GitHub nos dio un fuerte soporte CI/CD para un proyecto de código abierto. Cada solicitud de cambios ahora obtiene construcciones, pruebas unitarias, y pruebas de extremo a extremo automáticamente. El pod de prueba integrado de Kiro Crew ejecuta el código actualizado en un entorno aislado, captura capturas de pantalla y videos de características relacionadas, y las adjunta a la solicitud de cambios. El trabajo que solía comerse una tarde de un ingeniero ahora se ejecuta en cada cambio. Con CI automático, pudimos permitirnos agregar verificaciones mucho más ricas.
Figura 2: El código de fork no confiable y el flujo de trabajo de revisión confiable se ejecutan en lados opuestos de un límite.
Revisores con diferentes trabajos. Un revisor de IA que mira todo no puede profundizar lo suficiente, así que dividimos la revisión por rol. Un revisor de diseño pregunta si el cambio se ajusta a la arquitectura, y si existe una solución mucho más simple. Un revisor de primeros principios pregunta si el cambio soluciona la causa raíz, y si los usuarios realmente lo necesitan. Un revisor de UX evalúa cambios visibles para el usuario. El código también es escaneado por revisores en diferentes familias de modelos, porque un modelo revisando el código de su propia familia comparte sus puntos ciegos.
Un conjunto completo de pruebas. Pruebas unitarias, pruebas de integración, pruebas de interfaz, empaquetado de escritorio, presupuestos de tamaño de paquete, pisos de cobertura, y pruebas de extremo a extremo cada una captura una clase diferente de fallo. La integración es donde los agentes más a menudo se equivocan, porque cada cambio se ve correcto aisladamente.
Una definición de bloqueante. Un hallazgo bloqueante necesita una ubicación concreta, evidencia entrecomillada, un disparador, y una consecuencia. Si un revisor no puede nombrar qué se rompe y cómo, el hallazgo es asesor. Específicamente, las preocupaciones de diseño son asesoras a menos que describan una puerta de un solo sentido, una elección que no podemos deshacer más tarde. Para una elección reversible, aceptamos el riesgo porque la refactorización más tarde también es fácil.
Lo que aprendimos. La automatización te permite verificar mucho más de lo que la gente nunca podría. Pero más verificaciones significaban más resultados para que un humano leyera y actuara, y el humano se convirtió en el cuello de botella.
Etapa 4: cerrando el bucle
En la Etapa 3, un humano aún dirigía cada ronda de corrección de código. En cada ronda, un ingeniero verifica qué pasos de CI han fallado, copia detalles de fallo de CI, incluyendo sugerencias de revisión de IA, al equipo y le pide que arregle problemas. No tratamos una sugerencia de IA como algo que tengamos que confiar, sino como entrada para un análisis local más profundo. Kiro Crew la verifica contra el código, ejecuta las pruebas, y CI verifica la corrección de nuevo en la siguiente ronda. Comparado con estos trabajos adicionales detrás de cada corrección, un humano dirigiendo cada llamada agregó poco valor.
Así que enviamos una habilidad integrada, prepare-pr, que dirige el bucle hasta que la solicitud de cambios esté verde. Necesitaba una señal clara para leer, porque es costoso escanear 80+ verificaciones para cada solicitud de cambios. Agregamos una verificación única de Preparación de PR que enrolla todos los resultados de acción de CI/CD en un veredicto. Debe significar que cada acción de CI/CD esperada ha corrido y pasado, no solo que nada sea rojo aún, porque en una nueva solicitud de cambios «sin verificaciones fallando» y «nada ha corrido aún» se ven iguales.
prepare-pr lee ese veredicto, extrae las acciones de CI/CD fallidas y hallazgos de revisión, arregla lo que es real, responde con evidencia a lo que no es, y hace push de nuevo. CI verifica la nueva revisión, y el bucle lee el resultado. Cuando el veredicto es verde, la habilidad se detiene, y el mismo veredicto le dice a un humano que la solicitud de cambios está lista. El humano y el agente comparten una definición de «hecho».
Figura 3: Cada revisor y prueba se enrolla en un único veredicto de Preparación de PR.
Con agentes dirigiendo solicitudes de cambios a verde automáticamente, hacer clic en el botón de aprobación se volvió lento cuando hay muchos PR. Para automatizar este paso, primero clasificamos cada solicitud de cambios con un nivel. Los PR de nivel simple son aprobados y fusionados automáticamente, y para el resto, la IA redacta sugerencias de revisión para el humano. Para hacer rápida la revisión humana, el agente escribe la descripción de la solicitud de cambios y la reescribe cada vez para que coincida con los cambios de código reales. Usa una plantilla fija, lenguaje fácil de entender, e incluso diagramas relacionados con el cambio, para que un humano pueda entender la solicitud de cambios en unos pocos segundos. También hay una verificación de CI que verifica que la descripción coincida estrictamente con el código.
Lo que aprendimos. Una vez que las verificaciones y correcciones comparten una señal, el agente puede ejecutar el bucle, y los humanos solo hacen las llamadas que las necesitan. El bucle también crea sus propios problemas, principalmente sobre-ingeniería y correcciones de seguridad que van demasiado lejos. Cubrimos ambos después de la última etapa.
Etapa 5: cosas después de la fusión
Nuestras revisiones de código todavía pierden algunos errores, así que la fusión de código no es el paso final. La calidad general generalmente no es solo sobre el código en una solicitud de cambios.
Puertas de lanzamiento
Antes de cada lanzamiento, se ejecutan pruebas adicionales en el producto construido, incluyendo pruebas agentivas. Un agente instala y ejecuta la aplicación real a través de flujos centrales, como iniciar una sesión, ejecutar una tarea, usar memoria, y programar un trabajo. Las pruebas unitarias verifican que una característica específica funciona, y las pruebas agentivas verifican que el procedimiento central del producto aún hace lo que los usuarios esperan.
Seguridad después de la fusión
El código fusionado se sigue verificando en varias capas, así que un problema que se escapa de una capa no vive mucho.
- Verificaciones de cumplimiento cada hora en el código fusionado y en si pasó por la revisión requerida y sigue las reglas de seguridad central.
- Escaneos diarios desde un agente de seguridad interno, aplicados a nuestra base de código de código abierto como lo son a otros servicios internos.
- Bot de seguridad siempre activo que realiza operaciones reales en la herramienta e intenta penetrar los guardarraíles de seguridad para encontrar problemas a arreglar.
- Una bandeja de entrada de seguridad administrada para recibir informes externos con rápida respuesta.
Refactorización periódica
Los cambios rápidos dejan aristas ásperas, como archivos grandes, lógica duplicada, y código muerto. Refactorizamos según un cronograma regular para mantener la base de código fácil de cambiar tanto para humanos como para agentes. Esta refactorización periódica también nos permite usar modelos más nuevos para escribir mejor código.
Escalando las verificaciones mismas
Con docenas de verificaciones automáticas en cada solicitud de cambios y cien solicitudes de cambios al día, llegamos a los límites de velocidad de GitHub y nuestro límite de recursos. Para mitigar el problema, dividimos acciones de CI en grupos luego ejecutamos grupo por grupo. Por ejemplo, una puerta rápida ahora ejecuta las verificaciones baratas primero, y las verificaciones lentas comienzan solo una vez que la puerta rápida pase. También movimos revisión a AWS CodeCommit y algunas cargas de trabajo de prueba a nuestros propios hosts, así que nuestro hardware establece el límite.
El proceso de desarrollo
Cuando la codificación es fácil, decir sí a una característica es fácil también. Pero demasiadas características fusionadas a la vez fácilmente pueden llevar a conflictos implícitos inesperados. El código barato también puede tirar al equipo hacia soluciones técnicas elegantes en lugar de enfocarse en cuál es la forma correcta de construir. Para manejar este problema, cada solicitud de característica ahora se clasifica antes de que alguien la construya. Un cambio simple puede construirse directamente, una característica de nivel medio necesita un diseño de una página, y una complicada necesita una revisión por todo el equipo.
Lo que aprendimos. Pasar cada verificación antes de la fusión no es lo mismo que mantenerse saludable a lo largo del tiempo. La calidad necesita verificaciones que sigan ejecutándose en el código del producto. También necesita un proceso que decide qué características se construyen, porque los errores más caros son aquellos de características que los usuarios nunca necesitaron.
Dos problemas críticos que tuvimos que superar
Sobre-ingeniería
Esta es la mayor fuente de desperdicio una vez que el bucle de revisión y corrección se ejecuta por sí solo. Un revisor señala un caso extremo, y el agente agrega un mecanismo. La siguiente revisión señala un caso extremo en el nuevo mecanismo, y el agente agrega otro. Después de cinco rondas, una corrección de diez líneas se ha convertido en un pequeño subsistema que no era necesario.
Así que agregamos retrospectivas, tanto en la habilidad prepare-pr como en CI. Después de varios rondas en la misma área, el bucle mira su propio historial: qué cambió, qué dijeron los revisores, y si el cambio está creciendo en lugar de convergir. Luego recomienda una parada, una reversión, o un enfoque más simple. El primer movimiento es generalmente restar mecanismos que ya no son necesarios, y los hallazgos restantes se encogen también.
También verificamos cada corrección sugerida por proporción. Un hallazgo puede ser correcto y aún pedir demasiado. La corrección debe ser el cambio de código directo que soluciona el problema descrito en la descripción de la solicitud de cambios, y cualquier cosa más grande se convierte en un seguimiento. La única excepción es un crash alcanzable, un agujero de seguridad, o una pérdida de datos, que siempre se arreglan.
Pero la retrospectiva también creó un nuevo problema que requiere acciones humanas para decidir qué cambios de estrategia de codificación se deben hacer aunque las recomendaciones de estrategia son generalmente claras. Pedir más entrada humana podría parecer la mejor manera de obtener código de más alta calidad, pero cada pregunta detiene la automatización. Para manejar el problema, un segundo agente, el conductor, lee la recomendación de la retrospectiva y hace la primera llamada por sí mismo. El humano solo se incorpora cuando el conductor no puede decidir.
Figura 4: Una retrospectiva y un agente conductor detienen el bucle de revisión de convertir una corrección en un subsistema.
Consideraciones de seguridad
La IA es entusiasta con la seguridad. Dado un hallazgo real, agregará tres capas de defensa, una nueva bandera de configuración, y un paso de validación que nunca se ejecuta. Cada adición es código nuevo que puede romperse, y nueva superficie para que la siguiente ronda de revisión señale. A continuación hay dos casos interesantes que fueron detectados en nuestro desarrollo.
- Un sandbox que bloquea el trabajo normal. Cada ronda de revisión apretó el sandbox del agente un poco más. Cada paso se veía razonable solo, pero juntos hicieron que las tareas ordinarias fallaran, como ejecutar una construcción o leer un archivo de proyecto. El producto se hizo más seguro en papel pero no usable en la práctica.
- Defenderse contra un atacante que ya ganó. Una sugerencia clásica de seguridad es copiar un ejecutable a su propia carpeta y bloquearlo, así un atacante en la máquina no puede reemplazarlo. Esta defensa añade copia, verificaciones de permiso, y rutas de actualización que cada una puede romperse, y protege contra un caso super raro.
Por lo tanto, nuestras correcciones de seguridad siguen una regla simple: arreglá el problema real alcanzable por el usuario con los cambios de código más pequeños.
Lo que puedes aplicar hoy
A continuación hay sugerencias que funcionaron para nosotros, pero cada base de código es diferente, así que ajústalas para que funcionen para tu producto.
- Categoriza tus comentarios de revisión. «Resuelve cada comentario» suena riguroso pero a menudo produce rotación.
- Pide todos los comentarios en una ronda. Un revisor más lento pero completo cuesta menos que uno que gotea hallazgos por rondas.
- Divide revisores por rol y por modelo. Diferentes preguntas y familias de modelos capturan diferentes problemas.
- Comprime tus verificaciones en una única bandera de preparación que signifique «todos corrieron y pasaron», no «ninguno falló aún».
- Prueba el producto completo, no solo las partes, con al menos un paso ejecutando flujos reales antes del lanzamiento.
- Pon una retrospectiva en tu bucle de agente, y deja que un segundo agente haga la primera llamada así la pausa no siempre requiere un humano.
- Mantén las correcciones de seguridad razonables. Usa verificaciones estrictas para revelar problemas reales, pero haz correcciones pequeñas y directas.
Kiro Crew es de código abierto. Los flujos de trabajo, indicaciones de revisión, y la habilidad prepare-pr descrita aquí están todos en el repositorio en GitHub. Prueba Crew para verlas en acción.
Artículo original: Keeping a software factory stable at 1,000 PRs a week - Kiro



