Che cosa succede al vostro progetto se chi lo ha realizzato non è più raggiungibile, o se serve un'altra persona per estenderlo? Se il progetto è una scatola nera, la risposta è che siete in ostaggio — non di qualcuno in malafede, ma della semplice mancanza di una mappa.

Il problema è che quasi tutte le documentazioni esistono e non servono. Vale la pena partire da come si distingue una cosa dall'altra.

La prova del nove#

il test
La prova del nove di una documentazione utile, e l'unica che conta:  Una persona competente, che non ha mai visto questo progetto,  riesce a metterlo in esecuzione in locale e a pubblicare  una modifica banale — senza chiedere niente a nessuno?Se la risposta è no, la documentazione non esiste: esiste un testo.Il modo per scoprirlo non è rileggere il documento. È darlo aqualcuno e stare zitti mentre ci prova, annotando ogni punto in cuisi blocca. Ogni interruzione è una riga mancante.

Il README che supera la prova#

README.md
# Nome del progettoUna riga: che cosa fa e per chi.## Avvio in locale    git clone …    npm ci    cp .env.esempio .env      # i valori da chiedere a: <chi>    npm run avvia             # → http://localhost:3000## Struttura    server.js          rotte, header, file statici    site/layout.js     shell HTML condivisa (head, navbar, footer, SEO)    site/pages.js      pagine principali    site/articoli/     un file per articolo del blog    src/js, src/css    SORGENTI — mai serviti in chiaro    public/*-build     artefatti di build — questi sì    dati/              immagini## Pubblicazione    npm run build             # obbligatorio dopo ogni modifica a src/    rsync -avz --exclude src --exclude .claude ./ utente@host:/home/sito/    ssh utente@host 'cd /home/sito && npm ci --omit=dev && pm2 restart sito'## Decisioni che sorprendono- Nessun framework frontend: vedi docs/adr/0003.- SQLite e non PostgreSQL: una macchina sola, carico in lettura.- Il JS è offuscato: scelta del committente, non requisito tecnico.## Quando qualcosa non va- Il sito non riparte      → pm2 logs sito --lines 100- Le modifiche non si vedono → manca npm run build- 502 dal proxy           → il processo node è caduto: pm2 list

Quattro sezioni, e la struttura non è casuale. Avvio per primo, perché è la prima cosa che serve. Struttura come mappa per orientarsi, con la responsabilità di ciascuna cartella e non solo il nome. Pubblicazione con i comandi esatti, non con una descrizione a parole. E soprattutto la quarta.

«Decisioni che sorprendono» è la sezione che nessuno scrive e che vale più delle altre tre. Elenca le scelte non ovvie che qualcuno, arrivando, sarebbe tentato di «correggere» — riaprendo il problema che quella scelta risolveva.

Il registro delle decisioni#

i limiti del diagramma
Perché un registro delle decisioni vale più di un diagramma:  Un diagramma descrive com'è il sistema OGGI.  Il codice descrive com'è il sistema oggi, e meglio.  Nessuno dei due dice PERCHÉ.E il perché è l'unica informazione che non si ricava rileggendo.Senza, ogni scelta non ovvia diventa, per chi arriva dopo:  - un errore da correggere (e la correzione riapre il problema    che quella scelta risolveva), oppure  - un tabù da non toccare, che nessuno osa rimuovere nemmeno    quando la ragione è scaduta da anni.Un ADR è mezza pagina. Si scrive nel commit che introduce la scelta,e non si aggiorna mai: se la decisione cambia, se ne scrive un altroche supera il precedente. La storia resta leggibile.
docs/adr/0003-niente-framework.md
# 0003 — Nessun framework frontendStato: accettataData: 2026-01-15## ContestoSito multi-pagina, contenuto prevalentemente statico, interattivitàlimitata a: rivelazione dei contatti, canvas decorativo, indicelaterale degli articoli. Nessuno stato condiviso fra schermate.## DecisioneFrontend in JavaScript nativo, moduli ES compilati in un bundle unicocon esbuild. Nessuna dipendenza runtime nel browser oltre a GSAP.## Conseguenze+ Bundle di ~17 KB contro i ~140 KB di un framework medio+ Nessun aggiornamento di dipendenze frontend da inseguire− Un eventuale passaggio a interfacce con stato condiviso richiede  una riscrittura, non una migrazione− Chi conosce solo React ha una curva di ingresso su questo progetto## Quando riconsiderarlaSe compaiono più di due schermate che condividono lo stesso stato,o se la sincronizzazione manuale del DOM supera i tre punti perazione. Vedi /blog/frontend-vanilla-typescript-vite.

Mezza pagina, cinque sezioni. Quella che fa la differenza è l'ultima: «quando riconsiderarla». Trasforma una decisione da dogma in ipotesi con una data di scadenza, e dà a chi arriverà dopo il permesso esplicito di cambiarla — insieme al criterio per capire se è il momento.

Un ADR non si aggiorna mai. Se la decisione cambia, se ne scrive uno nuovo che dichiara di superare il precedente, e il vecchio resta con stato «superata». La storia del ragionamento resta leggibile, ed è spesso più utile della conclusione.

Il vero problema: la divergenza#

i tre rimedi
# Il problema della documentazione non e' scriverla: e' che diverge.# Un documento che descrive un sistema che non esiste piu' e' PEGGIO# dell'assenza di documentazione, perche' chi lo legge prende# decisioni sbagliate con fiducia.# Rimedio 1 — sta nel repository, e si aggiorna nello STESSO commit.#            Se aggiornarla e' un'attivita' separata, non si fa.git show --stat HEAD site/articoli/index.js  | 2 +- README.md               | 4 +--     ← nello stesso commit# Rimedio 2 — i comandi nel README sono ESEGUIBILI e vengono eseguiti.# Un blocco di comandi che nessuno prova e' un blocco che marcisce.grep -A3 '## Avvio in locale' README.md | tail -3 | sh -n# (in CI: si estraggono e si eseguono davvero su un ambiente pulito)# Rimedio 3 — la struttura delle cartelle si genera, non si trascrive.tree -L 2 -I 'node_modules|.git' --dirsfirst
Il primo è organizzativo, gli altri due tecnici. Il primo è quello che conta: se aggiornare la documentazione è un'attività separata, prima o poi non si fa.

Ciò che non va scritto a mano#

rotte.jsJavaScript
// La regola generale: quello che si può ricavare dal codice NON va// scritto a mano, perché è esattamente la parte che diverge per prima.// ✓ Generabile — non scrivetelo://   - elenco delle rotte          → dal router//   - schema del database         → dalle migrazioni//   - elenco delle dipendenze     → da package.json//   - variabili d'ambiente usate  → da una ricerca su process.env//   - struttura delle cartelle    → da tree// Esempio: le rotte, ricavate dal router invece che trascritte.function elencoRotte(app) {  return app.router.stack    .filter(s => s.route)    .map(s => `${Object.keys(s.route.methods)[0].toUpperCase()} ${s.route.path}`)    .sort();}// npm run rotte > docs/rotte.txt   ← e in CI si verifica che sia aggiornato// ✗ NON generabile — è questo che va scritto a mano, ed è poco://   - perché una scelta è stata fatta//   - cosa succede se si sbaglia//   - chi contattare quando si rompe//   - quali vincoli non sono negoziabili e perché

È la regola operativa che risolve la divergenza alla radice: l'elenco delle rotte, lo schema, le dipendenze e la struttura delle cartelle non si trascrivono. Si generano, e in integrazione continua si verifica che il file generato coincida con quello nel repository. Se non coincide, la build fallisce — e la documentazione non può più divergere, perché nessuno può pubblicare senza allinearla.

Quello che resta da scrivere a mano è poco, e non è ricavabile da nessuna parte: perché una scelta è stata fatta, cosa succede se si sbaglia, chi contattare, quali vincoli non sono negoziabili.

Il runbook#

docs/incidenti/502.md
## Il processo non riparte dopo il deploySintomo: il proxy risponde 502, pm2 list mostra "errored".1. Guardare l'errore vero, non il 502:       pm2 logs sito --lines 100 --err2. Cause in ordine di frequenza:   a) manca una dipendenza → "npm ci --omit=dev" non è stato eseguito   b) la porta 3000 è occupata da un processo zombie → "lsof -i :3000"   c) una variabile d'ambiente è assente → il codice fallisce all'avvio3. Rientro rapido (se la causa non è chiara e il sito è giù):       cd /home/sito && git checkout <tag-precedente> && npm ci --omit=dev       pm2 restart sito4. Verifica:       curl -s -o /dev/null -w '%{http_code}' https://www.esempio.it/api/healthQuesta pagina si scrive DOPO il primo incidente, non prima: soloallora si sa quali sono le cause reali e in quale ordine.

La documentazione operativa ha una regola sua: si scrive dopo. Un runbook redatto in anticipo elenca le cause che si immaginano; uno scritto dopo il primo incidente elenca quelle che si sono verificate davvero, nell'ordine in cui conviene controllarle. E la sezione più preziosa è il «rientro rapido»: cosa fare quando il sito è giù, la causa non è chiara e la priorità è tornare online, non capire.

DocumentoRisponde aSi aggiornaSe manca
READMEcome lo avvio, dove sta cosaa ogni cambio di strutturauna settimana di lettura del codice
ADRperché è fatto cosìmai: se ne scrive uno nuovole scelte vengono disfatte e rifatte
Runbookè rotto, cosa facciodopo ogni incidentesi improvvisa alle tre di notte
.env.esempioquali variabili servonocon il codice che le leggeerrori all'avvio senza spiegazione
Rotte / schemacosa espone, com'è fattogeneratidivergono entro un mese

Una nota finale sul destinatario, che cambia il modo di scrivere: il lettore più probabile di questa documentazione siete voi fra diciotto mesi, sotto pressione, dopo aver lavorato su altri tre progetti. Non ricorderete perché quella riga è lì. Scrivere per quella persona — che è competente ma ha dimenticato tutto — produce documenti molto più utili che scrivere per un ipotetico nuovo collaboratore.