Vai al contenuto
Anti-pattern

Anti-pattern di design che costano più dei bug 🚨

I bug fanno danni, ma gli anti-pattern di design hanno un talento speciale: fanno danni lentamente, in modo credibile, e spesso con l’aria di essere stati introdotti per “mettere ordine”. Il problema è che alzano il costo del cambiamento fino a rendere ogni intervento un’operazione delicata.

Questa guida non serve a collezionare etichette. Serve a riconoscere odori strutturali prima che diventino l’arredamento stabile della codebase.

God Object: tutto passa da lui 👑

Il God Object concentra troppe responsabilità in un solo componente. Conosce tutto, decide tutto, coordina tutto e probabilmente avrebbe opinioni anche sulla tua roadmap personale.

Segnali tipici:

  • file enormi e ingestibili
  • dipendenze ovunque
  • test lenti e fragili
  • paura diffusa di toccare quel componente

Correzione tipica:

  • separare responsabilità per motivo di cambiamento
  • estrarre servizi o moduli coesi
  • ridurre le dipendenze ai bordi

Spaghetti Code: flusso senza geometria 🍝

Lo Spaghetti Code non è solo codice brutto. È codice in cui il flusso è difficile da seguire, le dipendenze non sono leggibili e la modifica di un comportamento genera effetti collaterali poco prevedibili.

Segnali tipici:

  • condizionali annidati ovunque
  • logica distribuita in punti lontani
  • stato mutato da troppi attori
  • debugging che somiglia a un inseguimento in città senza mappa

Correzione tipica:

  • chiarire il flusso con funzioni più piccole e responsabilità nette
  • ridurre stato condiviso e coupling implicito
  • isolare I/O, orchestrazione e logica di dominio

Premature Abstraction: quando astrarre troppo presto costa caro 🏗️

La Premature Abstraction nasce da un’intenzione rispettabile: evitare duplicazione e prepararsi al futuro. Il risultato, spesso, è un layer generico che non rappresenta bene nessun caso reale.

Segnali tipici:

  • nomi vaghi come BaseManager, GenericService, AbstractProcessor
  • parametri e flag che cambiano il comportamento in dieci modi diversi
  • codice difficile da capire perché l’astrazione è più complicata dei casi concreti

Correzione tipica:

  • riportare il design più vicino ai casi reali
  • accettare una duplicazione temporanea quando chiarisce il dominio
  • astrarre solo dopo aver visto un pattern stabile

Shotgun Surgery: un cambiamento, venti file 💥

La Shotgun Surgery è l’anti-pattern in cui una singola modifica costringe a ritoccare molti punti diversi della codebase.

Segnali tipici:

  • ogni feature banale richiede una lista infinita di micro-modifiche
  • logica correlata sparsa in troppi moduli
  • alta probabilità di dimenticare un pezzo

Correzione tipica:

  • ricomporre comportamento e dati che cambiano insieme
  • aumentare la coesione
  • rendere espliciti i confini tra responsabilità

Golden Hammer: un pattern per dominarli tutti 🔨

Il Golden Hammer è l’abitudine di applicare sempre la stessa soluzione, indipendentemente dal problema. Succede con i microservizi, con DDD, con i workflow engine, con gli event bus e, sì, anche con la dependency injection.

Segnali tipici:

  • la soluzione arriva prima dell’analisi
  • ogni problema assomiglia al tool preferito del team
  • il contesto concreto conta meno della moda tecnica in corso

Correzione tipica:

  • ripartire dal problema e dai vincoli
  • confrontare più opzioni con trade-off espliciti
  • evitare architetture prestazionali per requisiti immaginari

Due anti-pattern moderni molto comuni 🧪

Interfacce premature

Creare interfacce ovunque “così siamo flessibili” parte da un’intenzione rispettabile. Il risultato, spesso, è una codebase che simula varietà senza averne il bisogno reale: contratti artificiali, boilerplate abbondante e un’illusione di testabilità che non regge alla prima pressione.

Segnali tipici:

  • interfacce con un unico implementatore, destinato probabilmente a restare tale
  • naming ridondante del tipo UserRepository, UserRepositoryInterface, UserRepositoryImpl
  • mock che vengono copiati spesso e aggiornati raramente quando il contratto cambia
  • wiring elaborato per sostituire qualcosa che in produzione non si sostituirà mai

Correzione tipica:

  • introdurre l’interfaccia solo quando esiste o è concretamente attesa una seconda implementazione
  • tenerla vicina al chiamante, non come ornamento architetturale condiviso
  • ricordare che uno struct concreto con dipendenze esplicite è già testabile senza interfacce aggiuntive

DI ovunque

La dependency injection è utile, ma applicarla indiscriminatamente trasforma una codebase ordinata in una sfilata di costruttori con parametri inutili, mock che non si sostituiscono mai e wiring che nessuno riesce più a tracciare. È il Golden Hammer applicato alla gestione delle dipendenze.

Segnali tipici:

  • costruttori con molti parametri, metà dei quali non vengono mai variati in test o runtime
  • mock di dipendenze che non cambiano mai tra un test e l’altro e che costano più che usare la versione reale
  • container o registry che nascondono il grafo reale dell’applicazione dietro configurazione implicita
  • code review dedicate a discutere “dove va iniettato X” invece che la logica effettiva

Correzione tipica:

  • applicare DI dove c’è vera variabilità: repository, clock, gateway, notifier
  • lasciare concrete le dipendenze stabili che non cambieranno mai
  • tenere il wiring nel main o in entry point chiari, non distribuito ovunque

Come usare questa guida in review 🔍

Domande utili:

  • questo componente sta facendo troppo?
  • questa astrazione è nata da un caso reale o da ansia preventiva?
  • un cambiamento locale resta locale?
  • il team sta applicando uno schema perché utile o perché familiare?

In sintesi 🧾

Gli anti-pattern di design non sono peccati morali. Sono segnali di un sistema che sta diventando più costoso da cambiare, capire e testare. Riconoscerli presto vale più di tante retrospettive drammatiche fatte quando il danno è già diventato struttura.

Per i principi che aiutano a prevenirli, vedi principi di software design, Clean code e Dependency Injection e Inversion of Control.

Ultimo aggiornamento il