Code Smell: I 12 Più Comuni e Come Risolverli
I 12 code smell più comuni, cosa indicano sul debito tecnico e i passaggi concreti per correggerli senza rompere la produzione.
In questo articolo:
- Cosa sono i code smell e perché sono importanti
- Code smell strutturali (da 1 a 6)
- Code smell comportamentali e di processo (da 7 a 12)
- Come risolvere i code smell senza rompere la produzione
- Conclusione
Cosa sono i code smell e perché sono importanti
I code smell sono indicatori superficiali di problemi più profondi in un codebase. Il termine, reso popolare da Martin Fowler e Kent Beck, si riferisce a caratteristiche del codice che suggeriscono la presenza di debito tecnico, design scadente o fragilità, anche se il codice tecnicamente funziona. Non sono bug. Il codice gira. Ma ha proprietà che lo rendono più difficile da modificare, testare e mantenere del necessario.
Cosa sono i code smell in termini pratici? Sono i pattern che spingono gli ingegneri a dire “non voglio toccare questo” o “non sono sicuro di cosa si romperebbe se cambiassi questo.” Sono le firme di un codebase che ha accumulato decisioni che avevano senso individualmente ma creano frizione collettiva su scala.
I 12 code smell descritti qui appaiono in modo coerente nei codebase che valutiamo. Ciascuno ha una firma specifica che può essere identificata tramite analisi statica o code review, e ciascuno ha un percorso di remediation definito che non richiede una riscrittura completa.
Code smell strutturali (da 1 a 6)
**1. **God Class. Una classe con troppe responsabilità. Firme tipiche: 500+ righe, 30+ metodi, dipendenze da un gran numero di altre classi. La God Class viola il Principio di Singola Responsabilità e crea un collo di bottiglia dove convergono cambiamenti di molti tipi. Correzione: applicare il refactoring Extract Class per separare le responsabilità distinte nelle loro classi, iniziando dai metodi che non condividono stato con il resto.
2. Metodo Lungo. Una funzione o metodo che cerca di fare troppo in un singolo blocco di codice. Le funzioni sopra le 50 righe sono difficili da testare e capire. Correzione: refactoring Extract Method, suddividendo il metodo lungo in sotto-funzioni più piccole e con nome che descrivono cosa fa ogni passo.
3. Feature Envy. Un metodo che trascorre più tempo ad accedere ai dati di un’altra classe che ai propri. Questo indica che il metodo si trova nella classe sbagliata. Correzione: spostare il metodo nella classe di cui utilizza maggiormente i dati.
4. Spaghetti code. Codice in cui il flusso di controllo salta tra funzioni, event handler e moduli in modi che non possono essere tracciati linearmente. Questo pattern emerge dall’aggiunta iterativa di funzionalità senza revisione architetturale. Correzione: mappare il flusso effettivo e introdurre un chiaro layer di orchestrazione.
5. Data Clumps. Gruppi di dati che appaiono sempre insieme, come dataInizio, dataFine e fuso_orario passati come tre parametri separati a cinque metodi diversi. La correzione è creare un value object che li incapsula.
6. Primitive Obsession. Usare tipi primitivi (stringhe, interi) per rappresentare concetti di dominio. Un numero di telefono memorizzato come stringa senza validazione è una primitive obsession. La correzione è introdurre value object con validazione e comportamento integrati.
Code smell comportamentali e di processo (da 7 a 12)
7. Shotgun Surgery. Una singola modifica richiede cambiamenti a molte classi diverse. Questo indica che una preoccupazione è distribuita nel codebase piuttosto che incapsulata. Ogni volta che una regola di business cambia, deve essere applicata in dieci posti. Correzione: consolidare la preoccupazione dispersa in un unico luogo.
8. Divergent Change. Una singola classe viene modificata per molte ragioni diverse. Il modulo di autenticazione viene modificato quando cambia l’UI di login, quando cambia la gestione delle sessioni e quando cambiano i requisiti di audit. Sono preoccupazioni diverse che vivono nello stesso posto. Correzione: estrarre ogni preoccupazione nel suo modulo.
**9. **Codice morto. Codice che non viene mai eseguito: variabili inutilizzate, rami irraggiungibili, metodi deprecati lasciati per sicurezza. Il codice morto crea overhead cognitivo e può contenere vulnerabilità di sicurezza. Correzione: identificare tramite analisi statica o strumenti di copertura ed eliminare. Il sistema di controllo versione preserva la cronologia.
10. Generalizazione Speculativa. Codice scritto per gestire casi che non esistono ancora, “per ogni evenienza.” Hook inutilizzati, classi astratte con una singola implementazione, opzioni di configurazione che nessuno imposta mai. Correzione: rimuovere l’astrazione e semplificare fino a quando non ci sia un requisito reale.
11. Message Chains. Codice che chiama un metodo, ottiene un oggetto, chiama un altro metodo, ottiene un altro oggetto, e così via: utente.getIndirizzo().getCitta().getPaese().getCodice(). Questo crea un accoppiamento stretto alla struttura interna di ogni oggetto intermedio. Correzione: applicare la Legge di Demetra e introdurre metodi intermediari che incapsulano la catena.
12. Intimità Inappropriata. Due classi che accedono ai membri privati dell’altra o sono strettamente consapevoli delle implementazioni interne dell’altra. Correzione: introdurre un’interfaccia o un confine chiaro tra le due classi.
Come risolvere i code smell senza rompere la produzione
Il rischio nell’affrontare i code smell in un codebase in produzione è che il refactoring cambia il comportamento del codice in modi che i test non rilevano. L’approccio più sicuro segue una sequenza coerente.
Primo, assicurati la copertura test del codice che stai per modificare. Se la copertura è assente, scrivi test di caratterizzazione: test che documentano il comportamento attuale, anche se quel comportamento è sbagliato.
Secondo, applica il passo di refactoring più piccolo possibile. Estrai un metodo, una classe o un value object alla volta. Esegui i test dopo ogni passo. Se passano, commit e continua. Se falliscono, ripristina e investiga.
Terzo, verifica che le metriche di salute del codice pertinenti siano migliorate dopo la modifica.
Il nostro servizio di legacy modernization applica questo approccio incrementale a codebase con anni di code smell accumulati, producendo miglioramenti misurabili senza downtime in produzione.
Conclusione
I code smell sono la manifestazione superficiale del debito tecnico. Non indicano sempre una crisi, ma sono segnali di avvertimento affidabili. I 12 descritti qui coprono i pattern che troviamo in quasi ogni codebase che valutiamo.
Affrontarli richiede la stessa disciplina della gestione di qualsiasi altro investimento tecnico: identificare, prioritizzare per impatto sul business, applicare correzioni incrementali con copertura test e verificare il risultato rispetto a metriche misurabili.
Eden Technologies ha aiutato oltre 200 team di engineering a passare da codebase dove “nessuno vuole toccare niente” a sistemi dove le modifiche sono sicure, i deployment sono prevedibili e gli incidenti sono rari.
Hai un codebase con questi problemi? Parliamo del tuo sistema