Prompt engineering avanzato per sviluppatori: pattern per task tecnici specifici

Prompt engineering avanzato per sviluppatori: pattern per task tecnici specifici

Uso LLM quotidianamente per il mio lavoro tecnico dal gennaio 2024 - non come curiosità o esperimento, ma come strumento di produzione integrato nel mio workflow. In 18 mesi di utilizzo intensivo con Claude e GPT-4 su task come refactoring di classi PHP, generazione di test, analisi di log, scrittura di query SQL complesse, review di codice e generazione di documentazione, ho accumulato una libreria di pattern di prompting che producono risultati consistenti e di qualità. La differenza tra un prompt che produce output utilizzabile e un prompt che produce spazzatura non è nella sofisticazione delle parole - è nella struttura del contesto che fornisci al modello e nella precisione dei vincoli che imponi all'output.

Il mito del "basta chiedere in modo chiaro" è il primo ostacolo che supero con i team che inizio ad affiancare nell'adozione degli LLM. Un prompt come "refactora questa classe PHP per renderla migliore" produce un output generico e quasi mai utilizzabile perché "migliore" non è un vincolo - è un desiderio. Un prompt che specifica "estrai le tre responsabilità di questa classe in tre service separati seguendo SRP, mantieni i metodi pubblici come interfaccia, usa constructor injection per le dipendenze, e produci i test unitari per ogni service con almeno 3 casi di test per metodo" produce output che posso copiare nel progetto con modifiche minime. La differenza è che il secondo prompt trasforma un problema aperto in un task strutturato con vincoli espliciti - e i vincoli espliciti sono ciò che permette all'LLM di produrre output preciso.

Quali pattern di prompting producono risultati consistenti per task tecnici PHP?

I pattern che ho catalogato si dividono in cinque categorie, ciascuna ottimizzata per un tipo specifico di task tecnico. Non sono tecniche universali - sono pattern calibrati sulla mia esperienza con codice PHP, infrastruttura Linux e automazione DevOps. Lo sviluppatore JavaScript o il data scientist avranno pattern diversi per i loro task. La specificità è ciò che rende un pattern utile: un pattern "generico" che funziona per tutto non funziona davvero per niente.

Pattern 1: Context-First per il refactoring. Il refactoring è il task dove l'LLM produce i risultati migliori quando riceve il contesto completo della classe da refactorare - non solo il codice, ma anche le interfacce che implementa, le dipendenze iniettate, e un esempio di come la classe viene usata nel controller o nel service che la chiama. Il prompt che uso segue questa struttura: prima il ruolo ("Sei un senior PHP developer che applica SOLID"), poi il contesto (la classe completa, le interfacce, un esempio di utilizzo), poi il task specifico ("estrai le responsabilità di logging e di validazione in service separati"), poi i vincoli ("mantieni backward compatibility sui metodi pubblici, usa constructor injection, non modificare i test esistenti"), e infine il formato di output ("restituisci ogni nuovo service come file PHP separato con namespace completo"). Nel mio profilo professionale trovi il dettaglio dell'esperienza nella produzione AI-assistita che porto in questi workflow - la qualità del prompt è il fattore che determina se l'LLM è un moltiplicatore di produttività o un generatore di debito tecnico.

Pattern 2: Few-Shot per la generazione di test. La generazione di test è il task dove il few-shot learning - fornire 2-3 esempi del risultato desiderato prima di chiedere il task - produce il miglioramento più drammatico nella qualità dell'output. Senza esempi, l'LLM genera test con naming inconsistente, struttura variabile e stile che non corrisponde al progetto. Con 2 esempi di test dallo stesso progetto, l'LLM replica lo stile, il naming, il framework (PHPUnit vs Pest), e le convenzioni di assertion del progetto. Il prompt è: "Ecco due test esistenti del progetto [incolla test 1 e test 2]. Genera i test per la seguente classe seguendo lo stesso stile, naming e struttura dei test di esempio." Ho descritto in dettaglio il processo nel mio articolo sulla generazione di test automatici con LLM, dove i few-shot examples sono il fattore che porta il tasso di test corretti al primo tentativo dal 50% all'80%.

Pattern 3: Chain of Thought per l'analisi di log. L'analisi di un log di errore o di un incidente di sicurezza richiede ragionamento sequenziale: prima identifica l'errore, poi traccia la causa, poi valuta l'impatto, poi proponi la fix. Se chiedi all'LLM di fare tutto in un colpo ("analizza questo log e dimmi cosa è successo"), produce una risposta generica che salta passaggi logici. Se chiedi di ragionare passo per passo ("Step 1: identifica tutte le righe con errore o warning nel log. Step 2: per ogni errore, identifica il timestamp e il contesto. Step 3: ordina gli errori cronologicamente e identifica il primo errore che ha causato la cascata successiva. Step 4: proponi la causa root basandoti sulla sequenza di errori"), l'output è sistematico, completo e verificabile - puoi controllare ogni passaggio del ragionamento.

Pattern 4: Structured Output per la documentazione. Quando chiedo all'LLM di generare documentazione (README, API docs, runbook operativi), il formato dell'output è fondamentale per l'utilità del risultato. Il pattern è specificare la struttura esatta del documento nel prompt: "Genera un README con le seguenti sezioni: 1) Descrizione (2-3 frasi), 2) Requisiti (lista puntata con versioni), 3) Installazione (comandi bash), 4) Configurazione (tabella con parametro, tipo, default, descrizione), 5) Utilizzo (3 esempi con codice), 6) Troubleshooting (FAQ con problema e soluzione)." Senza questa struttura esplicita, l'LLM genera un documento con sezioni arbitrarie, in ordine arbitrario, con livello di dettaglio inconsistente.

Pattern 5: Adversarial per la review di sicurezza. Per la review di sicurezza del codice, il pattern più efficace è il role-playing adversariale: "Sei un penetration tester che analizza questo endpoint PHP. Il tuo obiettivo è trovare vulnerabilità sfruttabili. Per ogni vulnerabilità trovata, descrivi: il tipo (OWASP category), il vettore di attacco (come la sfrutteresti), il payload di esempio, la severity (CVSS score approssimativo), e la remediation suggerita. Non segnalare best practice mancanti - solo vulnerabilità concretamente sfruttabili." Questo prompt produce risultati drasticamente superiori al generico "trova i problemi di sicurezza in questo codice" perché forza l'LLM a pensare come un attaccante, non come un linter - e le vulnerabilità che trova sono quelle che un attaccante reale sfrutterebbe, non quelle che un tool automatico segnalerebbe come "possibili."

Il prompt template che uso ogni giorno per il refactoring PHP

Il template è strutturato in cinque sezioni che compilo ogni volta che chiedo all'LLM un refactoring di codice - dalla riscrittura di una singola funzione alla ristrutturazione di un intero service layer:

La prima sezione è il ruolo e il contesto del progetto: "Sei un senior PHP 8.3 developer che lavora su un'applicazione [Laravel/Symfony] con [architettura: monolite/microservizi]. Il progetto segue [PSR-12, strict types, constructor injection]. Il database è [MySQL/PostgreSQL] con [Eloquent/Doctrine]." Questo contesto elimina le risposte con PHP 5.6-style code, con array helper functions invece di collection, o con pattern che non si applicano al framework.

La seconda sezione è il codice da refactorare: il codice completo, incluse le dipendenze e un esempio di utilizzo. Non abbreviare, non parafrasare - l'LLM lavora meglio con il codice reale completo che con una descrizione del codice.

La terza sezione sono i vincoli specifici: cosa non deve cambiare (la firma dei metodi pubblici, il nome della classe, il formato della risposta), cosa deve essere mantenuto (i test esistenti devono continuare a passare), e quali pattern evitare (non usare static methods, non aggiungere abstract base class, non introdurre trait per condividere codice).

La quarta sezione è il formato di output: "Restituisci ogni file come blocco di codice separato con il path completo nel commento iniziale. Non includere spiegazioni tra i blocchi di codice - solo codice. Aggiungi un blocco finale 'Migration steps' con i comandi da eseguire per applicare il refactoring."

La quinta sezione è la validazione: "Prima di restituire il codice, verifica mentalmente che: tutti i tipi di ritorno sono dichiarati, tutti i parametri sono tipizzati, non ci sono dipendenze circolari tra le nuove classi, e il codice compila senza errori su PHP 8.3 strict."

Questo template, che ho affinato in 18 mesi di utilizzo quotidiano, produce output che posso integrare nel progetto con meno di 10 minuti di adattamento nel 75% dei casi. Il 25% restante richiede correzioni che tipicamente riguardano la logica di business che l'LLM non poteva conoscere dal solo codice - ma il boilerplate strutturale è sempre corretto. Ho documentato un approccio complementare nell'articolo sulla code review automatizzata con LLM nelle pipeline CI/CD, dove il prompt è calibrato per la review anziché per la generazione - ma la struttura (ruolo, contesto, vincoli, formato) è identica.

Il prompt caching e il costo operativo: ottimizzare l'uso dell'API

Un aspetto pratico che i tutorial trascurano è il costo operativo dell'uso intensivo di LLM per il lavoro tecnico. Quando invii un prompt lungo (2.000-5.000 token di contesto con codice, esempi e vincoli) per ogni task di refactoring o generazione, il costo si accumula rapidamente. Con Claude Sonnet 4.6 a 3 dollari per milione di token di input, un workflow intensivo con 50-80 prompt al giorno produce un costo di 5-15 euro al giorno - sostenibile per un professionista, ma significativo per un team di 5 sviluppatori.

La soluzione che uso è il prompt caching: la maggior parte del prompt (il ruolo, il contesto del progetto, i vincoli standard, gli esempi di stile) è identica tra le chiamate. Con l'API Claude che supporta il prompt caching nativo, il prompt di sistema viene inviato una volta e riutilizzato per le chiamate successive - riducendo il costo dei token di input del 60-70% dopo la prima chiamata della sessione. Per un team che fa 200+ prompt al giorno condividendo lo stesso contesto di progetto, il risparmio è di 300-500 euro al mese rispetto all'invio del contesto completo ad ogni chiamata.

Un altro pattern di ottimizzazione è la scelta del modello in base alla complessità del task. Non tutti i task richiedono il modello più potente: per la generazione di boilerplate, la conversione di formati e la documentazione di codice semplice, Claude Haiku (0,80 dollari per milione di token) produce risultati equivalenti a Sonnet a un quarto del costo. Riservo Sonnet per il refactoring complesso, la review di sicurezza e i task che richiedono ragionamento architetturale - dove la differenza di qualità tra i modelli è misurabile. Questo model routing basato sulla complessità del task è lo stesso approccio che ho descritto nel mio articolo sul monitoring dei costi LLM in produzione, applicato al workflow individuale invece che alla pipeline automatizzata.

Quando il prompt engineering non basta: i limiti che ho imparato

Dopo 18 mesi, ho una visione chiara di cosa l'LLM fa bene e cosa no, indipendentemente dalla qualità del prompt. L'LLM eccelle nei task con pattern ripetitivo e alta prevedibilità: refactoring strutturale, generazione di test, conversione di formati, scrittura di boilerplate, documentazione di codice esistente. L'LLM fallisce nei task che richiedono contesto non presente nel prompt: decisioni architetturali che dipendono dalla storia del progetto, ottimizzazione di performance che dipende dal profilo di carico reale, scelte di naming che dipendono dalle convenzioni non scritte del team, e qualsiasi ragionamento che richiede di capire il perché di una scelta di design, non solo il come.

Il rischio più grande non è che l'LLM produca codice sbagliato (quello lo correggi in review) - è che l'LLM produca codice plausibilmente corretto ma architetturalmente sbagliato: codice che compila, che passa i test, ma che introduce un pattern che non si adatta al progetto o che crea una dipendenza che diventerà un problema tra sei mesi. Questo tipo di errore è invisibile ai tool automatici e richiede un senior developer che faccia review con comprensione del contesto - il che ci riporta al punto di partenza: l'LLM è un moltiplicatore per chi sa già cosa vuole, non un sostituto per chi non lo sa.

Se stai iniziando a integrare LLM nel tuo workflow di sviluppo e vuoi accelerare la curva di apprendimento evitando gli errori che ho fatto nei primi mesi, contattami per una sessione di coaching: in mezza giornata ti mostro i pattern di prompting calibrati per il tuo stack tecnologico, configuriamo insieme il workflow con Claude Code o API, e definiamo i guardrail per evitare che l'output dell'LLM introduca debito tecnico nel tuo progetto.

Ultima modifica: