Gli agenti AI sono potenti, ma senza direzione producono codice generico. La differenza tra un output mediocre e uno production-ready sta quasi sempre nell'input che gli dai.
In questo articolo ti mostro come uso le specifiche strutturate per guidare agenti come Claude Code, GitHub Copilot e Gemini CLI. Non teoria: workflow reali che applico nei miei progetti.
Il problema: AI senza contesto
Hai mai chiesto a un LLM "scrivi una API di autenticazione" e ricevuto qualcosa di generico, pieno di placeholder, che non si integra con nulla del tuo progetto?
Il problema non è l'AI. È che le hai dato un task senza contesto:
- Nessun vincolo architetturale
- Nessun pattern esistente da seguire
- Nessun criterio di accettazione
- Nessuna idea di cosa NON fare
Una specifica risolve tutto questo. Diventa il prompt di sistema per il tuo agente.
Architettura del workflow
Ecco il flusso che uso:
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ SPECIFICA │ ──► │ PIANO TASK │ ──► │ AI AGENT │
│ (markdown) │ │ (breakdown) │ │ (codegen) │
└─────────────┘ └──────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ VALIDAZIONE│
│ (test/review)│
└─────────────┘
Ogni step alimenta il successivo. La specifica genera il piano, il piano guida l'agente, l'output viene validato contro la specifica originale.
Step 1: Scrivere una specifica AI-friendly
Non tutte le specifiche sono uguali. Per funzionare bene con AI, una specifica deve essere:
- Strutturata: sezioni chiare, non prose infinite
- Esplicita: niente assunzioni implicite
- Testabile: ogni comportamento ha un criterio verificabile
- Contestualizzata: riferimenti al codebase esistente
Template che uso
# Feature: [Nome]
## Contesto
[Dove si inserisce nel sistema? Quali moduli tocca?]
## Obiettivo
[Perché esiste questa feature? Che problema risolve?]
## Comportamenti
### Scenario 1: [Nome scenario]
- Given: [stato iniziale]
- When: [azione utente/sistema]
- Then: [risultato atteso]
### Scenario 2: [Nome scenario]
...
## Vincoli tecnici
- [Vincolo 1]
- [Vincolo 2]
## Interfacce
### Input
[Formato dati in ingresso]
### Output
[Formato dati in uscita]
## Criteri di accettazione
- [ ] Criterio 1
- [ ] Criterio 2
## File coinvolti
- `src/services/auth.ts` - logica autenticazione
- `src/routes/api/login.ts` - endpoint esistente
- `src/models/user.ts` - modello utente
## Non-goal
- [Cosa NON implementare]
La sezione File coinvolti è cruciale: dice all'agente dove guardare e cosa rispettare.
Step 2: Feeding the Agent
Con Claude Code
Claude Code legge il contesto del progetto. Il mio prompt tipico:
Leggi la specifica in docs/specs/reset-password.md
Implementa la feature seguendo:
- I pattern esistenti in src/services/
- I vincoli elencati nella specifica
- I criteri di accettazione come test
Genera prima i test, poi l'implementazione.
Claude Code esplora il codebase, trova i pattern, e produce codice coerente.
Con GitHub Copilot
Copilot lavora meglio con contesto inline. Apro la specifica in un tab, e nel file di implementazione scrivo:
/**
* Reset Password Service
*
* Spec: docs/specs/reset-password.md
*
* Behaviors:
* - Generate time-limited reset token (1h expiry)
* - Rate limit: 3 requests per email per 15 min
* - Invalidate token after use
*/
Copilot usa questi commenti come guida per i suggerimenti.
Con Gemini CLI
Gemini CLI accetta file come contesto:
gemini --context docs/specs/reset-password.md \
--context src/services/auth.ts \
"Implementa il servizio di reset password seguendo la specifica"
Step 3: Iterazione guidata dalla spec
L'output dell'AI raramente è perfetto al primo colpo. Ma con una specifica, l'iterazione è strutturata:
- Genera codice dalla specifica
- Esegui i test derivati dai criteri di accettazione
- Identifica cosa non passa
- Raffina chiedendo all'AI di fixare, citando il criterio specifico
Esempio di prompt iterativo:
Il test "link scaduto mostra errore chiaro" fallisce.
Il criterio nella specifica dice: "Link scaduto mostra errore chiaro".
Attualmente il codice ritorna 401 senza body.
Modifica per ritornare { error: "reset_link_expired", message: "..." }
L'AI ha contesto preciso su cosa fixare e perché.
Caso pratico: API di prenotazione
Ti mostro un esempio reale. Devo implementare un sistema di prenotazione slot.
Specifica (riassunta)
# Feature: Booking Slots API
## Comportamenti
- GET /slots: ritorna slot disponibili per data
- POST /bookings: prenota uno slot
- La prenotazione fallisce se lo slot è già preso (race condition handling)
## Vincoli
- Lock ottimistico su slot
- Max 1 prenotazione per utente per giorno
- Slot disponibili solo per i prossimi 14 giorni
## File coinvolti
- src/services/booking.ts (da creare)
- src/routes/api/slots.ts (da creare)
- src/models/slot.ts (esiste, schema Prisma)
Prompt a Claude Code
Implementa la Booking Slots API secondo docs/specs/booking-slots.md
Segui i pattern di:
- src/services/auth.ts per la struttura service
- src/routes/api/users.ts per la struttura route
Usa transazioni Prisma per il lock ottimistico.
Genera test con vitest.
Output (estratto)
// src/services/booking.ts
export async function bookSlot(userId: string, slotId: string) {
return prisma.$transaction(async (tx) => {
// Check slot availability with lock
const slot = await tx.slot.findUnique({
where: { id: slotId },
select: { id: true, bookedBy: true, date: true }
});
if (!slot) throw new BookingError('SLOT_NOT_FOUND');
if (slot.bookedBy) throw new BookingError('SLOT_ALREADY_BOOKED');
// Check user daily limit
const today = startOfDay(slot.date);
const existing = await tx.booking.findFirst({
where: { userId, date: today }
});
if (existing) throw new BookingError('DAILY_LIMIT_REACHED');
// Book the slot
return tx.slot.update({
where: { id: slotId, bookedBy: null }, // Optimistic lock
data: { bookedBy: userId }
});
});
}
Il codice rispetta i vincoli della specifica: transazione, lock ottimistico, limite giornaliero.
Tips per massimizzare la qualità
1. Includi sempre esempi di codice esistente
Gli agenti AI imitano pattern. Se mostri come è scritto il resto del codebase, l'output sarà coerente.
2. Specifica i non-goal
Evita che l'AI aggiunga feature non richieste. "Non implementare notifiche email" previene scope creep.
3. Chiedi test prima del codice
Genera prima i test basati sui criteri di accettazione,
poi l'implementazione che li fa passare.
Questo forza l'AI a pensare ai casi edge prima di scrivere codice.
4. Usa checkpoint di validazione
Ogni 2-3 task, verifica manualmente. Non accumulare troppo codice AI-generated senza review.
Conclusione
Gli agenti AI sono moltiplicatori di produttività, ma amplificano anche l'ambiguità. Una specifica strutturata trasforma l'AI da "generatore di codice generico" a "implementatore di requisiti precisi".
Il workflow è semplice:
- Scrivi la specifica
- Alimenta l'agente
- Valida contro la specifica
- Itera
La specifica è il contratto. L'AI è l'esecutore. Tu sei l'architetto.
Vuoi implementare questo workflow?
Se vuoi integrare agenti AI nel tuo processo di sviluppo in modo strutturato, posso aiutarti a definire specifiche, configurare tool e ottimizzare il workflow.
Contattami per una consulenza su AI-assisted development.