LLM come strumento di code review: integrazione in pipeline GitHub e GitLab
A settembre 2025 un responsabile tecnico di un'azienda del settore fintech mi ha chiesto di risolvere un problema che non era tecnico in senso stretto: il suo team di sei sviluppatori PHP produceva circa 15 pull request al giorno su un monorepo Laravel, e lui era l'unico senior reviewer. Ogni PR aspettava in media 6 ore prima di ricevere la prima review, e nel 30% dei casi la review trovava errori che avrebbero potuto essere intercettati automaticamente - query SQL con concatenazione di stringhe invece di prepared statement, validazione mancante sui parametri di input, pattern N+1 su relazioni Eloquent, credenziali hard-coded nei file di configurazione, e endpoint API senza rate limiting. Ogni volta che un bug di sicurezza o di performance passava la review e arrivava in produzione, il costo di remediation era 5-10 volte superiore a quello che sarebbe stato correggerlo in fase di PR. La proposta: integrare un LLM nelle pipeline CI/CD per fare un primo passaggio automatico di code review, lasciando al senior il tempo di concentrarsi sull'architettura e sulle decisioni di design invece che sulla caccia ai bug ovvi.
Ho implementato l'integrazione con l'API di Claude di Anthropic su tre repository GitHub, usando GitHub Actions come orchestratore. Il bot analizza il diff di ogni PR, identifica potenziali problemi di sicurezza, performance e stile, e lascia commenti inline direttamente sul codice con spiegazione del problema e suggerimento di fix. Dopo tre mesi di utilizzo, il numero di bug che raggiungeva la produzione è sceso del 40%, il tempo medio di review del senior è sceso da 45 minuti a 20 minuti per PR (perché i problemi meccanici erano già segnalati), e il team ha iniziato a produrre codice migliore perché il feedback immediato del bot fungeva da formazione continua.
Come funziona un bot di code review basato su LLM in una pipeline CI/CD?
Il flusso è questo: uno sviluppatore apre una PR su GitHub, la GitHub Action si attiva, estrae il diff della PR, lo invia all'API Claude con un prompt di sistema che specifica le regole di review (stile del progetto, pattern da cercare, soglia di severità), riceve la risposta con i commenti strutturati, e posta ogni commento direttamente sulla riga di codice corrispondente nella PR tramite l'API GitHub. Lo sviluppatore vede i commenti del bot insieme a quelli dei colleghi umani - l'unica differenza è il nome utente che li ha postati.
Il componente critico è il prompt di sistema. Un LLM generico senza istruzioni specifiche produrrà review generiche e spesso irrilevanti ("considera di aggiungere commenti al codice", "il nome della variabile potrebbe essere più descrittivo"). Il prompt che uso è calibrato sulle regole del progetto e include esempi concreti di pattern da cercare:
# .github/workflows/llm-review.yml
name: LLM Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Genera diff della PR
run: |
git diff origin/${{ github.base_ref }}...HEAD \
--unified=5 --diff-filter=ACMR \
-- '*.php' > /tmp/pr-diff.txt
- name: Analisi LLM del diff
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
python3 .github/scripts/llm-review.py \
--diff /tmp/pr-diff.txt \
--pr ${{ github.event.pull_request.number }} \
--repo ${{ github.repository }}Lo script Python llm-review.py è il cuore del sistema. Legge il diff, lo divide in chunk per file (per rispettare il limite di contesto del modello), invia ogni chunk all'API Claude con il prompt di review, e parsa la risposta JSON strutturata per postare i commenti sulla PR. Il prompt di sistema include regole specifiche come:
Sei un senior PHP developer che fa code review. Analizza SOLO il codice
modificato nel diff. Segnala SOLO problemi concreti, non suggerimenti
stilistici. Categorie di problemi da cercare:
SECURITY (critico):
- Query SQL con concatenazione di stringhe (usa prepared statements)
- Input utente usato senza validazione o sanitizzazione
- Credenziali, token o chiavi API hard-coded nel codice
- File upload senza validazione MIME type e dimensione
PERFORMANCE (alto):
- Pattern N+1 su relazioni Eloquent (manca with() o load())
- Query dentro loop senza cache o batch
- Caricamento di collezioni intere quando serve un count o exists
BUG (alto):
- Confronto con == invece di === su tipi misti
- Return type mancante su metodi pubblici
- Catch generico che sopprime eccezioni senza logging
Per ogni problema, rispondi con JSON:
{"file": "path", "line": N, "severity": "SECURITY|PERFORMANCE|BUG",
"message": "Descrizione del problema", "suggestion": "Come fixarlo"}La specificità del prompt è ciò che rende la review utile invece che rumorosa. Un prompt generico ("trova i problemi in questo codice") produce 20 commenti per file, la maggior parte irrilevanti. Il prompt calibrato produce 1-3 commenti per file, quasi tutti azionabili. Nel mio profilo professionale trovi il dettaglio dell'esperienza che porto nell'automazione AI dei processi di sviluppo - l'integrazione di LLM nei workflow di team è una delle aree dove la differenza tra un'implementazione che il team adotta e una che il team disabilita dopo una settimana sta nella calibrazione del prompt e nella gestione dei falsi positivi.
Gestione dei falsi positivi: il problema più sottovalutato
Il rischio principale di un bot di code review non è che manchi dei bug - è che segnali troppi falsi positivi. Se il bot produce 10 commenti per PR di cui 7 sono irrilevanti, il team inizia a ignorare tutti i commenti del bot, compresi i 3 che erano legittimi. Questo è il pattern "cry wolf" che ho visto distruggere l'adozione di ogni strumento di analisi statica mal configurato, dai linter ai SAST tool.
La mia strategia per contenere i falsi positivi si basa su tre meccanismi. Il primo è il filtro di severità: il bot posta commenti inline solo per problemi di severità SECURITY e PERFORMANCE. I problemi di severità BUG vengono aggregati in un singolo commento riassuntivo nella PR, che lo sviluppatore può leggere o ignorare senza inquinare il diff. Il secondo è il feedback loop: ho aggiunto un sistema di reazioni emoji - se lo sviluppatore mette ? su un commento del bot, il commento viene loggato come falso positivo e il prompt viene aggiornato settimanalmente per escludere i pattern che generano falsi positivi ricorrenti. Il terzo è l'esclusione esplicita: file di migrazione, file di configurazione, e file generati automaticamente vengono esclusi dall'analisi perché producono solo rumore.
Dopo tre mesi di calibrazione iterativa, il tasso di falsi positivi è sceso dal 60% iniziale (le prime due settimane erano sperimentali) al 15% - un livello che il team considera accettabile perché il 85% dei commenti del bot sono azionabili e risparmiano tempo. Il costo dell'API Claude per il volume di review del team (circa 450 PR al mese, con un diff medio di 200 righe) è di circa 35 euro al mese - meno di un'ora di lavoro di un senior developer, che è esattamente il tempo che il bot risparmia su ogni singola PR.
Il codice del reviewer: come postare commenti inline via API GitHub
Il componente tecnico più delicato è la mappatura tra le righe del diff e le righe del file nella PR. L'API GitHub per i commenti inline sulle PR richiede che specifichi la posizione nel diff, non la riga assoluta nel file - e la conversione non è banale quando il diff contiene sia righe aggiunte che righe rimosse:
# Estratto da .github/scripts/llm-review.py
# Posta un commento inline sulla PR via API GitHub
import requests
def post_review_comment(repo, pr_number, file_path, line, body, token):
"""Posta un commento inline su una specifica riga della PR."""
url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}/comments"
# Il commento deve riferirsi al commit HEAD della PR
response = requests.post(url, headers={
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github.v3+json",
}, json={
"body": f"? **LLM Review** - {body}",
"commit_id": get_pr_head_sha(repo, pr_number, token),
"path": file_path,
"line": line,
"side": "RIGHT", # Commenta sulla versione nuova del file
})
if response.status_code != 201:
print(f"Errore posting commento: {response.json()}")Costi, modelli e scelta dell'LLM
La scelta del modello influenza direttamente la qualità della review e il costo operativo. Ho testato tre modelli sui diff del team fintech durante il primo mese di sperimentazione: Claude Sonnet (veloce, economico, buona qualità per pattern di sicurezza), Claude Opus (più lento, più costoso, migliore nel ragionamento architetturale), e GPT-4o (prestazioni comparabili a Sonnet, costo simile). Il modello che ho scelto per la produzione è Claude Sonnet 4.6 per la review automatica di ogni PR e Claude Opus 4.6 per le review on-demand su PR complesse che il senior vuole approfondire. Il costo operativo con questa configurazione è di circa 35 euro al mese per 450 PR mensili - un costo che il team ha accettato senza discussione quando ho mostrato che equivale a 40 minuti di lavoro del senior developer, mentre il bot ne risparmia 10-15 ore al mese.
Un aspetto che molti trascurano è il prompt caching. L'API Claude supporta il caching del prompt di sistema: la prima chiamata con un prompt lungo paga il costo pieno dei token, ma le chiamate successive con lo stesso prompt utilizzano la cache e costano significativamente meno. Per un workflow di code review dove il prompt di sistema è identico per tutte le PR dello stesso repository, il prompt caching riduce il costo per review di circa il 60% dopo la prima chiamata della giornata. Questa ottimizzazione da sola ha portato il costo mensile da 85 euro iniziali (senza caching) ai 35 euro attuali, senza cambiare modello né ridurre la qualità della review.
La scelta tra API cloud e modelli locali (Ollama, vLLM) è un'altra decisione che dipende dal contesto. Per il team fintech, i dati nel diff non sono classificati come riservati (sono diff di codice, non dati dei clienti), quindi l'API cloud è accettabile. Per un team che lavora su codice con segreti embedded o su repository con proprietà intellettuale sensibile, un modello locale su GPU interna è la scelta appropriata - ma richiede hardware dedicato (minimo 16 GB VRAM per un modello da 7-13B parametri) e una latenza di risposta più alta.
Limiti e responsabilità: cosa il bot non può fare
Il bot non sostituisce la review umana - lo ripeto perché è un punto su cui i manager hanno aspettative sbagliate. Un LLM può identificare pattern sintattici problematici (SQL injection, N+1, input non validato) con alta affidabilità, ma non può valutare se un'architettura è appropriata per il caso d'uso, se un nome di variabile comunica correttamente l'intento del codice, se un refactoring introduce una regressione funzionale, o se una decisione di design è coerente con la roadmap del prodotto. Queste sono valutazioni che richiedono contesto di business, conoscenza della storia del progetto e giudizio umano - capacità che nessun LLM possiede.
Il modello che funziona, verificato su tre team per tre mesi, è a due livelli: il bot intercetta i problemi meccanici (sicurezza, performance, bug di pattern), il senior reviewer si concentra sull'architettura e sul design. Il risultato netto è che il senior spende il 55% del tempo in meno sulla review senza che la qualità cali - anzi, la qualità è salita perché il senior ha più tempo per pensare alle cose che contano. Ho applicato lo stesso principio di automazione AI nella mia infrastruttura di pipeline editoriale e nei workflow di testing, dove l'LLM gestisce la parte ripetitiva e l'umano gestisce il giudizio. Se vuoi implementare un bot di code review AI nel tuo team di sviluppo, contattami per una sessione di setup: in una giornata configuriamo la GitHub Action, calibriamo il prompt sulle regole del tuo progetto, e facciamo un primo ciclo di test su 5-10 PR reali per validare il tasso di falsi positivi prima del rollout al team.