Regístrate Panel
21 de septiembre de 2026

Cómo evitamos que el slop llegue a producción

Explore with AI

Probablemente estés construyendo una fábrica de software o alquilando una a un proveedor. Tienes un sistema que te lleva de un prompt a un pull request. Se encarga de los tests, el linting, el formato y las revisiones de código automatizadas.

Pero todavía no tienes respuesta a la pregunta más importante: ¿este cambio está bien para ir a prod?.

graph TB
    W["Agent writes the code"] --> T["Types and tests"]
    T --> L["Linter and formatter"]
    L --> R["Code review"]
    R --> Q(["Is this okay for prod?"])
    Q --> D["Deploy"]
    style Q fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d

Todas tus comprobaciones actuales miran el diff, pero nada en tu fábrica sabe nada sobre el sistema de producción en el que ese diff está por aterrizar. Nada puede realmente evitar que el slop llegue a prod.

Construimos esta capacidad dentro de Polylane y este post trata sobre los detalles técnicos de cómo la implementamos.

Cómo funciona

La única pregunta que este sistema debería responder es:

¿Este cambio, una vez fusionado y desplegado, tendría un impacto negativo en producción?

No nos importan mucho las cosas típicas que comprobaría un agente de revisión de código, como el estilo, los nombres, la cobertura de tests, etc. Y decidimos responder esta pregunta en la etapa del pull request, junto con todos tus tests existentes.

En última instancia, Polylane comenta en el pull request con un simple mensaje de “go” / “no-go” junto con las pruebas de su investigación.

El flujo es bastante simple:

  • ¿Este pull request toca archivos que puedan afectar a producción?
  • ¿Qué recursos de nube podrían verse afectados?
  • Reunir contexto sobre el estado actual de producción para esos recursos
  • Evaluar varios modos de fallo potenciales que este cambio podría introducir
  • Pronosticar cómo podría cambiar producción con estos cambios desplegados
  • Alertar a los desarrolladores de los posibles modos de fallo probables
polylane bot commented 2 minutes ago ···
Caution

Merging this pull request may degrade production (high impact).

Merging this blocks every write to orders while the index builds. migrations/0114_order_search_trgm.sql:3 adds CREATE INDEX … USING gin (search_text gin_trgm_ops) without CONCURRENTLY, and a plain CREATE INDEX takes a full write lock on orders for the whole build. Checkout sustains ~38 writes/s on that table; each one queues behind the lock until the build finishes.

To make this safe: build the index with CREATE INDEX CONCURRENTLY outside the transactional migration.

orders-db · writes per second · last 48h
0 20 40
-48h -24h now
every one of these writes blocks while the index builds
Un veredicto de no-go: el mecanismo en la primera línea, la evidencia debajo, y qué haría que el cambio fuera seguro.

Todo esto se apoya en el context graph que construimos continuamente conectando todos los recursos de nube en tus diferentes cuentas de nube.

graph TB
    P["Open pull request"] --> Q(["Meaningful code change?"])
    Q -->|"docs, tests, comments"| C["Conclude the check"]
    Q -->|"yes"| R["Find affected resources<br/>in the context graph"]
    R -->|"none"| C
    R -->|"yes"| E["Gather context"]
    E --> X["Build failure trajectories"]
    X --> F["Forecast the affected series"]
    F --> V["Verdict"]
    V --> C
    style X fill:#fef3c7,stroke:#fcd34d,color:#78350f
    style F fill:#fef3c7,stroke:#fcd34d,color:#78350f
    style V fill:#d1fae5,stroke:#6ee7b7,color:#065f46

Ensamblando el contexto

Nuestro context graph es clave para que esto funcione: construye un registro de todos tus recursos de nube, todos tus repositorios, equipos, etc. Por ejemplo, un nodo de cómputo como una Lambda function está conectado a la base de datos de la que lee y a la cola que la dispara. También añadimos repositorios al grafo. Esto se hace revisando los archivos de manifiesto típicos del repositorio, por ejemplo archivos de terraform, archivos de Cloudformation o archivos de Wrangler. Esto habilita la conexión entre repositorios y recursos de nube.

graph LR
    Q["SQS queue<br/>orders-events"] -->|"triggers"| A["Lambda function<br/>checkout-api"]
    R["Repository<br/>checkout-edge"] -->|"deploys_to"| A
    A -->|"connects_to"| D["RDS instance<br/>orders-db"]
    style R fill:#d1fae5,stroke:#6ee7b7,color:#065f46

Cuando se envía un pull request al repositorio, seguimos los caminos en el context graph para reunir todos los recursos de nube potencialmente afectados por el cambio. Usamos un modelo pequeño para filtrar los recursos, ya que puede haber una gran cantidad de recursos desplegados desde el mismo repositorio. Pasamos este contexto junto con el diff, la descripción del PR y los commits del PR al agente.

graph LR
    R["Repository"] -->|"deploys_to"| C["Candidate resources"]
    C --> F(["Small model filters"])
    F --> A["Agent"]
    D["Diff, description, commits"] --> A
    style A fill:#d1fae5,stroke:#6ee7b7,color:#065f46

También pasamos el diff por un conjunto de heurísticas deterministas para guiar rápidamente la atención del agente hacia cosas que normalmente tienden a tener un impacto negativo en producción:

  • una migración que debe aplicarse a mano, o en un orden particular respecto al despliegue
  • CREATE INDEX sin CONCURRENTLY, o ADD COLUMN ... NOT NULL sin valor por defecto, ambos casos bloquean la tabla durante todo el proceso
  • un endpoint que se elimina mientras la versión actualmente desplegada todavía lo lee
  • código que empieza a leer una variable de entorno, un secreto o un binding que nada en el diff provisiona

Estas son recomendaciones que orientan al agente hacia posibles riesgos de despliegue.

Trayectorias de fallo

Con el contexto que se proporciona arriba, el modelo propone varios modos de fallo que el nuevo diff podría introducir en producción, y va e investiga cada uno.

Su salida es un registro de trayectorias de fallo. Una trayectoria es una cadena causal que va desde un disparador, a través del código modificado, hasta una degradación observable en una métrica específica, y cada eslabón lleva una cita: un archivo y una línea, una plantilla de log con su recuento, una lectura de métrica, una clave de configuración, una arista del grafo.

El agente intenta tanto validar como invalidar cada trayectoria antes de llegar a un veredicto. Cada una termina en uno de tres estados:

  • confirmed cuando la trayectoria se confirmó contra producción.
  • plausible cuando la cadena es concreta pero uno o varios eslabones solo pudieron “suponerse” sin datos de telemetría que lo confirmen.
  • refuted cuando los datos de telemetría de producción aportaron suficiente evidencia de que esta trayectoria es poco probable que ocurra en producción.

Adoptamos un enfoque conservador, y cualquier trayectoria confirmed lleva a una evaluación fallida.

export function deriveTrajectoryVerdict(trajectories: Trajectory[]): "pass" | "fail" {
  return trajectories.some((entry) => entry.status === "confirmed") ? "fail" : "pass";
}

graph LR
    D["The diff"] --> T1["Dropped index still<br/>used by checkout"]
    D --> T2["Retry change amplifies<br/>load on orders-db"]
    D --> T3["Removed binding<br/>breaks the worker"]
    T1 --> C1["confirmed"]
    T2 --> C2["plausible"]
    T3 --> C3["refuted"]
    C1 --> V["No-go"]
    C2 --> V
    C3 --> V
    style C1 fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d
    style C2 fill:#dbeafe,stroke:#93c5fd,color:#1e3a8a
    style C3 fill:#d1fae5,stroke:#6ee7b7,color:#065f46
    style V fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d

Pronosticando el impacto

Le damos al agente una herramienta para pronosticar series de tiempo a partir de datos históricos, inyectando posibles factores externos.

Supongamos que una trayectoria afirma que cierto cambio reduce a la mitad el timeout en una ruta que reintenta. Que esto importe depende del tráfico, y el tráfico no es realmente un solo número, tiene una forma. Una cola que se mantiene en un 60% de profundidad y se queda ahí está bien. La misma cola al 60% pero subiendo cada semana es una situación distinta, y leer la última hora de métricas no te dirá cuál de las dos estás viendo.

Así que antes de que el agente redacte el veredicto de una trayectoria, extrae las series históricas de los recursos afectados y las pronostica en conjunto. Actualmente alojamos nosotros mismos el modelo Toto-2.0-22m.

graph LR
    A["Agent"] -->|"telemetry query"| P["Provider"]
    P -->|"aligned series"| A
    A -->|"up to 16 series"| M["Forecasting model"]
    M -->|"p10, p50, p90"| A
    style M fill:#fef3c7,stroke:#fcd34d,color:#78350f

La razón de usar un modelo multivariado dedicado en lugar de otro prompt es que las series no son independientes. La tasa de peticiones, la tasa de errores, la latencia y la profundidad de la cola en un mismo recurso se mueven juntas, y pronosticar cada una por separado descarta la correlación que hace que el pronóstico valga la pena. Cada serie de una llamada comparte un mismo grupo de atención.

Esta es la capacidad más experimental que agregamos recientemente, y todavía estamos midiendo su impacto. Pero ya pasa la vibes-eval.

checkout-api request forecast
Hourly observations from the latest 64 complete buckets, followed by a 24-hour probabilistic forecast.
observed p10–p90 forecast
20,000 25,000 30,000 35,000 Forecast starts Sep 18 00:00 Sep 18 20:00 Sep 19 16:00 Sep 20 12:00 Sep 21 08:00 Time (UTC) Requests per hour
Figura 2
Un ejemplo de cómo funciona el pronóstico
Entran 64 observaciones horarias de un recurso afectado, y salen 24 horas de mediana y p10-p90. La banda se ensancha con el horizonte, y el agente lee la banda en vez de solo la mediana: un p10 que se mantiene por encima del umbral del que depende una trayectoria es una respuesta distinta de uno que lo cruza.

La restricción de latencia

Ejecutar la evaluación de impacto en producción dentro del flujo del pull request significa que tiene que ser rápida. Nadie quiere un paso que añada 15 minutos a su pipeline de CI. Por ejemplo, en nuestros propios repositorios esta evaluación es un paso obligatorio de CI: si es lenta, todo nuestro SDLC se atasca.

Básicamente tenemos un presupuesto de 2 a 3 minutos para producir una evaluación de impacto precisa. Cualquier cosa que supere eso no es aceptable en un pipeline de CI/CD.

Nuestro primer prototipo fallaba por completo esta restricción. Nuestra mediana rondaba los 7 minutos, y era común ver ejecuciones que tomaban hasta 20 minutos.

0 s 60 s 120 s 180 s 240 s 300 s Preludio webhook, registros, diff 7.4 s Sandbox clonar el head 16.5 s Turno de revisión 242.4 s Entrega ejecución de comprobación, comentario 3 s Sin contabilizar 38.6 s
Figura 3
Latencia mediana de cada uno de los pasos en la ejecución de la evaluación de impacto en producción
Medianas de producción, 15 de septiembre de 2026, sobre 325 ejecuciones. Cada fase tiene su propia mediana, así que el bloque sin contabilizar al final es el encolado entre fases más la aritmética de sumar medianas.

El objetivo pasó a ser averiguar cómo reducir la cantidad de pasos del modelo para lograr que todo el flujo bajara de 3 minutos. Normalmente ejecutábamos entre 30 y 40 pasos secuenciales del modelo, y en los peores casos hasta casi 600 pasos, cada uno consumiendo 17 segundos de nuestro presupuesto, y la gran mayoría de su salida eran tokens de razonamiento.

Hicimos bastantes cambios en las últimas 3 semanas, con distintos impactos en el rendimiento. Estos son los más significativos.

Establecer un presupuesto de pasos y comunicárselo al agente

Introdujimos un presupuesto de pasos, limitado a 30 pasos por turno del agente, y en cada paso comunicamos claramente al modelo cuántos pasos ha consumido. Poner el recuento en el prompt le permite al modelo planificar en torno a él, y eso resulta importar más que el número en sí.

graph TB
    A["Turn starts, 30 steps"] --> B["Model step"]
    B --> T["Tool call"]
    T --> C(["Steps left?"])
    C -->|"1"| E["Record the verdict now"]
    C -->|"more"| R["Next step, carrying<br/>the count in the prompt"]
    R --> B
    style R fill:#fef3c7,stroke:#fcd34d,color:#78350f
    style E fill:#d1fae5,stroke:#6ee7b7,color:#065f46

Empezar revisiones nuevas en lugar de plegarlas en la existente

Un pull request típico sigue recibiendo nuevos commits después de abrirse. En el primer prototipo, plegábamos todo el diff nuevo en el mismo hilo de revisión como un mensaje de usuario que redirigía el rumbo. Esa redirección confundía muchísimo al modelo y lo llevaba a leer archivos que ya había examinado y a volver a ejecutar consultas de telemetría que ya había completado.

Ahora, un commit nuevo cancela la ejecución de evaluación anterior y empieza un hilo completamente nuevo. Parece contraintuitivo, pero al final redujo la latencia de las revisiones completas.

Reutilizar el trabajo previo

Cuando un commit nuevo llega al pull request después de que una revisión ya se completó, antes empezábamos ingenuamente una nueva evaluación de impacto desde cero.

Introdujimos la capacidad de reutilizar la evaluación anterior, y de disparar una nueva evaluación sobre el diff más pequeño entre los dos commits consecutivos.

Desplegamos estos cambios a lo largo de septiembre y fuimos mejorando la latencia de forma gradual, hasta que ahora se ubica cómodamente dentro del presupuesto.

0 s 150 s 300 s 450 s 600 s 27 Aug 31 Aug 4 Sep 8 Sep 12 Sep 16 Sep 94
Figura 4
Latencia mediana de un turno de evaluación de impacto

¿Esto realmente importa?

¿Cuál es el punto de quemar todos estos tokens si no vemos resultados significativos? Nuestra métrica de éxito es la cantidad de incidentes que evitamos por semana para cada uno de nuestros clientes. Un incidente evitado es un pull request donde:

  • señalamos un riesgo potencial para producción
  • un ingeniero envía uno o varios commits
  • una nueva evaluación concluye que el riesgo potencial está mitigado
  • el pull request se fusiona
2.3
Incidentes de producción evitados, por cliente, cada semana

Esto ya es bastante significativo y esperamos que siga creciendo. Iteramos de forma continua y ejecutamos evals para mejorar el rendimiento y la calidad de nuestro agente.

Nadie debería estar de guardia. Polylane vigila tu infraestructura, investiga y arregla lo que se rompe.

Regístrate

Seguir leyendo