Code smell

Il termine code smell viene usato quando il codice in esame è stato scritto senza seguire buone pratiche di programmazione. La presenza di uno o più code smell è sintomo di un problema strutturale nel codice. Solitamente è accompagnata dalla sensazione che qualcosa non va. Nel seguente articolo elenchiamo i code smell più comuni e vi spieghiamo come eliminarli.

Cos’è un code smell?

Si parla di code smell quando, esaminando il codice, un programmatore o una programmatrice esperta ha la sensazione che questo non sia pulito. L’abilità di riconoscere i code smell può essere paragonata all’intuizione di un maestro artigiano che guarda un groviglio di fili o dei cavi mal installati e si rende subito conto che qualcosa non va. Esistono alcuni pattern nel codice sorgente che possono rivelare la presenza di un code smell.

Secondo il noto programmatore britannico Martin Fowler, il termine “code smell” sarebbe stato coniato dal collega Kent Beck. Il blog di Fowler fornisce la seguente definizione:

Citazione

“A Code Smell is a surface indication that usually corresponds to a deeper problem in the system.” – Fonte: https://martinfowler.com/bliki/CodeSmell.html
Traduzione: “Un code smell è un indicatore superficiale, che di solito rivela un problema più profondo nel sistema.” (traduzione di IONOS)

Un code smell indica quindi l’esistenza di un problema di metodo. Il codice è stato scritto da un programmatore o da una programmatrice con scarse competenze, oppure da sviluppatori sempre diversi. In quest’ultimo caso la scarsa qualità del codice si deve a uno scarso livello di competenze, a una conoscenza limitata del codebase e a poca familiarità con linee guida e standard. È come se il codice iniziasse a puzzare.

I code smell non vanno confusi con i singoli bug normalmente presenti in tutti i codebase, ma devono essere considerati semplici indicatori. Se un programmatore o una programmatrice si rende conto della presenza di un code smell, dovrà mettere in atto ulteriori verifiche per determinare se esiste effettivamente un problema di metodo.

Se ci sembra che nel codice ci sia qualcosa che non va, possiamo pulirlo in vari modi. Il refactoring serve proprio a questo ed è un’opzione per migliorare la struttura del codice mantenendone invariata la funzionalità. A seconda della portata e della complessità del codice, non sempre è possibile eliminare i code smell. In questi casi non resta che eseguire un “re-write”, ovvero riscrivere il codice da capo.

Quali sono i diversi tipi di code smell?

Il codice sorgente è un sistema che si sviluppa su diversi livelli di astrazione. Si pensi a un’applicazione, formata da diverse componenti, tutte definite all’interno del codice: variabili, funzioni, classi, moduli. Di seguito esamineremo i diversi code smell distinguendo fra i diversi gradi di astrazione:

  1. Code smell generici
  2. Code smell a livello di funzione
  3. Code smell a livello di classe
  4. Code smell a livello di applicazione

Code smell generici

I code smell generici sono presenti in tutti i livelli di un’applicazione. Nella maggior parte dei casi non si tratta di errori evidenti, ma di pattern che influenzano il codice in modo negativo. Secondo uno dei principi di base della programmazione, il codice dovrebbe essere scritto principalmente per le persone che lo leggono. Tuttavia, chi ha iniziato da poco a programmare e ha poca esperienza, scrive spesso un codice che sortisce gli effetti desiderati, ma che di fatto è incomprensibile.

Denominazione inadeguata dei costrutti del codice

I code smell generici sono presenti in tutti i livelli di un’applicazione. Nella maggior parte dei casi non si tratta di errori evidenti, ma di pattern che influenzano il codice in modo negativo. Secondo uno dei principi di base della programmazione, il codice dovrebbe essere scritto principalmente per le persone che lo leggono. Tuttavia, chi ha iniziato da poco a programmare e ha poca esperienza, scrive spesso un codice che sortisce gli effetti desiderati, ma che di fatto è incomprensibile.

Stile di programmazione non uniforme

Quando il codice è pulito, sembra sia stato scritto da una sola persona; risulta immediatamente visibile che il codice è nato sulla base di regole ben definite. Se manca la coerenza, ci troviamo di fronte a un code smell. Un codice le cui denominazioni non sono riconducibili a una convenzione lascia intuire che diverse persone ci hanno lavorato in modo indipendente le une dalle altre. Se il codice è stato scritto in team, potrebbero mancare delle linee guida uniformi.

Consiglio

Vi abbiamo incuriosito? Leggete anche l’articolo “Cos’è il clean code?”.

Variabili non definite

Alcuni linguaggi di programmazione come C++, Java e JavaScript consentono di dichiarare una variabile senza doverne definire il valore iniziale. La variabile esiste dal momento in cui la si dichiara, ma se non è definita può causare dei bug nascosti. Questo code smell è particolarmente insidioso nei linguaggi debolmente tipizzati: infatti, in mancanza di una definizione iniziale, non è possibile riconoscere qual è il tipo della variabile.

Valori a codifica fissa nel codice

Un errore che i programmatori inesperti fanno spesso: per comparare una variabile con un valore specifico, inseriscono direttamente il valore. In questo caso di parla di “valori a codifica fissa” o di “valori cablati”. Quando compaiono in diversi punti del programma, i valori a codifica fissa risultano problematici, questo perché le singole copie tendono a mutare nel tempo, indipendentemente l’una dall’altra. La cosa migliore da fare è definire una costante riferita al valore. In tal modo si definisce una “Single Source of Truth” (SSOT): qualsiasi parte del codice che si serve di quel valore accede alla stessa costante, definita in un singolo punto.

Numeri magici all’interno del codice

I numeri magici sono un tipo particolare di valori a codifica fissa. Immaginate che il nostro codice lavori con un valore limitato a 24 bit. 24 bit corrispondono a 16.777.216 valori possibili, ovvero numeri da 0 a 16.777.215.

Qui manca un commento e pertanto in un secondo momento potrebbe risultare poco chiaro come è nato il valore in questione: così nasce un numero magico. Se non si è a conoscenza del modo in cui è nato il valore, aumenta il rischio che questo venga modificato inavvertitamente.

Queste fonti di errore si possono eliminare se si assume la definizione del valore come espressione nell’assegnazione della costante.

Strutture di controllo eccessivamente intricate

Nella maggior parte dei linguaggi, i costrutti di codice possono essere annidati a diversi livelli di profondità. Purtroppo, in questo modo cresce anche la complessità, rendendo più difficile la lettura del codice. Un code smell molto comune riguarda strutture di controllo delle istruzioni eccessivamente annidate, come iterazioni e diramazioni. Per risolvere questo problema ci sono varie opzioni, ad esempio l’uso dell’operatore booleano AND, per inserire le due condizioni in un’unica istruzione if. Un altro approccio che funziona con numerose condizioni consiste nell’usare “guard clauses”.

N.B.

La valutazione “eccessivamente annidato” si basa su un criterio soggettivo. La regola di base è: le istruzioni di controllo dovrebbero essere situate al massimo a tre livelli di indentazione, quattro in situazioni davvero eccezionali. Oltre non è quasi mai consigliabile né necessario. Se si sente la necessità di annidare il codice più in profondità, è il caso di prendere in considerazione il refactoring.

Code smell a livello di funzione

Nella maggior parte dei linguaggi, le funzioni sono l’unità di base del codice. Esistono alcuni snippet di codice più piccoli, ad esempio variabili ed espressioni, ma queste sono indipendenti. Per scrivere funzioni pulite ci vuole una certa esperienza. Vi mostriamo i principali code smell a livello di funzione.

Trattamento mancato delle eccezioni

Le funzioni ricevono dei valori come argomenti. In molti casi solo determinati valori o intervalli di valore sono validi. È responsabilità di chi programma verificare che i valori inseriti siano validi, trattando le eccezioni di conseguenza.

Accesso alla variabile da un intervallo di visibilità superiore nella funzione

Gli intervalli di visibilità (anche detti scope) sono presenti nella maggior parte dei linguaggi di programmazione: stabiliscono quali nomi (di variabili o funzioni) sono definiti in quali punti del codice. A tal proposito gli intervalli di visibilità inferiori ereditano da quelli superiori. Se all’interno di una funzione si accede a una variabile definita esternamente, ci troviamo di fronte a un code smell. Questo avviene perché il valore potrebbe essersi modificato nell’intervallo fra la definizione della variabile e la chiamata della funzione.

Idealmente, all’interno delle funzioni si accede solo a valori che vengono trasmessi come argomenti. Per non dover passare sempre lo stesso valore, si stabilisce un parametro predefinito.

N.B.

L’accesso a una variabile dall’intervallo di visibilità di una funzione interna crea una “chiusura”. Anche questo è un code smell. Le chiusure (o closure) sono un concetto importante nella programmazione JavaScript.

Code smell a livello di classe

La programmazione orientata agli oggetti (OOP) può aiutare a incrementare il riuso di codice e a diminuirne la complessità. Purtroppo, durante il design delle classi è facile commettere degli errori, che successivamente ricompaiono sottoforma di code smell.

Uno dei code smell più comuni a livello di classe è il “god object” e la relativa “god class”. Un god object raggruppa una serie di funzionalità che in realtà non sarebbero destinate a stare insieme, contraddicendo quindi il principio della “separazione dei compiti”.

Spesso, nel contesto dell’ereditarietà gerarchica, si verificano dei code smell. A volte il codice viene suddiviso senza necessità su più livelli di ereditarietà. Spesso si commette anche l’errore di usare l’ereditarietà al posto della composizione come approccio primario per il raggruppamento di oggetti.

Anche l’utilizzo non voluto di “classi di dati” è da considerarsi un code smell. In questo caso si tratta di classi che non implementano un comportamento proprio. Se non sono definiti altri metodi all’infuori di Getter e Setter generici, bisognerebbe prendere in considerazione di utilizzare una struttura di dati più semplice come Dict o Struct.

Code smell a livello di applicazione

È l’incubo di ogni programmatore o programmatrice: si inizia a lavorare sul codice di un’applicazione esistente e immediatamente salta all’occhio che l’intera applicazione è contenuta in un unico file gigante. Non è chiaro come i singoli componenti del codice stiano in relazione l’uno all’altro. Come in un piatto di pasta, nel cosiddetto “spaghetti code”, il codice sorgente è tutto intrecciato.

Manca qualsiasi tipo di astrazione, non ci sono suddivisioni delle classi e le funzioni sono poche. Allo stesso tempo sono presenti duplicati nel codice, da cui possono generarsi dei bug più o meno evidenti. Inoltre, si fa un uso eccessivo di variabili globali.

L’utilizzo di variabili globali è un code smell piuttosto fastidioso. Poiché le variabili globali possono essere modificate potenzialmente in qualsiasi punto del codice, si crea il presupposto perfetto per errori successivamente difficili da eliminare. Quando si esegue il debug, ci si vede costretti a cercare gli errori ovunque e non solo in un punto specifico.

Un ulteriore code smell a livello di applicazione è la mancata suddivisione dei compiti. Soprattutto per quanto riguarda i progetti in PHP è possibile mescolare facilmente markup, funzionalità e rappresentazione in un solo file. Questo rende il riutilizzo del codice e il refactoring molto complicati. Si consiglia di suddividere i compiti in modo chiaro, ad esempio utilizzando il modello “Model-View-Controller” (MVC).

Come si elimina un code smell?

Di per sé è preferibile mantenere il codice sempre pulito. Per farlo si può seguire questo schema:

  1. Prototype: creare un modello
  2. Test: testare la funzionalità
  3. Refactor: riordinare il codice
  4. Ship: inviare il codice all’ambiente di produzione

Purtroppo, il terzo passaggio viene spesso ignorato, specialmente quando a decidere è un manager senza alcuna esperienza nella programmazione. L’argomentazione è: “il codice funziona, perché continuare a perderci del tempo?” Più passa il tempo, più il codice inizia a puzzare; si accumulano i debiti tecnici.

Se un codebase contiene code smell ma non può essere riscritto da capo, è necessario riordinare il codice. Il modo migliore per farlo è procedere in modo incrementale e limitarsi a lavorare su quelle aree all’interno del codebase relativamente facili da pulire. Si isolano le componenti funzionanti o migliorabili dagli “angoli bui” che è meglio non toccare.

Separare le parti meno maleodoranti da quelle che lo sono di più diminuisce la complessità. In questo modo risulta più facile testare il codice e procedere al debugging. Possono venire in aiuto strumenti di revisione del codice automatizzati, che contribuiscono a individuare i code smell e forniscono suggerimenti o ausilio per la pulizia del codice.

Un approccio efficace per eliminare i code smell è il refactoring. La funzionalità del codice, consistente in un’unica funzione, viene suddivisa in funzioni più piccole. Eliminare le parti di codice superflue e migliorarne la denominazione alleggerisce il codebase. A tal proposito consigliamo di tenere a mente alcune regole d’oro, ad esempio “Don’t repeat yourself” (DRY) e “You ain’t gonna need it” (YAGNI), che potrete usare come linee guida.

Per offrirti una migliore esperienza di navigazione online questo sito web usa dei cookie, propri e di terze parti. Continuando a navigare sul sito acconsenti all’utilizzo dei cookie. Scopri di più sull’uso dei cookie e sulla possibilità di modificarne le impostazioni o negare il consenso.