Clean Code

Clean code: principi pratici per scrivere meno e meglio 🧼

Scrivere buon codice non è una gara a chi conosce più design pattern, ma a chi consegna sistemi chiari, testabili e facili da mantenere. Tre principi aiutano più di altri: DRY, KISS e il sano realismo di “less code, less bugs”. Qui trovi come usarli senza religione, con esempi concreti e una checklist pronta per le PR.

Perché il clean code conta 🧠

Il codice “pulito” non è quello più elegante, ma quello che il tuo collega capisce al primo sguardo (anche quando il collega sei tu tra 6 mesi). Benefici immediati:

  • meno bug e regressioni
  • cicli di sviluppo più veloci
  • costi di manutenzione più bassi
  • team meno dipendenti dai “guru del progetto”

DRY: non ripeterti 🧴

Il principio DRY (Don’t Repeat Yourself) dice che ogni informazione dovrebbe avere una sola rappresentazione autorevole nel sistema. Come sintetizzato in modo celebre: “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.” Formulato da Andy Hunt e Dave Thomas in “The Pragmatic Programmer”, si applica anche a schemi di database, piani di test, build system e documentazione.

Quando applicarlo:

  • estrai funzioni/metodi per logiche ripetute
  • centralizza validazioni, mapping e formattazioni
  • usa un’unica fonte di verità per costanti e configurazioni

Attenzione alle trappole:

  • DRY non è “creare una mega-astrazione”. Se per rimuovere due righe duplicate ne introduci cinquanta, hai perso.
  • non astrarre troppo presto: la duplicazione moderata nelle prime iterazioni può chiarire il design.

Esempio prima/dopo (Go):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Prima: stessa logica ripetuta
func PriceWithVATIT(p float64) float64 { return p * 1.22 }
func PriceWithVATDE(p float64) float64 { return p * 1.19 }

// Dopo: DRY con singola fonte di verità
const (
  VATIT = 0.22
  VATDE = 0.19
)

func PriceWithVAT(p, rate float64) float64 { return p * (1 + rate) }
// Uso
// final := PriceWithVAT(100, VATIT) // 122

Nota: per importi monetari reali evita float64 (problemi di arrotondamento binario). Usa rappresentazioni a centesimi (int64), librerie decimal o big.Rat e applica regole fiscali di rounding (es. 2 decimali commerciali, half-up).


KISS: mantienilo semplice 😌

Il principio KISS (Keep It Simple, Stupid) invita a scegliere la soluzione più semplice che funziona. Nato in ambito militare (U.S. Navy, anni ’60), ricorda che la semplicità è spesso l’asset più affidabile. Varianti popolari: Keep it short and simple e Keep it simple and straightforward. Non serve un microservizio per ogni if. Alcune pratiche:

  • preferisci funzioni piccole con nomi chiari
  • evita ottimizzazioni premature: prima la correttezza, poi la velocità
  • riduci gli indici di complessità ciclomatica
  • scegli strutture dati semplici finché bastano

Antipattern tipici da evitare:

  • over-engineering “perché un giorno potrebbe servire”
  • astrazioni a strati che nascondono la logica
  • configurazioni iper-dinamiche dove basterebbe un enum
  • ignorare YAGNI (You Aren’t Gonna Need It): costruire oggi ciò che non ha ancora validato un bisogno reale

Less code, less bugs: misura e riduci 📉

Meno codice significa meno superfici d’errore, meno dipendenze mentali, meno merge conflittuali. Non è minimalismo estetico: è gestione del rischio. Meno codice significa meno superfici d’errore, meno dipendenze mentali, meno conflitti di merge. Non è minimalismo estetico: è gestione del rischio.

Come ridurre:

  • elimina feature morte o mai usate (misura con telemetria)
  • elimina funzionalità morte o inutilizzate (telemetria: endpoint non chiamati, flag sempre off)
  • sostituisci 200 righe di “plumbing” con una libreria affidabile
  • accorpa funzioni quasi identiche con parametri ben tipizzati
  • automatizza boilerplate (generator, template, snippet)

Misura ciò che conta:

  • complessità per file/modulo
  • percentuale di duplicazione
  • tempo medio di onboarding su una codebase

Quando NON applicare DRY e KISS alla cieca 🚧

  • prototipi ed esplorazione: duplicare può essere più veloce e chiarificatore
  • domini ancora instabili: astrazioni premature invecchiano male
  • performance critiche: a volte un po’ di “bruttezza” localizzata è l’ottimo globale

Regola d’oro: duplica prima, astrai quando capisci davvero il pattern. Corollario: applica YAGNI — implementa solo ciò che serve ora; il resto quando (e se) emerge un requisito reale.


Checklist per review e refactoring ✅

  • il nome della funzione dice davvero cosa fa?
  • la funzione / il modulo ha una singola responsabilità chiara (SRP)?
  • c’è duplicazione evidente di logica o costanti?
  • la soluzione più semplice funzionerebbe lo stesso?
  • posso cancellare qualcosa senza che nessuno se ne accorga?
  • i test descrivono il comportamento, non l’implementazione?

Ricette veloci 🍝

  • estrai funzione: quando vedi 2+ blocchi identici o simili
  • sostituisci condizionali annidati con guard clause
  • promuovi costanti “magiche” a nomi espliciti
  • sposta la logica vicino ai dati che la usano
  • aggiungi test prima di toccare codice sospetto

Strumenti utili 🔧

  • linters e formatter (golangci-lint, eslint, rubocop)
  • analisi duplicazioni (SonarQube, PMD CPD)
  • metriche di complessità (gocyclo, complexity-report)
  • metriche di complessità (gocyclo per Go, complexity-report per codebase JS)
  • refactoring assistito dall’IDE (rename, extract method, inline)

In sintesi 🧾

Applica DRY per rimuovere ripetizioni, KISS per evitare ghirigori inutili e ricordati che meno codice spesso significa meno problemi. Lavora in piccoli passi, misura l’impatto e lascia il codice un po’ migliore di come l’hai trovato.

Ultimo aggiornamento il