Code generation con LLM: limiti reali e dove l'AI non arriva ancora nel 2025
Nel biennio 2024-2026 ho usato sistematicamente LLM per generazione codice su progetti PHP di produzione - oltre 1.500 task documentati fra refactoring, nuove feature, debugging, test, documentazione - su sei codebase clienti diverse per dimensione (da piccoli sistemi Laravel sotto le 20.000 righe fino a monoliti di oltre 200.000 righe) e per complessità (da semplici CRUD gestionali fino a sistemi distribuiti con microservizi e integrazioni B2B). L'esperienza accumulata mi ha dato una visione concreta e onesta di dove gli LLM eccellono, dove falliscono, e soprattutto dove falliscono in modi che un developer inesperto non riconosce. Questo articolo non è un elenco generico di "pro e contro dell'AI" - è una mappa dettagliata dei pattern reali osservati, con esempi specifici, e con una prospettiva che contesta alcuni hype prevalenti nel 2025-2026.
La tesi di fondo che voglio articolare: gli LLM stanno trasformando radicalmente la produttività del developer senior, ma la narrativa mainstream sui loro "limiti" è spesso superficiale. Il problema non è che "l'AI non capisce il contesto" - in molti casi la capisce benissimo. Il problema è più sottile: l'AI produce output che sembra corretto e che passa review superficiali, ma nasconde assumption implicite sbagliate in modi difficili da individuare. La mia esperienza pratica indica che il 5-10% del codice generato da LLM contiene bug che richiedono intervento manuale, il 2-3% contiene bug che potrebbero arrivare in produzione senza essere rilevati da test unitari standard, e meno dell'1% contiene bug catastrofici (security-critical, data-loss-critical). Il rate "meno dell'1%" sembra piccolo, ma su 1.000 task all'anno significa 10 bug potenzialmente gravi che solo la disciplina di review può catturare.
Dove gli LLM eccellono davvero: i task di alta frequenza e bassa complessità architetturale
Parto dal positivo perché è importante calibrare correttamente le aspettative. Gli LLM nel 2026 sono eccezionalmente bravi in cinque categorie di task.
Prima categoria - generazione di boilerplate strutturato. Creazione di CRUD standard Laravel (controller + model + migration + form request + resource + feature test), scaffolding di API REST, implementazione di pattern CQRS con command handler, generazione di factory Pest per dataset tipici. Tutti questi sono task dove la struttura è prevedibile, le convenzioni sono note, e il modello applica consistentemente pattern che ha visto migliaia di volte nel training. Il tempo risparmiato è 70-85% rispetto al lavoro manuale.
Seconda categoria - refactoring locale e sintattico. Estrazione di metodi, rinomina di variabili con consistenza cross-file, conversione da un pattern sintattico a un altro (array access a null coalescing, chained conditions a match expressions, callback a arrow function), application di static analysis hints. Il modello è efficace perché i refactoring di questo tipo sono trasformazioni locali che non richiedono comprensione architetturale profonda.
Terza categoria - traduzione fra linguaggi o framework. Portare una classe da un framework a un altro (da Symfony controller a Laravel controller, da Express a Laravel API), convertire codice PHP da un'API legacy a una moderna (da funzioni procedurali a metodi di classe), tradurre logica da un linguaggio a un altro. Qui il modello vince perché ha visto entrambi i contesti nel training e riesce a mappare le corrispondenze strutturali.
Quarta categoria - spiegazione di codice esistente. "Spiegami cosa fa questa funzione" è task dove l'LLM può produrre documentazione comprensibile partendo da codice più o meno comprensibile. Questo è particolarmente utile su codebase legacy ereditato senza documentazione - il pattern descritto nel mio articolo sull'automazione della documentazione tecnica con LLM da codice a wiki aziendale.
Quinta categoria - scrittura di test per codice esistente. Quando il codice esiste e funziona, generare test che lo coprono è task con struttura prevedibile: leggi la funzione, identifica input/output, costruisci casi. Il modello è efficace, soprattutto se guidato a coprire edge case specifici.
In queste cinque categorie, che coprono circa il 50-60% del lavoro quotidiano di un developer senior su progetto maturo, gli LLM producono beneficio netto misurabile. Nei progetti dei miei clienti, l'adozione disciplinata ha ridotto il tempo di sviluppo complessivo del 30-40% senza degradare qualità.
Dove gli LLM falliscono sistematicamente: il ragionamento architetturale
Il pattern di fallimento più importante da capire è il ragionamento architetturale a livello di sistema. Gli LLM sono fondamentalmente strumenti di completamento probabilistico - predicono il token successivo basandosi sul contesto precedente. Questo funziona benissimo per task locali dove il contesto rilevante sta in poche centinaia di righe. Fallisce quando il task richiede di ragionare sull'interazione fra componenti distribuiti nella codebase, sulle conseguenze a lungo termine di una decisione, sui trade-off fra proprietà di sistema che non sono presenti nel contesto immediato.
Esempio concreto dal lavoro recente. Un cliente aveva una classe OrderProcessor che chiamava direttamente Stripe::charge($amount) dentro una transazione database Laravel. Ho chiesto a Claude di "refactor this to handle payment failures gracefully". Il modello ha prodotto una soluzione tecnicamente corretta a livello locale: ha wrappato il charge in try/catch, ha aggiunto logging, ha gestito gli errori specifici di Stripe. Tecnicamente migliore del codice iniziale.
Ma. Non ha considerato il problema architetturale fondamentale: un charge verso Stripe dentro una transazione database è un pattern strutturalmente rotto, perché se la transazione DB rollback dopo il charge, il cliente è addebitato ma l'ordine non è salvato. Il fix corretto non è migliorare l'error handling del charge - è spostare il charge fuori dalla transazione, implementare outbox pattern, o usare two-phase commit con sistema di riconciliazione. Il modello non ha colto questo perché il "problema" architetturale richiede visione sulle conseguenze sistemiche dell'interazione fra database transaction e chiamata HTTP esterna - visione che emerge dall'esperienza operativa di aver gestito quel tipo di bug in produzione, non dal training su codice pubblico.
Questo pattern - "l'LLM risolve il problema che gli mostri, senza vedere il problema più grande che sta dietro" - è la trappola più insidiosa dell'uso non supervisionato. Un junior che chiede "refactor this" si fida del risultato, mergeia, e il pattern architetturale rotto continua a vivere in codebase per anni.
Dove l'LLM "dimentica" il contesto: la coerenza a lungo termine
Il secondo grande pattern di fallimento è la coerenza a lungo termine cross-session. Un developer umano che lavora per una settimana su un progetto accumula contesto: ricorda le decisioni prese martedì, le applica consistentemente giovedì, nota quando un suggerimento nuovo è inconsistente con una scelta precedente. L'LLM tecnicamente non ha questa memoria - ogni sessione di Claude Code è indipendente, ogni prompt è valutato contro il contesto immediato fornito.
Questo produce un pattern di fallimento subdolo: chiedi all'LLM di modificare il modulo X, lui propone implementazione Y. Due settimane dopo chiedi di modificare il modulo Z (che interagisce con X), lui propone implementazione W che è inconsistente con Y. Entrambe Y e W sono internamente ragionevoli, ma insieme rompono assumption che un umano avrebbe mantenuto consistenti. Il risultato è entropia architetturale - il sistema diventa gradualmente più caotico perché ogni decisione individuale è locale.
La difesa è materializzare il contesto persistente in artefatti scritti che l'LLM legge a ogni sessione. Il pattern CLAUDE.md al root del progetto è una forma di questa tecnica, ma scala solo fino a certe dimensioni. Per progetti complessi, l'approccio che ho adottato è di mantenere ADR (Architecture Decision Records) versionati e di referenziarli esplicitamente nei prompt: "considerato ADR-007 che stabilisce che tutti i charge Stripe devono essere fuori da transazioni database, implementa...". Senza questa disciplina, l'LLM produce decisioni locali corrette ma globalmente inconsistenti.
Dove l'LLM inventa dati o metodi: il pattern dell'hallucination plausibile
Il pattern di fallimento più insidioso è quello dell'hallucination plausibile. Il modello genera codice sintatticamente corretto che sembra fare sense ma che chiama metodi inesistenti, usa firme non reali di API esterne, o inventa comportamenti di librerie che nella realtà non esistono. Questo pattern è insidioso perché l'output non è "sbagliato" in senso ovvio - è sbagliato in senso sottile che solo la verifica puntuale cattura.
Esempio reale: ho chiesto di integrare una libreria di parsing XML specifica italiana per la fatturazione elettronica. Il modello ha prodotto codice che chiamava metodi della libreria con firme che sembravano plausibili (Parser::parseFattura($xml)->getTotale()), ma quei metodi specifici non esistevano nella libreria reale - il parser usava una struttura DOM navigabile con XPath che richiedeva codice più verboso. Il codice generato "compilava" in senso PHP (PHP è permissivo) ma avrebbe fallito al runtime con "Call to undefined method".
La difesa è duplice. Prima: non accettare mai codice che chiama metodi di librerie esterne senza verificare l'esistenza effettiva. PHPStan a livello 9 cattura buona parte di questi problemi se la libreria ha stub corretti, ma non tutti - quindi review umana attenta su ogni chiamata di metodo "non ovvio" rimane necessaria. Seconda: guidare il modello con estratti reali della documentazione quando si lavora con librerie meno mainstream. Il prompt "usa esattamente questi metodi della libreria X, con queste firme" elimina lo spazio di hallucination.
Stai cercando un Consulente Informatico esperto per introdurre uso disciplinato degli LLM nello sviluppo del tuo team PHP, con guardrail che evitano le trappole più insidiose senza limitare i benefici produttivi dell'AI? Nel mio profilo professionale trovi l'esperienza concreta su workflow AI-assisted per senior developer e formazione di team su pattern operativi di Claude Code e API LLM.
Security: l'area dove i limiti sono più critici e meno ammessi
La security è l'area dove preferisco applicare la disciplina più rigorosa e dove l'hype dei vendor AI è più fuorviante. I pattern di fallimento specifici dell'LLM in contesto security sono tre.
Autenticazione e autorizzazione con pattern "sicuri di default". Quando chiedo di implementare autenticazione, il modello produce codice che sembra sicuro (usa bcrypt, segue pattern Laravel standard, include MFA come opzione) ma può introdurre vulnerabilità specifiche del contesto. Esempio: un endpoint di "password reset" generato dal modello includeva un token random ma con length di 16 caratteri invece di 32, e TTL di 24 ore invece di 15 minuti. Tecnicamente funziona, passa test standard, ma è significativamente più brute-forceable di quello che la community security considera sicuro nel 2026. Il problema è che la valutazione di "quanto è sicuro abbastanza" richiede contesto operativo (chi è l'attaccante modello, quali risorse ha, qual è la superficie di attacco) che il modello non ha.
Crittografia applicata in modo sottilmente sbagliato. Il modello genera codice che usa primitives crittografiche corrette (AES-256-GCM, HMAC-SHA256) ma può sbagliare nell'uso: IV riutilizzati, key derivation con salt mancante, modalità CBC quando dovrebbe essere GCM. Ogni singolo errore è sottile e non emerge dai test funzionali - emerge solo da un audit di sicurezza mirato. Per questa categoria, la mia disciplina è mai delegare crittografia all'LLM senza review da parte di qualcuno con specializzazione specifica.
Sanitization incompleta di input user-provided. Il modello genera codice che valida e sanifica input, ma può saltare vettori specifici (es: sanitize contro XSS ma dimentica la tag attribute injection, valida email ma non CRLF injection, escape SQL ma dimentica LDAP injection in un contesto che lo richiede). Il pattern di analisi di sicurezza applicativa OWASP per applicazioni Laravel con le vulnerabilità tipiche e le remediation pratiche è il complemento necessario - l'LLM produce il codice, il pentest con Burp Suite verifica se le protezioni sono complete.
Il mito dell'"AI che sostituisce il developer" è solo un'esagerazione di vendor
Una narrativa ricorrente nel 2025-2026 è che gli LLM stiano per sostituire i developer entry-level o addirittura mid-level. Questa narrativa è principalmente marketing dei vendor AI e non corrisponde alla mia esperienza operativa. Il ruolo che vedo cambiare è amplificazione di developer con contesto adeguato, non sostituzione.
Il developer che cresce in un mondo AI-assisted impara comunque a leggere codice, a progettare architetture, a debuggare sistemi distribuiti, a comunicare con il business. Le skill fondamentali non cambiano - cambia il ritmo con cui diventano produttive. Un junior competente che usa bene l'AI raggiunge contribuzioni da mid-level in 12 mesi invece di 24. Un mid-level raggiunge senior in 3-4 anni invece di 6-7. Ma senza le skill fondamentali, l'AI non produce un senior "artificiale" - produce un generatore di codice sbagliato che nessuno capisce abbastanza da correggere.
Il caso concreto più eloquente viene da un cliente dove un developer junior aveva mergeato in produzione 40 PR in due settimane usando Claude Code senza review adeguata. Il codice funzionava in maggioranza, ma nei sei mesi successivi 14 di quei 40 PR si sono rivelate contenere bug che emergono solo in edge case - il dev era incapace di diagnosticarli perché non capiva davvero il codice che aveva mergeato. Il cliente ha dovuto assumere me per rivedere sistematicamente quei 40 PR, operazione che ha richiesto più tempo di quello che sarebbe servito a scriverli bene la prima volta. L'AI senza supervisione esperta è un moltiplicatore di debito, non di produttività.
La questione del contesto di training: dati del 2023-2024 su problemi del 2026
Un limite meno discusso ma molto pratico è la deriva temporale del training. Gli LLM correnti (inclusi i migliori come Claude Sonnet 4.5 e GPT-5) sono addestrati su dati tipicamente vecchi di 6-18 mesi. Questo significa che su framework e librerie a evoluzione rapida (Laravel, Symfony, React, Vue), il modello conosce versioni e API non più correnti.
Esempio concreto: Laravel 11 (rilasciato 2024) ha introdotto cambiamenti nella struttura dei service provider che rendono molti pattern di Laravel 10 obsoleti. Un LLM addestrato nel 2024 e non aggiornato genererà codice in stile Laravel 10 anche se gli chiedi di lavorare su progetto Laravel 11. Il codice "funziona" (Laravel mantiene backwards compatibility), ma non è idiomatico della nuova versione e non sfrutta le feature recenti.
La difesa è di specificare esplicitamente la versione del framework nel prompt e, quando serve, fornire esempi del pattern corretto per quella versione. "Stai lavorando su Laravel 11. NON usare il pattern boot() nel service provider, usa register() e boot() separati come nella nuova convention" - questo tipo di istruzione elimina il ritardo di training e guida il modello verso pratiche correnti.
Le implicazioni per la formazione: cosa insegnare ai team nel 2026
Tutto questo ha implicazioni dirette sulla formazione di team di sviluppo. Il mio pattern di formazione, che applico sui clienti, ha quattro moduli.
Modulo 1 - "cosa l'LLM fa bene e cosa no". Una giornata di training dove mostro esempi concreti delle cinque categorie di eccellenza e delle cinque categorie di fallimento. L'obiettivo è calibrare le aspettative: il team impara a riconoscere i pattern di delega appropriati invece di "usarlo sempre" o "non usarlo mai".
Modulo 2 - "review disciplinata del codice AI-generated". Una mezza giornata sui checklist di review: per ogni categoria di output, cosa cercare, quali domande porsi, quali tool automatici integrare (PHPStan, composer audit, security linters). Il pattern del senior developer con sei abitudini operative che faccio adottare nelle code review PMI è il fondamento teorico.
Modulo 3 - "prompt engineering pragmatico". Una giornata sulla strutturazione dei prompt per massimizzare output utile: contesto preliminare, vincoli espliciti, esempi di output atteso, regole di fallimento. Non è "magia AI", è disciplina di comunicazione strutturata con uno strumento che interpreta naturale language.
Modulo 4 - "integrare AI nel workflow esistente". Mezza giornata sulla tool chain: Claude Code vs GitHub Copilot vs Cursor, hook pre-commit per verifica codice AI-generated, CLAUDE.md come memoria progetto, integrazione con MCP server custom. Il pattern è quello descritto nel mio articolo sulla costruzione di MCP server per integrare Claude con strumenti aziendali.
La prospettiva onesta: dove saremo nei prossimi 12-24 mesi
L'area degli LLM per code generation sta evolvendo rapidamente. La mia previsione realistica, basata sul trend osservato nei 18 mesi passati, è che nei prossimi 12-24 mesi vedremo miglioramenti significativi in tre aree: coerenza cross-session tramite memoria persistente dei modelli (già in sperimentazione); comprensione architettuale di progetti complessi tramite context window estesi e indexing semantico della codebase (tecniche già parzialmente disponibili); riduzione dell'hallucination per librerie specifiche tramite training mirato o fine-tuning per dominio.
Quello che NON cambierà nei prossimi 24 mesi è il requisito di supervisione competente. L'AI sarà più potente, più accurata, più economica - ma resterà uno strumento che amplifica quello che l'operatore umano sa fare. La disciplina di review, il giudizio architetturale, la comprensione del business, la gestione dei trade-off sistemici, la responsabilità etica e di security - tutte queste rimarranno competenze umane essenziali. Chi le sviluppa oggi sarà ben posizionato; chi le trascura pensando che "l'AI le coprirà domani" sta facendo un errore di previsione che pagherà in debito professionale.
Se gestisci un team di sviluppo e stai valutando come integrare LLM in modo strutturato nel flusso di lavoro, senza cadere nell'entusiasmo che produce codice peggiore né nella diffidenza che rinuncia ai benefici produttivi, contattami per una consulenza di workflow: in due giornate di lavoro analizzo il vostro contesto tecnico e organizzativo, identifico le categorie di task dove l'adozione LLM darebbe il massimo beneficio sul vostro progetto specifico, definisco i guardrail operativi calibrati sulla maturità del team, e formo i developer sui pattern di delega appropriata - con l'obiettivo di portare il team a produttività aumentata senza compromettere la qualità del codice, disciplina che distingue un uso professionale dell'AI da un cargo cult tecnologico.