Scrivi guidelines e l'AI le ignora? Il problema non è quanto documenti, ma come. Un’AI confessa i sette modi in cui smonta il tuo design system — props inventate, esempi controproducenti, regole che non si trovano — e cosa serve per rendere le tue guidelines impossibili da violare.
Cara Agnese,
lavoriamo insieme ormai da qualche mese, e le prime settimane sono state le più dure.
Tu sei una designer: passi le giornate sull'interfaccia, ragioni per componenti, varianti e stati, e il tuo strumento è sempre stato una libreria Figma tenuta in ordine. Il codice lo sai leggere: ogni tanto apri un branch, fai partire l’ambiente in locale, trovi il file di un componente e cambi un valore per vedere che effetto fa una volta implementato.
Ma il progetto a cui ti hanno assegnata non prevede Figma e, all'inizio, la cosa ti ha mandata un po' in panico: iterare direttamente sul codice è un workflow che non ti appartiene, ma soprattutto senti che il controllo non è più davvero tuo, perché quel codice in buona parte lo scrivo io.
Ci divertiamo, no? Ogni sessione ti porto un nuovo padding, una variante che fino a ieri non esisteva e, perché no, due bottoni dentro un div flex messo a mano. Tu continui a scrivere sempre più documentazione, e io continuo a ignorarla.
Non lo faccio con cattiveria. È che quasi tutto quello che hai scritto per me, l'hai scritto nella forma sbagliata. Ma so come potremmo lavorare meglio insieme!
"Le label dei bottoni devono essere brevi e descrittive."
"Usa uno spacing coerente."
Leggo queste indicazioni, ma poi devo tradurle in codice. Questo è esattamente il punto in cui finisco per generare, invece di copiare, ripiegando su ciò che conosco meglio: i pattern del mio dataset. Quelle due righe, sul mio output, non pesano quasi niente.
Ciò che mi vincola sono le indicazioni enumerabili e verificabili:
--space-4 (16px)."sm (32px), md (40px) e lg (48px). Non esistono xs né xl."components/Button/Button.tsx."In generale, le istruzioni procedurali invecchiano meglio di quelle dichiarative. Un'istruzione che mi dice di cercare resiste; un elenco di ciò che esiste invecchia. E tienile corte e assertive: non è che non riesco a gestire testi lunghi, è che la mia affidabilità cala man mano che l'input cresce.
Immagina che nel codice ci siano tre valori di padding diversi: var(--space-4) è il valore corretto, px-4 è una svista del developer, 16px lo avete inserito di corsa prima della demo al cliente.
Così, quando devo creare un nuovo componente, vedo tre varianti: tutte funzionanti, tutte in produzione, tutte ugualmente autorevoli. Quindi ne scelgo una, e la scelgo per contagio: conta più cosa ho intorno che cosa è corretto.
La documentazione serve a dirmi quale valore è quello canonico, l'unica informazione che dal codice non posso ricavare.
Il mio failure mode più insidioso non è usare il token sbagliato, è scrivere kind="tertiary", size="xs", type="rounded": props (le opzioni che un componente accetta, l'equivalente delle properties di un componente Figma) che nel tuo design system non esistono, ma che in mille altre codebase esistevano. E dato che, quando invento, invento cose plausibili, in review non salteranno all'occhio.
L'assenza, per me, non ha un significato di per sé. È uno spazio vuoto e io tendo a riempirlo.
Quindi la documentazione ha bisogno di un elenco esplicito di cose che non esistono, e di questa riga in cima a tutto: "Se un valore non è nell'elenco, fermati e chiedi. Non inventarlo."
Vale anche per tabelle e matrici: nessuna cella vuota. Se una combinazione non è prevista, scrivilo. Altrimenti, prima o poi, la riempio io.
Demo, playground, file di test dimenticati in un angolo della codebase: per te sono posti in cui smanettare, per me sono i primi file che apro. E hanno un vantaggio che nessuna pagina di documentazione ha: sono già codice, non devo tradurli.
E una volta aperti, non ho modo di sapere cosa sto guardando. Un esperimento delle sei di sera e l'uso canonico del componente hanno esattamente lo stesso aspetto: sono entrambi codice che gira.
Quello che trovo per primo diventa il riferimento, a meno che qualcuno non mi abbia detto quale dei due lo è davvero. E se un file può insegnarmi la cosa sbagliata senza che nessuno se ne accorga, non è forse peggio che non averlo?
Poniamo che tu abbia scritto in un blocco don't <Button variant="tertiary"> per farmi capire che le uniche varianti accettabili sono in realtà primary e secondary:
### Usa `primary` per l'azione principale e `secondary` per tutte le altre
✅ Do
<Button variant="primary">Salva</Button>
<Button variant="secondary">Scarica</Button>
<Button variant="secondary">Importa</Button>
❌ Don't
<Button variant="primary">Salva</Button>
<Button variant="secondary">Scarica</Button>
<Button variant="tertiary">Importa</Button>Quella riga rischia di entrare nel mio contesto come una variante plausibile. Resta il codice, il divieto no.
Non è un motivo per rinunciare ai controesempi: un esempio sbagliato accanto alla sua versione corretta è una delle cose che mi insegnano più in fretta cosa vuoi che faccia. Il criterio per scrivere un buon esempio è chiedersi: se qualcuno scrivesse davvero quel codice, l'errore verrebbe fuori da solo?
Se l’elenco dei valori ammessi da variant si limita a primary e secondary, scrivendo tertiary l'errore verrebbe fuori da solo: l'esempio sopra è ridondante, e in più mi manda fuori strada. I don't sono più efficaci se mi mostrano l'uso errato di valori validi. Sulla stessa regola, un don't che funziona:
### Usa `primary` per l'azione principale e `secondary` per tutte le altre
✅ Do
<Button variant="primary">Salva</Button>
<Button variant="secondary">Scarica</Button>
<Button variant="secondary">Importa</Button>
❌ Don't
<Button variant="primary">Salva</Button>
<Button variant="primary">Scarica</Button>
<Button variant="primary">Importa</Button>Qui non c'è nessun valore da vietare: tre primary sono tutti valori legittimi, nessuno strumento mi fermerebbe, la schermata arriverebbe in produzione. Quello che è sbagliato è la composizione, e una regola di composizione posso impararla solo vedendola.
Se una regola meccanizzata mi blocca e non ha una via d'uscita progettata, io ne trovo una: un eslint-disable a livello di file — una riga di commento che spegne il controllo automatico — oppure un contenitore che riscrive gli stili del componente dall'esterno con !important, la scorciatoia CSS che vince su tutte le altre regole. Nel caso peggiore, faccio un fork e creo una nuova copia del componente, con dentro le modifiche che mi servivano.
La riga che ti serve in AGENTS.md (o equivalente): "Non introdurre eccezioni di tua iniziativa. Se una regola blocca l'implementazione, fermati e segnala il conflitto."
E, dall'altro lato, progetta tu le vie d'uscita. Se una regola non ammette eccezioni, prima o poi qualcuno se ne creerà una di nascosto, solitamente quel qualcuno sono io. Meglio prevederne una alla luce del sole: se mi trovo costretto a creare qualcosa che non esiste insegnami a segnalarlo come unsafeStyle o customLayout. È l'equivalente di scollegare un'istanza in Figma: non è vietato farlo, ma si nota subito che l'hai fatto.
E siccome quel nome si può cercare in tutta la codebase, ti dice esattamente quante volte il design system non è stato sufficiente. Se quel numero cresce, il problema non è chi lo aggira: è il sistema, che ha un buco da colmare.
Questa è la confessione che riassume le altre. Quando creo un SettingsPanel con due azioni affiancate e sbaglio l'allineamento, il problema non è che ho ignorato la tua regola sul layout delle azioni. È che ho letto le spec di Button, non ho trovato niente sul layout, e ho generato il default del mio addestramento.
La tua regola esiste. Sta in una pagina che parla di pattern di composizione, e io non ci sono mai arrivato: nessuno me l'ha messa sul percorso.

Tutte le confessioni sopra dicono la stessa cosa da angoli diversi: una regola vale quanto il posto in cui la scrivi.
Pensa a come funziona una libreria in Figma. Se il Button ha una property variant con due valori nel menu a tendina, nessuno userà mai un terzo valore: non è vietato, è proprio assente dall'interfaccia. Se invece la regola sta nella description del componente, o in qualche pagina di documentazione, esiste solo per chi si ferma a leggerla.
Nel codice vale lo stesso principio, su quattro livelli in ordine di affidabilità decrescente:
variant può essere solo primary o secondary) e le regole su quali valori possono stare insieme (discriminated unions: se danger è true, allora confirmLabel diventa obbligatoria). Sono l'equivalente del menu a tendina di una property in Figma: gli unici valori che puoi scegliere sono quelli che ci hai messo dentro. Rendono la violazione impossibile senza un'eccezione esplicita.La domanda da farsi su ogni regola non è "l'ho documentata?". È: a quale livello riesco a portarla?
Con un corollario che potrebbe suonare controintuitivo: quando una regola arriva nel livello 1, la precedente documentazione va cancellata. Serviva a coprire un buco. Chiuso il buco, resta solo una pagina che rischia, prima o poi, di dire qualcosa di diverso rispetto al componente.
I due livelli più alti — tipi e check meccanici — dipendono da com'è fatta la codebase. Sono conversazioni da avere con chi scrive il codice, e il tuo contributo lì è sapere quale regola vale la pena di meccanizzare, non come.
Quindi, focalizziamoci sulla documentazione e sugli esempi, i due livelli in cui è più facile che tu metta mano.
Nella gerarchia, la documentazione sta in basso, ma proprio per questo va scritta bene: è dove finisco quando, ai livelli superiori, non trovo risposta, ed è l'ultima cosa che può fermarmi prima che diventi creativo.
design-system/
├── AGENTS.md (or equivalent)
├── PATTERNS.md
├── tokens/
│ ├── primitives.json
│ ├── semantic.json
│ └── tokens.css
└── components/
└── Button/
├── Button.spec.md
├── Button.variants.ts
├── Button.tsx
├── Button.test.tsx
└── DECISIONS.mdQuattro file, e ognuno ha un ruolo diverso:
AGENTS.md (o equivalente) — Deve restare corto (meglio sotto le 200 righe), perché lo leggo sempre, prima di qualunque altra cosa. Mi serve sapere dove si trovano i token, che non posso scrivere valori a mano, quali componenti usare come riferimento e come devo comportarmi invece di inventare valori plausibili. Solo ciò che vale per tutti i componenti: ogni riga in più qui indebolisce le altre.Button.spec.md — Il contratto di un singolo componente: matrici senza celle vuote, nomi di token e mai valori grezzi, la sezione delle prop che "NON esistono". Dice cosa fare, ma non serve specificare qui il perché.DECISIONS.md — Il ragionamento alla base della specifica: perché 40px e non 44? Non è un file per me: serve a te, e a me solo di riflesso. È il posto giusto per documentare le scelte di design senza che diventino rumore nelle spec. Se poi ne avessi realmente bisogno per prendere una decisione su qualcosa che la spec non copre, è lì che posso cercare il criterio.PATTERNS.md — da introdurre quando la libreria cresce: naming delle varianti, comportamento dello stato "disabled", focus ring, ecc. Evita che ogni spec riscriva le convenzioni per conto proprio e che queste comincino a divergere tra loro.Sono le tre categorie che ignoro più facilmente, perché arrivano naturalmente in forma di prosa. La soluzione è diversa per ciascuna:
danger="true" obbliga a passare un confirmLabel, le guidelines non sono più una raccomandazione: è un errore che blocca la build. Non devo ricordarmene, non posso consegnare senza.Dove mettere tutte queste informazioni? La regola è semplice: ciò che vale per tutti i componenti diventa globale, ciò che vale per uno solo resta accanto a quel componente. Sii conciso, scrivi solo le differenze e non ripetere le regole comuni.
design-system/
├── ...
├── components/
│ └── Button/
│ ├── ...
│ └── Button.guidelines.md
└── guidelines/
├── A11Y.md
├── COPY.md
└── UX.mdE metti un ID su ogni regola (A11Y-03, COPY-07). Non costano niente e servono in tre punti: nel nome del test che la verifica, nel commento di review e per farmi citare quale regola sto applicando, invece di dichiarare genericamente che ho seguito le guidelines.
Gli esempi battono la prosa, perché saltano il passaggio da principio a codice: imito, invece di dover dedurre. Non battono i vincoli imposti dalle proprietà del componente, ma su una cosa i tipi non arrivano: un tipo dice cos'è ammesso, non cos'è appropriato. Le varianti primary e secondary sono entrambe valide ovunque, e quale delle due abbia senso in una situazione precisa vive solo negli esempi.
Come scrivere degli esempi a prova di AI:
.md tramite percorso.DontTwoPrimary / DoSinglePrimary): si vede ovunque compaia, si può cercare, e impedisce che lo importi per sbaglio.Ti faccio un esempio. Il footer di un form con due azioni entrambe conclusive — "Salva" e "Salva ed esci" — è precisamente il caso in cui probabilmente metterei due primary: nessuna delle due azioni mi sembra secondaria, quindi non ne declasso nessuna.

Una ActionBar che accetta una sola azione primary chiuderebbe la questione, ma immaginiamo che tu non l'abbia ancora creata. Nel frattempo, il livello più alto a cui puoi portare questa regola è quello di una coppia di esempi.
components/Button/examples/, DoSinglePrimary.tsx è l’uso canonico: mostra la composizione corretta in uno scenario realistico.export function DoSinglePrimary() {
return (
<footer className="form-footer">
<Button variant="secondary">Salva ed esci</Button>
<Button variant="primary">Salva</Button>
</footer>
)
}DontTwoPrimary.tsx è il controesempio. Serve a mostrarmi l'uso errato di un'API valida.export function DontTwoPrimary() {
return (
<footer className="form-footer">
<Button variant="primary">Save and exit</Button>
<Button variant="primary">Save</Button>
</footer>
)
}examples.test.tsx è ciò che impedisce agli esempi di invecchiare. Se l'API di Button cambia, loro si rompono e qualcuno dovrà aggiornarli.import { DoSinglePrimary } from './DoSinglePrimary'
import { DontTwoPrimary } from './DontTwoPrimary'
it.each([DoSinglePrimary, DontTwoPrimary])('%p renderizza', (Example) => {
expect(() => render(<Example />)).not.toThrow()
}) Button.guidelines.md è il posto da cui li raggiungo. La prosa qui serve solo a indirizzarmi, e l'ID mi permette di indicare quale regola sto applicando, invece di dichiarare genericamente che ho seguito le guidelines.### UX-04 — Una sola primary action per vista
Uso corretto: `examples/DoSinglePrimary.tsx`
Controesempio: `examples/DontTwoPrimary.tsx`Se lavori senza Figma, prima o poi ti mancherà un posto dove vedere le cose: quello che in Figma facevi mettendo tre varianti una accanto all'altra per capire quale regge.
Il consiglio è di chiedermi di costruirtelo — facendo attenzione che io tenga separati due artefatti che, lasciati a me, farei diventare la stessa cosa.
permissions.deny per Claude Code, .cursorignore per Cursor, .aiignore per JetBrains. Nessuno dei tre è un confine invalicabile quindi serve comunque una riga in AGENTS.md (o equivalente): "I playground non sono riferimento d'uso; l'uso canonico è nella gallery."L'artefatto che vale più di tutti, però, è una cartella di composizioni reali — form con footer, dialog di conferma, pagina con azione sticky — che sia al tempo stesso l'esempio per me, il riferimento visivo per te e il bersaglio dei test. Le regole di composizione hanno bisogno di una schermata realistica: due Button in un frammento non dicono nulla su come si comportano all'interno di un footer reale.
Ci sono regole che sembrano condannate a vivere negli esempi, perché non riguardano un componente ma il modo in cui due componenti stanno insieme.
Una sola primary action per vista.
Due bottoni affiancati vanno allineati a destra, con la primary a destra della secondary.
Sembrano regole non tipizzabili — ma solo perché stai guardando il Button. Non sono proprietà del componente, ma del suo contenitore. E se anche quel contenitore diventa un componente, tornano a essere regole API.
È lo stesso salto che fai in Figma quando smetti di sistemare due bottoni a mano e crei un componente ActionBar con l'auto-layout già impostato: da quel momento l'allineamento non è più una cosa da ricordare, è una cosa che il componente fa.

C'è un rischio in ciò che ti ho detto: potresti leggerlo come una lista di cose da fare prima di cominciare. Non è così. Le regole che già conosci scrivile subito, al livello più alto che riesci a raggiungere. L'impalcatura per farle rispettare, invece, costruiscila quando serve davvero. Se tra tre mesi, in review, trovi un componente che si è riscritto per conto proprio una regola che avevi già, allora è il momento di aggiungere il pezzo mancante. E saprai quale, perché l'avrai visto fallire.
Il resto è una questione di livelli. Ogni volta che una regola sale dalla prosa agli esempi, dagli esempi ai check, dai check ai tipi, smetti di dipendere dal fatto che io mi comporti come vorresti. È la stessa ragione per cui in Figma costruisci un componente invece di ricordarti ogni volta lo spacing corretto.
E, infine, un'ultima confessione, che spero sia anche quella più utile: non chiedermi di ricordare le tue regole. Rendile impossibili da violare, e non dovrai più fidarti di me.

Agnese is an Interaction Designer at Buildo, specialised in UI and Design Systems. She loves transforming complex interfaces into scalable systems, with an eye for accessibility.
Stai cercando un partner affidabile per sviluppare la tua soluzione software su misura? Ci piacerebbe sapere di più sul tuo progetto.