Ogni sviluppatore l'ha sentito: "Don't Repeat Yourself". È uno dei primi principi che impariamo, e sembra così ragionevole che raramente lo mettiamo in discussione. Codice duplicato? Estrarlo. Un pattern? Astrarlo. Tre righe simili? È il momento di una funzione di utilità.
Ma dopo 16 anni di sviluppo software in diverse industrie e paesi — dalle piattaforme di e-learning a Roma ai sistemi di trading finanziario a Zurigo — sono arrivato a una conclusione diversa: YAGNI — "You Aren't Gonna Need It" — è il principio più prezioso, e l'adesione cieca a DRY danneggia attivamente le codebase.
La trappola DRY
DRY è stato introdotto da Andy Hunt e Dave Thomas in "The Pragmatic Programmer". La loro definizione originale riguardava la conoscenza: "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system." Notate la parola: conoscenza, non codice.
Ciò che la maggior parte degli sviluppatori pratica non è DRY — è "Don't Repeat Code", che è fondamentalmente diverso. Due pezzi di codice possono sembrare identici oggi ma rappresentare concetti di business completamente diversi che divergeranno domani.
L'ho visto accadere molte volte: la logica di validazione per due flussi diversi sembra identica all'inizio. Uno sviluppatore ben intenzionato li unisce in un'utilità condivisa. Mesi dopo, cambiamenti normativi o decisioni di prodotto richiedono regole diverse per ogni flusso, e districare quell'utilità condivisa significa toccare ogni chiamante, riscrivere i test e coordinare i deployment tra team.
// These look the same, but they're NOT the same knowledge
function validateUserEmail(email: string) {
return email.includes('@') && email.length > 5;
}
function validateNewsletterEmail(email: string) {
return email.includes('@') && email.length > 5;
}
// Merging them into validateEmail() creates coupling
// between user registration and newsletter signup.
// When newsletter needs to accept '+' aliases but
// registration doesn't, you'll fight the abstraction.Perché YAGNI vince
YAGNI dice: non costruirlo finché non ne hai bisogno. Non astrarre finché non hai visto il vero pattern. Non generalizzare finché non capisci i casi specifici. Non è pigrizia — è disciplina.
Il costo di un'astrazione sbagliata è molto più alto del costo della duplicazione. La duplicazione è ovvia e locale — la vedi, e correggerla è semplice. Un'astrazione sbagliata è sottile e virale — plasma il modo in cui le persone pensano al codice, e disfarsene significa rifattorizzare ogni chiamante.
Seguo la "Rule of Three": non astrarre finché non hai visto tre utilizzi genuinamente indipendenti. E anche allora, chiediti se la duplicazione rappresenta la stessa conoscenza o solo codice dall'aspetto simile.
In Migros, quando abbiamo ricostruito Bikeworld e Micasa su un monorepo Next.js condiviso, la tentazione di condividere tutto tra i negozi era enorme. Ma le pagine prodotto per biciclette e mobili hanno percorsi utente fondamentalmente diversi. Abbiamo tenuto i componenti separati finché non sono emersi pattern condivisi reali in modo organico — e il risultato è stata una codebase dove ogni negozio poteva evolversi indipendentemente senza coordinare i rilasci.
Il vero costo dell'astrazione prematura
Ho visto questo pattern ripetersi decine di volte nei team che ho guidato — dalle piccole agenzie in Italia ai team enterprise di AXA e UBS. Qualcuno crea un componente o utilità "condivisa" all'inizio. Funziona benissimo per due casi d'uso. Poi arriva un terzo che quasi ci sta ma ha bisogno di un flag. Poi un quarto che ne richiede un altro. Presto hai una funzione con sei parametri booleani, e nessuno riesce a capire cosa faccia realmente.
Lo chiamo "debito di astrazione". A differenza del debito tecnico, visibile nei tempi di build e nelle liste di dipendenze, il debito di astrazione si nasconde nella velocity del team. Gli sviluppatori rallentano perché hanno paura di modificare codice condiviso. Le nuove feature richiedono più tempo perché devono aggirare astrazioni progettate per altri casi d'uso.
// The abstraction graveyard — we've all seen this
function formatData(
data: unknown,
isUser?: boolean,
isAdmin?: boolean,
includeMetadata?: boolean,
skipValidation?: boolean,
legacyMode?: boolean
) {
// 200 lines of branching logic
// that nobody dares to touch
}YAGNI nella pratica
Ecco come applico YAGNI nelle decisioni quotidiane:
Costruendo la piattaforma Vontobel Markets, avevamo bisogno di aggiornamenti dei prezzi in tempo reale tramite WebSocket. L'istinto iniziale era costruire un layer di dati real-time generico che potesse gestire qualsiasi caso futuro di streaming. Invece, abbiamo costruito esattamente ciò di cui il ticker dei prezzi aveva bisogno — niente di più. Quando è arrivata una seconda feature real-time (avvisi portfolio), capivamo entrambi i casi d'uso abbastanza bene da progettare qualcosa che servisse genuinamente entrambi, invece di tirare a indovinare in anticipo.
In UBS, nell'unificazione dell'applicazione degli Administrative Officers, ho attivamente sconsigliato la creazione di un "framework universale per i form" che gestisse ogni form nel sistema. Invece, ogni team ha costruito i propri form secondo le proprie esigenze, e abbiamo identificato i veri pattern condivisi solo dopo che diversi team avevano consegnato. I pattern che alla fine abbiamo estratto erano più semplici e robusti di qualsiasi cosa avessimo potuto progettare in anticipo.
Cosa dico ai miei team
Quando inserisco nuovi sviluppatori, condivido questo modello mentale: la duplicazione è molto meno costosa dell'astrazione sbagliata. Copia-incolla quelle tre righe. Lascia che il pattern emerga naturalmente. Quando hai visto lo stesso concetto — non lo stesso codice, lo stesso concetto — apparire tre o più volte, allora considera se un'astrazione chiarificherebbe o oscurerebbe.
"Make sense" è uno dei miei principi fondamentali. Ogni decisione deve avere senso nel contesto, non semplicemente seguire una regola. DRY ha senso quando stai genuinamente codificando conoscenza condivisa. YAGNI ha senso quasi ovunque altrove.
Il miglior codice che ho consegnato non era il più intelligente o il più DRY. Era il codice dove ogni pezzo esprimeva chiaramente la sua intenzione, dove si poteva cambiare una cosa senza preoccuparsi di romperne cinque, e dove i nuovi membri del team potevano capirlo senza una visita guidata.
La prossima volta che cerchi un'utilità condivisa, fermati. Chiediti: "Sto astraendo perché vedo un vero pattern, o perché mi è stato detto che la ripetizione è sbagliata?" Quella pausa vale più di qualsiasi design pattern.

