Il consiglio di “buon gusto” di Brian Kernighan mostra come il codice leggibile faccia risparmiare tempo, riduca i bug e aiuti i team reali a muoversi più velocemente rispetto ai trucchi furbi.

Il nome di Brian Kernighan compare in posti che molti sviluppatori usano senza pensarci: strumenti classici di Unix, l'ecosistema C e decenni di scritti che hanno insegnato a spiegare i programmi in modo chiaro. Che tu ricordi The C Programming Language (con Dennis Ritchie), The Unix Programming Environment o i suoi saggi e interventi, il filo comune è un'insistenza sulle idee semplici espresse con chiarezza.
I migliori consigli di Kernighan non dipendono dalla sintassi del C o dalle convenzioni Unix. Riguardano il modo in cui gli esseri umani leggono: scandagliamo per struttura, facciamo affidamento sui nomi, inferiamo l'intento e ci confondiamo quando il codice nasconde il significato dietro dei trucchi. Ecco perché il “gusto” per la leggibilità conta ancora quando scrivi TypeScript, Python, Go, Java o Rust.
I linguaggi cambiano. Gli strumenti migliorano. Le squadre continuano a consegnare funzionalità sotto pressione, e la maggior parte del codice è mantenuta da qualcun altro rispetto all'autore originale (spesso il te del futuro). La chiarezza è il moltiplicatore che rende tutto ciò sostenibile.
Non è un tributo al “coding eroico” né un invito a memorizzare regole vecchia scuola. È una guida pratica alle abitudini che rendono il codice quotidiano più facile da gestire:
L'influenza di Kernighan conta perché punta a un obiettivo semplice e orientato al team: scrivere codice che comunica. Quando il codice si legge come una spiegazione chiara, perdi meno tempo a decodificarlo e più tempo a migliorarlo.
Il “buon gusto” nella leggibilità del codice non riguarda lo stile personale, pattern alla moda o compattare una soluzione nel minor numero di righe. È l'abitudine di scegliere l'opzione semplice e chiara che comunica in modo affidabile l'intento.
Una soluzione di buon gusto risponde a una domanda fondamentale per il prossimo lettore: Cosa sta cercando di fare questo codice e perché lo fa in questo modo? Se quella risposta richiede esercizi mentali, assunzioni nascoste o il decodificare trucchi, il codice sta costando tempo al team.
La maggior parte del codice viene letta molto più spesso di quanto venga scritta. Il “buon gusto” considera la lettura come l'attività principale:
Ecco perché la leggibilità non è solo estetica (indentazione, larghezza delle righe o se preferisci snake_case). Questi sono utili, ma il “buon gusto” riguarda soprattutto facilitare il ragionamento: nomi chiari, flusso di controllo ovvio e struttura prevedibile.
Un errore comune è ottimizzare per la brevità invece che per la chiarezza. A volte il codice più chiaro è un po' più lungo perché rende espliciti i passaggi.
Per esempio, confronta due approcci:
La seconda versione può aggiungere righe, ma riduce il carico cognitivo necessario per verificare la correttezza. Rende anche più semplice isolare i bug e applicare modifiche in sicurezza.
Il buon gusto è sapere quando smettere di “migliorare” una soluzione con astuzie e invece rendere l'intento palese. Se un collega può capire il codice senza chiederti un tour, hai fatto la scelta giusta.
Il codice furbo spesso sembra una vittoria sul momento: meno righe, un trucco carino, un effetto “wow” nella diff. In un team reale, quella furberia si trasforma in un conto ricorrente—pagato in tempo di onboarding, tempo di review e esitazione ogni volta che qualcuno deve toccare di nuovo quel codice.
L'onboarding rallenta. I nuovi membri non devono solo imparare il prodotto; devono anche imparare il tuo dialetto privato di scorciatoie. Se capire una funzione richiede decodificare operatori furbi o convenzioni implicite, le persone eviteranno di cambiarla—o la cambieranno con paura.
Le review si fanno più lunghe e meno affidabili. I revisori spendono energia a dimostrare che il trucco è corretto invece di valutare se il comportamento corrisponde all'intento. Peggio ancora, il codice furbo è più difficile da simulare mentalmente, quindi i revisori perdono casi limite che avrebbero notato in una versione più lineare.
La furberia si accumula durante:
Alcuni recidivi:
17, 0.618, -1) che codificano regole che nessuno ricorda.&& / ||) che fanno affidamento sulla conoscenza di regole di valutazione sottili.Il punto di Kernighan sul “gusto” emerge qui: la chiarezza non significa scrivere di più; significa rendere l'intento ovvio. Se una versione “intelligente” fa risparmiare 20 secondi oggi ma costa 20 minuti a ogni futuro lettore, non è intelligente—è costosa.
Il “gusto” di Kernighan spesso si manifesta in decisioni piccole e ripetibili. Non serve una ristrutturazione completa per rendere il codice più facile da vivere—le piccole vittorie di chiarezza si sommano ogni volta che qualcuno scorre un file, cerca un comportamento o corregge un bug sotto pressione.
Un buon nome riduce la necessità di commenti e rende gli errori più difficili da nascondere.
Punta a nomi che rivelano l'intenzione e che corrispondono al linguaggio del team:
invoiceTotalCents a sum.Se un nome ti costringe a decodificarlo, sta facendo il contrario del suo lavoro.
La maggior parte della lettura è scansione. Spazi bianchi e struttura coerente aiutano l'occhio a trovare ciò che conta: confini di funzione, condizioni e il “percorso felice”.
Alcune abitudini pratiche:
Quando la logica si complica, la leggibilità di solito migliora rendendo le decisioni esplicite.
Confronta questi due stili:
// Harder to scan
if (user && user.active && !user.isBanned && (role === 'admin' || role === 'owner')) {
allow();
}
// Clearer
if (!user) return deny('missing user');
if (!user.active) return deny('inactive');
if (user.isBanned) return deny('banned');
if (role !== 'admin' && role !== 'owner') return deny('insufficient role');
allow();
La seconda versione è più lunga, ma si legge come una checklist—ed è più facile da estendere senza rompere.
Queste sono scelte “piccole”, ma sono l'artigianato quotidiano del codice manutenibile: nomi onesti, formattazione che guida il lettore e flusso di controllo che non richiede ginnastica mentale.
Lo stile di chiarezza di Kernighan emerge soprattutto nel modo in cui suddividi il lavoro in funzioni e moduli. Un lettore dovrebbe poter scorrere la struttura, indovinare cosa fa ogni parte e avere ragione per lo più prima di leggere i dettagli.
Punta a funzioni che facciano esattamente una cosa a un singolo “livello di zoom”. Quando una funzione mescola validazione, regole di business, formattazione e I/O, il lettore deve tenere in mente più fili contemporaneamente.
Una prova rapida: se ti ritrovi a scrivere commenti come “// ora fai X” dentro una funzione, X è spesso un buon candidato per una funzione separata con un nome chiaro.
Lunghe liste di parametri sono una tassa di complessità nascosta: ogni sito di chiamata diventa un mini-file di configurazione.
Se più parametri viaggiano sempre insieme, raggruppali con cura. Oggetti di opzioni (o piccoli struct dati) possono rendere i siti di chiamata autoesplicativi—se però mantieni il gruppo coerente ed eviti di mettere tutto in un sacco “varie”.
Inoltre, preferisci passare concetti del dominio invece di primitivi. UserId è meglio di string, e DateRange è meglio di (start, end) quando quei valori hanno regole.
I moduli sono promesse: “Tutto ciò che serve per questo concetto è qui, il resto è altrove.” Mantieni i moduli abbastanza piccoli da poterne tenere lo scopo in testa e progetta confini che minimizzino gli effetti collaterali.
Abitudini pratiche che aiutano:
Quando hai bisogno di stato condiviso, chiamalo onestamente e documenta le invarianti. La chiarezza non è evitare la complessità—è piazzarla dove i lettori se l'aspettano. Per approfondire come mantenere questi confini durante i cambiamenti, vedi /blog/refactoring-as-a-habit.
Il “gusto” di Kernighan si vede anche nel modo in cui commenti: l'obiettivo non è annotare ogni riga, ma ridurre le confusioni future. Il miglior commento è quello che previene un'errata assunzione—specialmente quando il codice è corretto ma sorprendente.
Un commento che ripete il codice (“incrementa i”) aggiunge ingombro e insegna ai lettori a ignorare i commenti. I commenti utili spiegano l'intento, i compromessi o i vincoli non evidenti dalla sintassi.
# Bad: says what the code already says
retry_count += 1
# Good: explains why the retry is bounded
retry_count += 1 # Evita il ban da throttling su errori ripetuti
Se ti senti tentato di scrivere commenti sul “cosa”, spesso è un segno che il codice potrebbe essere più chiaro (nomi migliori, funzione più piccola, flusso di controllo più semplice). Lascia che il codice porti i fatti; lascia ai commenti il compito di portare il ragionamento.
Niente mina la fiducia più di un commento obsoleto. Se un commento è opzionale, col tempo si discosta; se è sbagliato, diventa una fonte attiva di bug.
Un'abitudine pratica: tratta l'aggiornamento dei commenti come parte della modifica, non come “bello da avere”. Durante le review è giusto chiedere: Questo commento corrisponde ancora al comportamento? Se no, aggiornalo o rimuovilo. “Nessun commento” è meglio di un “commento sbagliato”.
I commenti inline sono per le sorprese locali. Le spiegazioni più ampie appartengono a docstring, README o note per gli sviluppatori—soprattutto per:
Una buona docstring dice come usare correttamente la funzione e quali errori aspettarsi, senza raccontare l'implementazione. Una breve nota in /docs o /README può catturare la storia del “perché l'abbiamo fatto così” in modo che sopravviva ai refactor.
La vittoria silenziosa: meno commenti, ma ognuno merita il suo posto.
La maggior parte del codice sembra “a posto” sul percorso felice. La vera prova del gusto è cosa succede quando mancano input, i servizi fanno timeout o un utente fa qualcosa di inaspettato. Sotto stress, il codice furbo tende a nascondere la verità. Il codice chiaro rende il fallimento ovvio—e recuperabile.
I messaggi di errore fanno parte del prodotto e del flusso di debugging. Scrivili come se la prossima persona che li legge fosse stanca e in turno di reperibilità.
Includi:
Se hai logging, aggiungi contesto strutturato (come requestId, userId o invoiceId) così il messaggio è azionabile senza scavare in dati non correlati.
C'è la tentazione di “gestire tutto” con un one-liner furbo o un catch-all generico. Il buon gusto è scegliere i pochi casi limite che contano e renderli visibili.
Per esempio, un ramo esplicito per “input vuoto” o “non trovato” spesso si legge meglio di una catena di trasformazioni che implicitamente produce null da qualche parte. Quando un caso speciale è importante, dagli un nome e mettilo in evidenza.
Mescolare forme di ritorno (a volte un oggetto, a volte una stringa, a volte false) costringe i lettori a tenere una struttura di decisione in mente. Preferisci pattern che rimangono coerenti:
La gestione chiara dei fallimenti riduce le sorprese—e la sorpresa è dove nascono bug e pagine a mezzanotte.
La chiarezza non riguarda solo ciò che tu intendevi quando hai scritto il codice. Riguarda ciò che il prossimo si aspetta di vedere quando apre un file alle 16:55. La coerenza trasforma la lettura del codice in riconoscimento di pattern—meno sorprese, meno incomprensioni, meno discussioni ricorrenti in ogni sprint.
Una buona guida di team è breve, specifica e pragmatica. Non cerca di codificare ogni preferenza; risolve le domande ricorrenti: convenzioni di naming, struttura dei file, pattern di gestione errori e cosa significa “fatto” per i test.
Il valore reale è sociale: impedisce che la stessa discussione si riapra a ogni nuova pull request. Quando qualcosa è scritto, le review passano da “Preferisco X” a “Abbiamo concordato X (e ecco perché)”. Mantienila viva e facile da trovare—molti team la pinnano nel repo (per esempio, /docs/style-guide.md) così è vicina al codice.
Usa formatter e linters per tutto ciò che è misurabile e noioso:
Questo libera le persone per concentrarsi sul significato: naming, forma delle API, casi limite e se il codice rispecchia l'intento.
Le regole manuali contano ancora quando descrivono scelte di design—per esempio, “Preferisci return anticipati per ridurre il nesting” o “Un punto d'ingresso pubblico per modulo.” Gli strumenti non possono giudicare tutto questo.
A volte la complessità è giustificata: limiti stretti di performance, vincoli embedded, concorrenza complessa o comportamenti specifici della piattaforma. L'accordo dovrebbe essere: le eccezioni sono ammesse, ma devono essere esplicite.
Uno standard semplice aiuta: documenta il compromesso in un breve commento, aggiungi una micro-benchmark o una misurazione quando si cita la performance e isola il codice complesso dietro un'interfaccia chiara così la maggior parte della codebase rimane leggibile.
Una buona review dovrebbe sentirsi meno come un'ispezione e più come una lezione breve e mirata su “buon gusto”. Kernighan non dice che il codice furbo sia malvagio—dice che la furberia è costosa quando altre persone devono conviverci. Le review sono il luogo dove il team può rendere visibile quel compromesso e scegliere la chiarezza di proposito.
Inizia chiedendo: “Un collega può capire questo al primo passaggio?” Di solito significa guardare naming, struttura, test e comportamento prima di tuffarsi in micro-ottimizzazioni.
Se il codice è corretto ma difficile da leggere, considera la leggibilità come un vero difetto. Suggerisci di rinominare variabili per riflettere l'intento, spezzare funzioni lunghe, semplificare il flusso di controllo o aggiungere un piccolo test che dimostri il comportamento atteso. Una review che cattura “funziona, ma non capisco perché” previene settimane di confusione futura.
Un ordine pratico che funziona bene:
Le review vanno fuori strada quando il feedback suona come segnare punti. Invece di “Perché lo fai così?” prova:
Le domande invitano alla collaborazione e spesso portano alla luce vincoli che non conoscevi. I suggerimenti comunicano direzione senza implicare incompetenza. Questo tono è come il “gusto” si diffonde in un team.
Se vuoi leggibilità consistente, non affidarti all'umore del revisore. Aggiungi qualche “check di chiarezza” al template di review e alla definition of done. Mantienili brevi e specifici:
Col tempo, questo trasforma le review da polizia di stile a insegnamento del giudizio—esattamente la disciplina quotidiana che Kernighan auspicava.
Gli strumenti LLM possono produrre codice funzionante rapidamente, ma “funziona” non è la soglia su cui Kernighan puntava—comunica lo è. Se il tuo team usa un flusso vibe-coding (per esempio, costruire feature via chat e iterare su codice generato), vale la pena trattare la leggibilità come criterio di accettazione di primo livello.
Su piattaforme come Koder.ai, dove puoi generare frontend React, backend Go e app Flutter da un prompt di chat (e poi esportare il sorgente), valgono le stesse abitudini guidate dal gusto:
La velocità è più preziosa quando l'output resta facile da rivedere, mantenere ed estendere per gli esseri umani.
La chiarezza non è qualcosa che si “raggiunge” una volta. Il codice rimane leggibile solo se continui a spingerlo verso un linguaggio semplice man mano che cambiano i requisiti. La sensibilità di Kernighan si adatta qui: preferisci miglioramenti costanti e comprensibili piuttosto che riscritture eroiche o one-liner “intelligenti” che impressionano oggi e confondono il mese prossimo.
Il refactoring più sicuro è noioso: cambiamenti minuscoli che mantengono il comportamento identico. Se hai test, eseguili dopo ogni passo. Se non li hai, aggiungi qualche controllo mirato intorno all'area che tocchi—pensa a quelli come guardrail temporanei così puoi migliorare la struttura senza paura.
Un ritmo pratico:
Commit piccoli rendono anche la review più facile: i colleghi possono giudicare l'intento, non inseguire effetti collaterali.
Non devi eliminare ogni costrutto “furbo” in un colpo solo. Quando tocchi il codice per una feature o un bugfix, sostituisci le scorciatoie con equivalenti più chiari:
Così la chiarezza vince nei team reali: un hotspot migliorato alla volta, proprio dove le persone stanno già lavorando.
Non tutto il cleanup è urgente. Una regola utile: refattorizza ora quando il codice è attivamente modificato, spesso frainteso o probabile causa di bug. Programma per dopo quando è stabile e isolato.
Rendi visibile il debito di refactoring: lascia un breve TODO con contesto, o apri un ticket che descriva il dolore (“difficile aggiungere nuovi metodi di pagamento; la funzione fa 5 lavori”). Così puoi decidere deliberatamente—invece di lasciare che il codice confuso diventi la tassa permanente del team.
Se vuoi che il “buon gusto” emerga con costanza, rendilo facile da praticare. Ecco una checklist leggera che puoi riutilizzare in pianificazione, sviluppo e review—sufficientemente breve da ricordare, abbastanza specifica da agire.
Prima: process(data) fa validazione, parsing, salvataggio e logging tutto in un posto.
Dopo: Dividi in validateInput, parseOrder, saveOrder, logResult. La funzione principale diventa un sommario leggibile.
Prima: if not valid then return false ripetuto cinque volte.
Dopo: Una sezione upfront di guardia (o una singola funzione di validazione) che restituisce una lista chiara di problemi.
Prima: x, tmp, flag2, doThing().
Dopo: retryCount, draftInvoice, isEligibleForRefund, sendReminderEmail().
Prima: Un ciclo con tre casi speciali nascosti nel mezzo.
Dopo: Gestisci i casi speciali per primi (o estrai helper), poi esegui il ciclo semplice.
Scegli una miglioramento da adottare questa settimana: “nessuna nuova abbreviazione”, “happy path first”, “estrai un helper per PR” o “ogni messaggio di errore include i passi successivi”. Monitoralo per sette giorni e poi mantieni ciò che davvero ha reso la lettura più semplice.
L'influenza di Kernighan riguarda meno il linguaggio C e più un principio duraturo: il codice è un mezzo di comunicazione.
I linguaggi e i framework cambiano, ma i team hanno sempre bisogno di codice facile da scansionare, comprendere, revisionare e debuggare—soprattutto a mesi di distanza e sotto pressione.
“Buon gusto” significa scegliere in modo coerente l'opzione più semplice e chiara che comunichi l'intento.
Un buon test è: un collega può rispondere a “cosa fa questo e perché è fatto così?” senza dover decodificare trucchi o fare affidamento su assunzioni nascoste.
Perché la maggior parte del codice viene letto molto più spesso di quanto venga scritto.
Ottimizzare per i lettori riduce i tempi di onboarding, gli attriti nelle review e il rischio di cambiamenti errati—specialmente quando il manutentore è il “tu futuro” con meno contesto.
La “tassa” della trovata brillante si manifesta come:
Se la versione furba fa risparmiare secondi ora ma costa minuti ogni volta che viene modificata, è una perdita netta.
Colpevoli comuni:
Questi pattern tendono a nascondere lo stato intermedio e rendono più facile dimenticare i casi limite durante la revisione.
Quando riduce il carico cognitivo.
Esplicitare i passaggi con variabili nominate (ad esempio: validate → normalize → compute) può rendere più semplice verificare la correttezza, semplificare il debug e rendere i cambiamenti futuri più sicuri—anche se aggiunge qualche riga.
Abitudini semplici ed efficaci:
Se devi decodificare un nome, non sta facendo il suo lavoro: il nome dovrebbe ridurre la necessità di commenti.
Preferisci ramificazioni semplici ed esplicite e mantieni la “happy path” visibile.
Tattiche utili:
Commenta il perché, non il cosa.
I buoni commenti catturano intenti, compromessi, vincoli o invarianti non ovvie. Evita di narrare codice ovvio e tratta l'aggiornamento dei commenti come parte della modifica stessa: i commenti obsoleti sono peggiori dell'assenza di commenti.
Usa gli strumenti per le regole meccaniche (formattazione, import, errori ovvi) e riserva la revisione umana al significato.
Una guida leggera allo stile aiuta a risolvere decisioni ricorrenti (naming, struttura, pattern di gestione errori) così le review diventano su chiarezza e comportamento, non su preferenze personali.
Quando fai eccezioni per prestazioni o vincoli, documenta il compromesso e isola la complessità dietro un'interfaccia chiara.