Iscriviti Dashboard
21 settembre 2026

Come impediamo allo slop di arrivare in produzione

Explore with AI

Probabilmente stai costruendo una software factory oppure ne stai affittando una da un provider. Hai un sistema che ti porta da un prompt a una pull request. Gestisce test, linting, formattazione e code review automatizzate.

Ma non hai ancora una risposta alla domanda più importante: questa modifica va bene per andare in produzione?.

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

Tutti i tuoi controlli attuali guardano il diff, ma niente nella tua factory sa qualcosa sul sistema di produzione su cui il diff sta per finire. Niente può davvero impedire allo slop di arrivare in produzione.

Abbiamo costruito questa capacità dentro Polylane e questo articolo riguarda i dettagli tecnici di come l’abbiamo implementata.

Come funziona

La domanda a cui questo sistema deve rispondere è:

Questa modifica, una volta fatto il merge e il deploy, avrebbe un impatto negativo sulla produzione?

Non ci interessano davvero le cose tipiche che un agente di code review controllerebbe, come lo stile, la denominazione, la coverage dei test, ecc. E abbiamo deciso di rispondere a questa domanda nella fase della pull request, insieme a tutti i tuoi test esistenti.

Alla fine, Polylane commenta la pull request con un semplice messaggio “go” / “no-go” con le prove della sua indagine.

Il flusso è piuttosto semplice:

  • Questa pull request tocca file che potrebbero avere un impatto sulla produzione?
  • Quali risorse cloud sono potenzialmente colpite?
  • Raccogliere il contesto sullo stato attuale della produzione per quelle risorse
  • Valutare più potenziali modalità di guasto che questa modifica potrebbe introdurre
  • Prevedere come la produzione potrebbe cambiare con queste modifiche distribuite
  • Avvisare gli sviluppatori delle probabili modalità di guasto potenziali
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 verdetto no-go: il meccanismo nella prima riga, le prove sotto di essa, e cosa renderebbe la modifica sicura.

Tutto questo si basa sul context graph che costruiamo continuamente collegando tutte le risorse cloud nei tuoi vari account cloud.

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

Assemblare il contesto

Il nostro context graph è fondamentale per far funzionare tutto questo: costruisce un registro di tutte le tue risorse cloud, tutti i tuoi repository, team, ecc. Per esempio, un nodo di calcolo come una funzione Lambda è collegato al database da cui legge e alla coda che lo attiva. Aggiungiamo anche i repository al grafo. Questo viene fatto osservando i tipici file manifest nel repository, per esempio i file terraform, i file Cloudformation o i file Wrangler. Questo consente la connessione tra i repository e le risorse cloud.

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

Quando una pull request viene inviata al repository, seguiamo i percorsi sul context graph per raccogliere tutte le risorse cloud potenzialmente interessate dalla modifica. Usiamo un piccolo modello per filtrare le risorse, dato che potrebbero essercene un gran numero distribuite dallo stesso repository. Passiamo questo contesto insieme al diff, alla descrizione della PR e ai commit della PR all’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

Passiamo anche il diff attraverso una serie di euristiche deterministiche per guidare rapidamente l’attenzione dell’agente verso le cose che di solito hanno probabilmente un impatto negativo sulla produzione:

  • una migrazione che deve essere applicata manualmente, o in un ordine particolare rispetto al deploy
  • CREATE INDEX senza CONCURRENTLY, oppure ADD COLUMN ... NOT NULL senza un valore predefinito, entrambi i quali bloccano la tabella per tutta la durata dell’operazione
  • un endpoint che viene rimosso mentre la versione attualmente distribuita lo legge ancora
  • codice che inizia a leggere una variabile d’ambiente, un secret o un binding che niente nel diff predispone

Queste sono raccomandazioni, che orientano l’agente verso probabili rischi di deployment.

Traiettorie di guasto

Con il contesto fornito sopra, il modello elabora varie modalità di guasto che il nuovo diff potrebbe introdurre in produzione e va a indagarle una per una.

Il suo output è un registro di traiettorie di guasto. Una traiettoria è una catena causale che va da un trigger, attraverso il codice modificato, fino a un degrado osservabile su una metrica specifica, e ogni anello della catena porta una citazione: un file e una riga, un template di log con il suo conteggio, una lettura di metrica, una chiave di configurazione, un arco del grafo.

L’agente cerca sia di confermare sia di smentire ogni traiettoria prima di arrivare a un verdetto. Ognuna termina in uno di tre stati:

  • confirmed quando la traiettoria è stata confermata a fronte della produzione.
  • plausible quando la catena è concreta ma uno o più anelli hanno potuto solo essere “ipotizzati”, senza dati di telemetria a confermarli.
  • refuted quando i dati di telemetria di produzione hanno fornito prove sufficienti che questa traiettoria è improbabile che avvenga in produzione.

Adottiamo un approccio conservativo: qualsiasi traiettoria confirmed porta a una valutazione fallita.

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

Prevedere l’impatto

Diamo all’agente uno strumento per prevedere serie temporali basate su dati storici, iniettando potenziali fattori esterni.

Supponiamo che una traiettoria affermi che una modifica dimezza il timeout su un percorso che effettua retry. Se questo conta davvero dipende dal traffico, e il traffico non è davvero un singolo numero, ha una forma. Una coda che sta al 60% di profondità e ci resta va bene. La stessa coda al 60% ma in crescita ogni settimana è una situazione diversa, e leggere l’ultima ora di metriche non ti dirà quale delle due stai osservando.

Quindi, prima che l’agente redigga il verdetto di una traiettoria, recupera le serie storiche per le risorse interessate e le prevede insieme. Attualmente ospitiamo autonomamente il modello 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

Il motivo per cui usiamo un modello multivariato dedicato invece di un altro prompt è che le serie non sono indipendenti. Il request rate, l’error rate, la latenza e la profondità della coda su una risorsa si muovono insieme, e prevedere ciascuna separatamente butta via la correlazione che rende la previsione utile. Ogni serie in una chiamata condivide un unico gruppo di attenzione.

Questa è la capacità più sperimentale che abbiamo aggiunto di recente, e ne stiamo ancora misurando l’impatto. Ma passa già 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 esempio di come funziona la previsione
Entrano 64 osservazioni orarie di una risorsa interessata, e tornano 24 ore di mediana e p10-p90. La banda si allarga con l'orizzonte, e l'agente legge la banda piuttosto che la sola mediana: un p10 che resta sopra la soglia da cui dipende una traiettoria è una risposta diversa da uno che la supera.

Il vincolo di latenza

Eseguire la valutazione dell’impatto sulla produzione nel flusso della pull request significa che deve essere veloce. Nessuno vuole uno step che aggiunge 15 minuti alla propria pipeline CI. Per esempio, sui nostri stessi repository questa valutazione è uno step CI obbligatorio: se è lenta, tutto il nostro SDLC si blocca.

In pratica abbiamo un budget di 2-3 minuti per produrre una valutazione d’impatto accurata. Qualsiasi cosa oltre non è accettabile in una pipeline CI/CD.

Il nostro primo prototipo falliva completamente questo vincolo. La nostra mediana era di quasi 7 minuti ed era comune vedere run che arrivavano fino a 20 minuti.

0 s 60 s 120 s 180 s 240 s 300 s Preludio webhook, record, diff 7.4 s sandbox clone della head 16.5 s Turno di review 242.4 s Consegna check run, commento 3 s Non contabilizzato 38.6 s
Figura 3
Latenza mediana di ciascuno degli step nella run di valutazione dell'impatto sulla produzione
Mediane di produzione, 15 settembre 2026, su 325 run. Ogni fase ha la propria mediana, quindi il blocco non contabilizzato alla fine è l'accodamento tra le fasi più l'aritmetica della somma delle mediane.

L’obiettivo è diventato capire come ridurre il numero di step del modello per portare l’intero flusso sotto i 3 minuti. Tipicamente eseguivamo da 30 a 40 step sequenziali del modello, e negli scenari peggiori fino a quasi 600 step, ciascuno dei quali consumava 17 secondi del nostro budget, e la stragrande maggioranza del loro output erano token di reasoning.

Abbiamo fatto parecchi cambiamenti nelle ultime 3 settimane, con impatti diversi sulle performance. Ecco i più significativi.

Impostare un budget di step e comunicarlo all’agente

Abbiamo introdotto un budget di step, limitato a 30 step per turno dell’agente, e comunichiamo chiaramente al modello quanti step ha consumato a ogni step. Inserire il conteggio nel prompt permette al modello di pianificare di conseguenza, il che si rivela più importante del numero stesso.

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

Iniziare nuove review invece di integrarle in quella esistente

Una tipica pull request continua a ricevere nuovi commit dopo l’apertura. Nel primo prototipo, integravamo l’intero nuovo diff nello stesso thread di review come un messaggio utente di orientamento. Questo orientamento confondeva molto il modello e lo portava a rileggere file che aveva già esaminato e a rieseguire query di telemetria che aveva già completato.

Ora, un nuovo commit annulla la run di valutazione precedente e ne avvia una completamente nuova in un thread nuovo. Sembra controintuitivo, ma alla fine ha ridotto la latenza per le review complete.

Riutilizzare il lavoro precedente

Quando un nuovo commit arriva nella pull request dopo che una review era già stata completata, ingenuamente avviavamo una nuova valutazione dell’impatto da zero.

Abbiamo introdotto la possibilità di riutilizzare la valutazione precedente, e di avviare una nuova valutazione sul diff più piccolo tra i due commit consecutivi.

Abbiamo distribuito queste modifiche durante tutto il mese di settembre e abbiamo migliorato gradualmente la latenza, che ora rientra comodamente nel budget.

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
Latenza mediana per un turno di valutazione dell'impatto

Ha davvero importanza?

A cosa serve bruciare tutti questi token se non vediamo risultati significativi? La nostra metrica di successo è il numero di incidenti che preveniamo ogni settimana per ciascuno dei nostri clienti. Un incidente prevenuto è una pull request in cui:

  • segnaliamo un potenziale rischio per la produzione
  • un engineer effettua il push di uno o più commit
  • una nuova valutazione conclude che il rischio potenziale è stato mitigato
  • la pull request viene sottoposta a merge
2.3
Incidenti in produzione prevenuti, per cliente, ogni settimana

Questo è già piuttosto significativo e ci aspettiamo che continui a crescere. Stiamo continuamente iterando ed eseguendo eval per migliorare le performance e la qualità del nostro agente.

Nessuno dovrebbe essere in on-call. Polylane osserva la tua infrastruttura, indaga e ripara ciò che si rompe.

Iscriviti

Continua a leggere