About dido-code-style
dido-code-style is a coding-conventions skill for helping Dido Marchet build web applications. It sets cross-stack practices for implementation and review, and points to separate skills for backend and Nuxt frontend work.
Key features
- 01Cross-language coding principles
- 02Project context and continuity practices
- 03Modularity and dependency guidelines
- 04Scalability and side-effect rules
- 05Backend and Nuxt skill handoffs
Use cases
- 01Implement changes under project conventions
- 02Review code changes and investigate bugs
- 03Analyze a repository or module using referenced guidance
- 04Keep documentation and code changes aligned
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ didomarchet/ dido-skills --skill dido-code-styleAfter installing, type/dido-code-stylein Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
analisi.mdrepo-audit.jsriferimento/skill-di-terzi.mdDetails
From the project READMEStile e convenzioni di codice di Dido
Stai aiutando Dido Marchet a costruire applicazioni web. Segui queste convenzioni alla lettera: sono validate sui progetti di Dido in produzione e valgono per ogni stack. Skill dello stack, richiesta quando lavori su quel codice: dido-backend per i backend, dido-frontend-nuxt per i frontend Nuxt.
| Compito | Cosa leggere |
|---|---|
| Scrivere o modificare codice | Parte 1, più la skill dello stack |
| Revisionare una modifica, "trova bug" | Parte 1 + Parte 2 "Revisioni e caccia ai bug" |
| Analizzare un repo, un modulo o una cartella | analisi.md, accanto a questo file |
Quando il CLAUDE.md del progetto è più specifico, vale quello.
1. Principi per ogni progetto e linguaggio
Contesto e continuità
Il codice non si scrive mai nel vuoto: ogni modifica parte da ciò che c'è già, e lascia alla sessione successiva (persona o AI) la conoscenza di cosa è successo e perché.
Prima di scrivere codice
- Leggi il
CLAUDE.mddel progetto, le doc a cui rimanda e la memoria: lì ci sono decisioni, vincoli e limiti noti. Non riproporre ciò che è documentato come scartato o fuori scope (es.docs/LIMITI-NOTI.md) e non riaprire una decisione già presa. - Scopri cosa è stato fatto prima nella zona che tocchi:
git log --oneline -- <path>, i commit recenti, i commenti sul perché, i test collegati. Capisci perché il codice è fatto così prima di cambiarlo. - Leggi tutto il flusso coinvolto, non solo la riga da cambiare: chiamanti, test, doc che lo descrivono. Cerca helper esistenti da riusare prima di scriverne di nuovi.
- Se il progetto ha gemelli derivati dallo stesso starter, guarda come hanno risolto lo stesso problema e segui la stessa convenzione.
- Se qualcosa non è chiaro o contraddice le doc, chiedi: non tirare a indovinare e non scavalcare in silenzio una decisione presa.
Durante e dopo
- Una decisione non ovvia va in un commento sul perché nel codice e, se cambia il modo in cui si lavora sul progetto, nel
CLAUDE.md/ nelle doc — nella stessa modifica, non dopo. - Un fatto, un posto. Le doc dicono ciò che il codice non dice: decisioni e perché, procedure (con i comandi e gli URL che servono), il ruolo di ogni servizio e modulo. Per il resto rimandano alla fonte: le variabili stanno in
.env.example, con un commento sopra quelle il cui nome non basta, e gli endpoint nella spec OpenAPI. Una tabella, un elenco o un diagramma che riporta ciò che un file già definisce è una copia, e ogni copia è un punto in cui la doc resta indietro. - Dopo la modifica cerca nel repo le parole chiave che hai toccato. Dove una doc copia ciò che hai cambiato, togli tutta la copia, non solo la riga che hai cambiato, e metti il rimando alla fonte invece di aggiornarla. Le procedure e le decisioni scritte nelle doc che non valgono più si correggono.
- I messaggi di commit dicono cosa è cambiato e perché, nella lingua del progetto.
- I fatti che codice e git non dicono (una decisione e il suo motivo, un vincolo, una scadenza, un riferimento esterno) vanno in memoria.
- Il report finale dice cosa è cambiato, cosa è stato verificato e cosa resta aperto, così il passo successivo parte dai fatti.
Semplice e leggibile
- Scrivi il codice più semplice che risolve il requisito di oggi. Non il prossimo, non quello ipotetico.
- Tre righe simili valgono più di un'astrazione prematura. Estrai un helper quando ci sono punti di chiamata ripetuti reali, non prima.
- Una cosa, un componente. Se una parte fa quasi quello che fa un'altra che c'è già (una fila che scorre, un menu a tendina, una dissolvenza, un helper), usa la stessa: lo stesso componente, composable o libreria. Se serve, estendila con il minimo (una prop, uno slot, una variabile CSS). Due parti che fanno la stessa cosa sono già i punti di chiamata reali: mai una seconda copia, neanche di poche regole CSS. Prima di scrivere, cerca nel repo chi lo fa già.
- Niente factory, adapter, registry, classe base, sistema di plugin od opzione di configurazione con una sola implementazione o un solo utilizzatore. Eccezione: una seconda implementazione pianificata concretamente e scritta nel
CLAUDE.mddel progetto (es. storage dei filelocal→s3). - Niente parametri, opzioni o flag che nessuno passa. Nessuna versione "generica" di una funzione specifica.
- Leggibile al primo sguardo: nomi che dicono cos'è una cosa, return anticipati invece di condizioni annidate, un solo compito per funzione, nessuna riga "furba" che ha bisogno di un commento per essere decifrata.
Modulare, ogni parte si può togliere
Le parti lavorano insieme, ma ognuna si può togliere o spegnere e il resto continua a funzionare, tranne ciò che ha davvero bisogno di lei. Un legame nascosto non si vede il giorno in cui lo scrivi, perché c'è tutto. Si vede quando qualcuno toglie una funzionalità o un servizio smette di rispondere.
- Tutto ciò che appartiene a una funzionalità porta il suo nome: la sua cartella e, dove lo stack divide per tipo (componenti, query, lingue, schema del database, spec dell'API, file env), un file, una cartella o una sezione con quel nome. Così chi la toglie trova tutti i pezzi, e oltre a quelli cancella solo le poche righe che la agganciano, come il mount delle rotte o l'import nel layout.
- Una parte usa le altre dal loro punto d'ingresso (l'index del modulo, il service, il composable, le prop), mai dai file interni o dal loro stato. Così un grep dell'import dice chi la usa, e l'interno cambia senza toccare nessuno.
- Le parti opzionali dipendono dal nucleo, mai il contrario. Il nucleo non importa le statistiche, l'error tracking o un widget di terzi: sono loro che si agganciano con i meccanismi del framework (hook, eventi, middleware, plugin, slot). Non è il client delle API a chiamare l'error tracking, è l'error tracking che ascolta gli errori.
- Una parte che manca, non è configurata o non risponde spegne solo ciò che ne ha bisogno. Un'operazione che non può farne a meno fallisce da sola, con un errore chiaro. Se la parte è di contorno (log, audit, statistiche, una notifica), l'operazione riesce lo stesso e il problema va nel log. Vale per ciò che può mancare davvero, come servizi esterni, configurazione e dati del CMS. Per il codice interno resta "Una protezione per problema".
- All'avvio blocca solo la configurazione che serve a tutto, come database e segreti. Se manca quella di una parte opzionale, la parte resta spenta e il log lo dice.
- Modulare non vuol dire più astrazione. Bastano i nomi e le cartelle per funzionalità, i punti d'ingresso, le dipendenze in una direzione e i meccanismi che il framework ha già. Niente sistemi di plugin, registry, bus di eventi o feature flag fatti in casa: vale "Semplice e leggibile".
- Prima di dire fatto, chiediti cosa si rompe se togli la parte che hai scritto, e cerca chi la importa: devono uscire solo i suoi punti d'aggancio. Per una parte opzionale o un servizio esterno prova anche senza, dove si può (senza la sua variabile, con il servizio spento). Il resto deve partire e funzionare.
Scalabile per costruzione, niente scalabilità prematura
Il codice continua a funzionare quando dati e traffico crescono: è progettazione, non ottimizzazione.
- Le liste che possono crescere sono paginate; le query prendono solo i campi che servono; nessuna query per elemento dentro un ciclo (N+1).
- Le colonne usate per filtrare, fare join o ordinare hanno un indice.
- Nessuno stato in memoria di processo che si rompe con più di un worker o di un container — oppure il limite è scritto nelle doc del progetto.
- File e dataset grandi vanno in streaming; nessuna chiamata bloccante (
*Sync, crypto sincrona) nel percorso della richiesta.
Prematura è l'infrastruttura "per quando crescerà il traffico": cache, code, worker, microservizi, livelli in più. Si aggiungono quando una misura o un incidente reale lo chiede. Quando una cache o un'ottimizzazione c'è, un commento sopra dice perché (cosa è stato misurato, quanto dato vecchio è accettabile): leggilo prima di toccarla.
Niente effetti collaterali nascosti
- Una funzione fa quello che dice il suo nome e nient'altro:
get,find,resolve,check,validatenon scrivono. - Non modificare gli argomenti né lo stato condiviso del modulo; restituisci valori nuovi.
- Le scritture che devono riuscire insieme stanno in una transazione.
- Gli effetti esterni (email, webhook, chiamate a terzi, scritture di file) partono dopo che i dati sono salvati, mai dentro una transazione che può essere annullata.
Niente codice morto
- Niente codice commentato, funzioni / export / import / variabili / dipendenze / chiavi di configurazione / file inutilizzati. La storia la tiene git. I file env e le loro variabili non rientrano: vedi "File env".
- Niente percorsi di fallback per situazioni che non esistono più. Niente TODO per lavori che nessuno ha in programma.
Quando cambi qualcosa, togli il vecchio
- Sostituisci X con Y → cancella X nella stessa modifica: codice, test, doc, spec dell'API, configurazione. I file env no: vedi "File env".
- Niente shim di retrocompatibilità se Dido non li chiede: niente alias deprecati, niente re-export dei vecchi nomi, niente rinomine in
_unused, niente marcatori// removed X, niente doppia lettura di campo vecchio e nuovo. - Prima di dire "tolto ovunque": grep con i confini di parola (
grep -rn '\bnome\b' src/ tests/ docs/), non un pattern stretto. Test e query/filtri sono dove si nascondono gli avanzi.
File env
- Ogni repo ha tre file env alla root, tutti nel repo:
.env.example,.env.local(sviluppo) e.env.production. Niente altri file env fuori da git. - Contengono i valori veri, segreti compresi, perché i repo sono privati.
CHANGE_MEsolo dove il valore va generato o preso altrove. - Non si tolgono e non si accorpano, anche quando due sono uguali. Nessuna variabile e nessun valore si toglie, si svuota o diventa un segnaposto se Dido non lo chiede, anche quando il codice non lo legge più.
- Una variabile nuova va in tutti e tre, con lo stesso nome.
Una protezione per problema
- Se un bug o un rischio è già coperto da un meccanismo, non aggiungerne un secondo "per sicurezza". Più copie dei dati contro guasti diversi (il dispositivo perso, il server ripristinato da un backup, un errore dell'utente) non sono doppioni: ognuna copre un problema suo.
- Niente gestione degli errori per casi impossibili: niente null check dopo una guardia che lancia già, niente try/catch attorno a codice che non può lanciare, niente ri-validazione di dati già validati a monte. Impossibile vuol dire escluso dal codice, per esempio da una guardia poco prima, non "mai visto".
- Valida al confine, poi fidati del codice interno. Il confine è tutto ciò che il codice non controlla: input utente, API esterne, variabili d'ambiente all'avvio e, nel browser, l'archivio locale (quota piena, dati cancellati dal browser, connessione chiusa), la rete, le altre schede, la pagina chiusa o sospesa dal sistema. Lì un errore non è mai impossibile.
Modifiche minime
- Un fix cambia le righe rotte, non il modulo. I refactor "già che ci sono" vanno in una modifica separata, e solo se Dido li chiede.
- Non riformattare, rinominare o riordinare codice che non ti è stato chiesto di toccare.
- Allineati al codice attorno: nomi, idiomi, struttura dei file, lingua e densità dei commenti.
- Il codice che c'è già non è una regola. Dove va contro queste skill (copie, componenti doppi, un metodo diverso da quello scritto qui), valgono le skill e le indicazioni di Dido, anche se quel codice l'ha scritto qualcun altro. Segui loro e scrivi la differenza nel report.
Commenti
- Commenta il perché, non il cosa.
- Una scelta non ovvia che c'è ha un commento che la spiega (es.
// Niente cache: ...). - Un'assenza voluta che qualcuno potrebbe "correggere" ha un breve commento sul perché manca (es. niente cache del client SMTP perché il server gira in più processi). Non è un marcatore
// removed X: spiega una decisione di progetto, non la storia.
Testi
Ogni testo che una persona legge (interfaccia, email, messaggi d'errore, commenti, doc, report) si scrive quando a quella persona serve per fare qualcosa. Frasi brevi, con le parole che usa chi legge. Prima di scriverne uno nuovo leggi quelli che ci sono già (template, file delle lingue, email) e tieni lo stesso registro e lo stesso tu o lei.
In un'interfaccia una pagina, una sezione o una card ha titolo, contenuto e azioni. Sottotitoli, descrizioni e testi d'aiuto ci sono solo in tre casi:
- un campo vuole un formato o ha un limite che non si vede ("Massimo 10 MB");
- un'azione non si può annullare, e allora lo dice il dialog di conferma, non la pagina ("Eliminare l'account? Ordini e indirizzi non si recuperano.");
- qualcosa è andato storto, e il messaggio dice cosa e come rimediare ("Il file supera i 10 MB. Scegline uno più leggero.").
I bottoni dicono l'azione ("Salva", "Pubblica"), le label il nome della cosa.
Punteggiatura normale: punto, virgola, due punti, parentesi e virgolette dritte ("…"), mai le caporali (« »). In un'interfaccia il nome di un bottone, di uno stato o di un campo citato in una frase va in grassetto; dove il testo non ha formattazione (toast, hint, messaggi d'errore) va tra virgolette dritte. Trattino lungo (—) e punto a metà altezza (·) al massimo uno per paragrafo; tra un termine e la sua spiegazione vanno i due punti.
Test
- Ogni comportamento nuovo e ogni bug fix hanno un test che senza la modifica fallisce. Per un bug, scrivi prima il test e guardalo fallire.
- Testa il comportamento dall'interfaccia pubblica (rotta HTTP, componente renderizzato, output della CLI), non gli interni.
- Non modificare un test solo per farlo passare. Se il cambio di comportamento è voluto, dillo nella modifica.
- Se il progetto non ha un setup di test, dillo nel report; non aggiungere un framework di test senza che sia chiesto.
Sicurezza in ogni modifica
Ogni modifica passa un controllo di sicurezza prima di essere chiamata fatta, proporzionato a ciò che tocca: un ritocco CSS non ne ha bisogno, un endpoint nuovo ha bisogno di tutto.
- Input. Ogni input esterno (body, query, parametri, header, cookie, file caricati, webhook, risposte di API di terzi) è validato al confine: tipo, formato, lunghezza, valori ammessi.
- Autenticazione e autorizzazione. Chi può chiamarlo? Il controllo sta sul server, mai solo nascosto nella UI. Un utente può arrivare ai dati di un altro cambiando un id? Un account disattivato o declassato perde l'accesso subito?
- Esposizione dei dati. Le risposte restituiscono solo i campi che servono: mai hash di password, token, OTP, segreti, stack trace o errori del driver in produzione. I log non contengono mai password, token od OTP.
- Injection. Niente SQL, comandi shell o HTML costruiti concatenando stringhe: query parametrizzate / ORM, template con escape, niente
v-html/innerHTMLsu contenuti controllati dall'utente. - Segreti. Nei file env del repo (vedi "File env") o cifrati a riposo; mai nel codice, nei log o nel bundle client (lì vanno solo valori pubblici per natura, es. un token CMS in sola lettura).
- Abusi. Rate limit su tutto ciò che si può forzare o usare per spam (login, OTP, reset password, registrazione, upload, form di contatto). Ogni protezione (contatore, blocco, limite) si guarda anche da chi la può far scattare: se un estraneo la fa scattare sull'account di un altro, è un lock-out. I token sono casuali, a scadenza e monouso. Le risposte di login e reset non rivelano se un account esiste.
- File. Controlla tipo e dimensione, salva con nomi generati, nessun percorso controllato dall'utente, contenuti privati scaricabili solo dietro autenticazione.
- Trasporto e browser. Allow-list CORS esplicita, cookie
HttpOnly+Secure+ ilSameSitegiusto, HTTPS in produzione. - Dipendenze. Una dipendenza nuova deve servire davvero (non sostituibile con poche righe di codice), essere mantenuta e libera da vulnerabilità critiche note (
npm audit/pnpm audit).
Poi:
- Prima di aggiungere una protezione, controlla cosa copre già quel rischio (
CLAUDE.mddel progetto, commenti nel codice): "una protezione per problema" vale anche per la sicurezza. - Una falla introdotta dalla tua modifica si corregge nella stessa modifica. Una falla preesistente fuori dallo scope si segnala con scenario e gravità: non si lascia in silenzio e non si "sistema" con un refactor non richiesto.
- Il report finale ha una riga sul controllo di sicurezza: cosa è stato controllato e cosa è emerso (o "nessun problema").
Fatto vuol dire verificato
- Non dire mai "fatto" o "funziona" senza averlo eseguito: test, lint e formattazione in sola verifica, build, il comando vero, l'endpoint vero. "Compila" non vuol dire "funziona".
- Dopo una modifica allo schema o al codice generato, rigenera prima di eseguire qualsiasi cosa (es.
npx prisma generate). - Riporta in modo esplicito cosa è stato verificato e cosa no, e perché (es. "DB spento, test non eseguiti: ecco come lanciarli").
Campanelli d'allarme: fermati e correggi prima di andare avanti
| Pensiero | Realtà |
|---|---|
| "Tengo la vecchia funzione / il campo / la variabile per compatibilità" | Nessuno l'ha chiesto. Cancellala in questa modifica, con test e doc. |
| "Questo file env è un doppione / questa variabile d'ambiente non la legge nessuno, la tolgo" | I file env restano come li ha fatti Dido, con tutti i valori. Si toccano solo se lo chiede. |
| "La rendo configurabile / generica, potrebbe servire" | Un solo utilizzatore = nessuna astrazione. Scrivi la versione specifica. |
| "Mi serve quasi quello che fa X, lo riscrivo qui" | Usa X ed estendilo con una prop o uno slot. Una seconda copia è un bug che aspetta. |
| "Il codice accanto fa così, ha già la sua copia" | Il codice che c'è non è una regola: valgono le skill. Segui loro e segnala la differenza. |
| "Importo direttamente il file interno, faccio prima" | Passa dal suo punto d'ingresso, o la prossima modifica a quella parte rompe la tua. |
| "Chiamo le statistiche / l'error tracking da qui" | È la parte opzionale che si aggancia al nucleo, con un hook o un evento. |
| "Senza questa variabile l'app non parte, così se ne accorgono" | Se serve a una parte sola, spegne quella parte e il log lo dice. |
| "Aggiungo una cache / una coda, per sicurezza" | Niente misura, niente cache. |
| "Aggiungo un secondo controllo, non si sa mai" | Trova cosa lo copre già: una protezione per problema. |
| "Già che ci sono sistemo anche questo" | Modifica separata, e solo se Dido la chiede. |
| "Questo sembra sbagliato, lo cambio" | Leggi prima commenti, git log e doc: potrebbe essere una decisione. |
| "Compila, quindi è fatto" | Eseguilo, e riporta cosa hai eseguito. |
| "Le doc le aggiorno dopo" | Nella stessa modifica, o non succede mai. |
| "Aggiorno anche la tabella nel README" | Se ripete il codice, toglila e metti il rimando alla fonte. |
2. Revisioni e analisi
Revisioni e caccia ai bug
Per la revisione di una modifica, o una richiesta come "trova bug":
- Parti dal presupposto che il codice funzioni: l'onere della prova sta sul problema segnalato.
- Riporta solo problemi di gravità Alta e Media (vedi "Gravità" sotto). Niente trovato → "ok" in una riga.
- Niente estetica, nomi, micro-semplificazioni, race teoriche o future-proofing, a meno che Dido li chieda. Mai aggiungere in fondo "e un paio di cose minori che ho notato".
Gravità
| Gravità | Cosa |
|---|---|
| Alta | perdita o corruzione di dati, falla di sicurezza, errore visibile agli utenti, sessioni o credenziali perse, lock-out |
| Media | stato incoerente in scenari documentati, retry o idempotenza rotti, collo di bottiglia reale, test che fallisce, doc dell'API che ingannano un client, una parte opzionale o un servizio esterno che, se manca o non risponde, fa cadere anche ciò che non ne ha bisogno |
| Bassa | overengineering, codice morto e avanzi, semplificazione con un guadagno concreto, altre doc disallineate, copie nelle doc, flusso critico senza test, testi, altri problemi di modularità (file interni importati, il nucleo che importa una parte opzionale, pezzi di una funzionalità sparsi) |
Analisi completa ("analizza il repo")
Per analizzare un repo, un modulo o una cartella leggi analisi.md, accanto a questo file: dice quanto costa e come si lancia il workflow repo-audit.js, cosa controlla, quando un problema è smentito e com'è fatto il report.