Il termine code smell viene usato quando il codice in esame è stato scritto senza seguire buone pratiche di pro­gram­ma­zio­ne. La presenza di uno o più code smell è sintomo di un problema strut­tu­ra­le nel codice. So­li­ta­men­te è ac­com­pa­gna­ta dalla sen­sa­zio­ne che qualcosa non va. Nel seguente articolo elen­chia­mo i code smell più comuni e vi spie­ghia­mo come eli­mi­nar­li.

Cos’è un code smell?

Si parla di code smell quando, esa­mi­nan­do il codice, un pro­gram­ma­to­re o una pro­gram­ma­tri­ce esperta ha la sen­sa­zio­ne che questo non sia pulito. L’abilità di ri­co­no­sce­re i code smell può essere pa­ra­go­na­ta all’in­tui­zio­ne di un maestro artigiano che guarda un groviglio di fili o dei cavi mal in­stal­la­ti 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 pro­gram­ma­to­re bri­tan­ni­co Martin Fowler, il termine “code smell” sarebbe stato coniato dal collega Kent Beck. Il blog di Fowler fornisce la seguente de­fi­ni­zio­ne:

Citazione

“A Code Smell is a surface in­di­ca­tion that usually cor­re­sponds to a deeper problem in the system.” – Fonte: https://mar­tin­fo­w­ler.com/bliki/CodeSmell.html
Tra­du­zio­ne: “Un code smell è un in­di­ca­to­re su­per­fi­cia­le, che di solito rivela un problema più profondo nel sistema.” (tra­du­zio­ne di IONOS)

Un code smell indica quindi l’esistenza di un problema di metodo. Il codice è stato scritto da un pro­gram­ma­to­re o da una pro­gram­ma­tri­ce con scarse com­pe­ten­ze, oppure da svi­lup­pa­to­ri sempre diversi. In quest’ultimo caso la scarsa qualità del codice si deve a uno scarso livello di com­pe­ten­ze, a una co­no­scen­za limitata del codebase e a poca fa­mi­lia­ri­tà con linee guida e standard. È come se il codice iniziasse a puzzare.

I code smell non vanno confusi con i singoli bug nor­mal­men­te presenti in tutti i codebase, ma devono essere con­si­de­ra­ti semplici in­di­ca­to­ri. Se un pro­gram­ma­to­re o una pro­gram­ma­tri­ce si rende conto della presenza di un code smell, dovrà mettere in atto ulteriori verifiche per de­ter­mi­na­re se esiste ef­fet­ti­va­men­te un problema di metodo.

Se ci sembra che nel codice ci sia qualcosa che non va, possiamo pulirlo in vari modi. Il re­fac­to­ring serve proprio a questo ed è un’opzione per mi­glio­ra­re la struttura del codice man­te­nen­do­ne invariata la fun­zio­na­li­tà. A seconda della portata e della com­ples­si­tà del codice, non sempre è possibile eliminare i code smell. In questi casi non resta che eseguire un “re-write”, ovvero ri­scri­ve­re il codice da capo.

Quali sono i diversi tipi di code smell?

Il codice sorgente è un sistema che si sviluppa su diversi livelli di astra­zio­ne. Si pensi a un’ap­pli­ca­zio­ne, formata da diverse com­po­nen­ti, tutte definite all’interno del codice: variabili, funzioni, classi, moduli. Di seguito esa­mi­ne­re­mo i diversi code smell di­stin­guen­do fra i diversi gradi di astra­zio­ne:

  1. Code smell generici
  2. Code smell a livello di funzione
  3. Code smell a livello di classe
  4. Code smell a livello di ap­pli­ca­zio­ne

Code smell generici

I code smell generici sono presenti in tutti i livelli di un’ap­pli­ca­zio­ne. Nella maggior parte dei casi non si tratta di errori evidenti, ma di pattern che in­fluen­za­no il codice in modo negativo. Secondo uno dei principi di base della pro­gram­ma­zio­ne, il codice dovrebbe essere scritto prin­ci­pal­men­te per le persone che lo leggono. Tuttavia, chi ha iniziato da poco a pro­gram­ma­re e ha poca espe­rien­za, scrive spesso un codice che sortisce gli effetti de­si­de­ra­ti, ma che di fatto è in­com­pren­si­bi­le.

De­no­mi­na­zio­ne ina­de­gua­ta dei costrutti del codice

I code smell generici sono presenti in tutti i livelli di un’ap­pli­ca­zio­ne. Nella maggior parte dei casi non si tratta di errori evidenti, ma di pattern che in­fluen­za­no il codice in modo negativo. Secondo uno dei principi di base della pro­gram­ma­zio­ne, il codice dovrebbe essere scritto prin­ci­pal­men­te per le persone che lo leggono. Tuttavia, chi ha iniziato da poco a pro­gram­ma­re e ha poca espe­rien­za, scrive spesso un codice che sortisce gli effetti de­si­de­ra­ti, ma che di fatto è in­com­pren­si­bi­le.

Stile di pro­gram­ma­zio­ne non uniforme

Quando il codice è pulito, sembra sia stato scritto da una sola persona; risulta im­me­dia­ta­men­te 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 de­no­mi­na­zio­ni non sono ri­con­du­ci­bi­li a una con­ven­zio­ne lascia intuire che diverse persone ci hanno lavorato in modo in­di­pen­den­te le une dalle altre. Se il codice è stato scritto in team, po­treb­be­ro mancare delle linee guida uniformi.

Consiglio

Vi abbiamo in­cu­rio­si­to? Leggete anche l’articolo “Cos’è il clean code?”.

Variabili non definite

Alcuni linguaggi di pro­gram­ma­zio­ne come C++, Java e Ja­va­Script con­sen­to­no di di­chia­ra­re 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 è par­ti­co­lar­men­te insidioso nei linguaggi de­bol­men­te tipizzati: infatti, in mancanza di una de­fi­ni­zio­ne iniziale, non è possibile ri­co­no­sce­re qual è il tipo della variabile.

Valori a codifica fissa nel codice

Un errore che i pro­gram­ma­to­ri inesperti fanno spesso: per comparare una variabile con un valore specifico, in­se­ri­sco­no di­ret­ta­men­te 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 pro­ble­ma­ti­ci, questo perché le singole copie tendono a mutare nel tempo, in­di­pen­den­te­men­te 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 par­ti­co­la­re di valori a codifica fissa. Im­ma­gi­na­te che il nostro codice lavori con un valore limitato a 24 bit. 24 bit cor­ri­spon­do­no 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 co­no­scen­za del modo in cui è nato il valore, aumenta il rischio che questo venga mo­di­fi­ca­to inav­ver­ti­ta­men­te.

Queste fonti di errore si possono eliminare se si assume la de­fi­ni­zio­ne del valore come espres­sio­ne nell’as­se­gna­zio­ne della costante.

Strutture di controllo ec­ces­si­va­men­te intricate

Nella maggior parte dei linguaggi, i costrutti di codice possono essere annidati a diversi livelli di pro­fon­di­tà. Purtroppo, in questo modo cresce anche la com­ples­si­tà, rendendo più difficile la lettura del codice. Un code smell molto comune riguarda strutture di controllo delle istru­zio­ni ec­ces­si­va­men­te annidate, come ite­ra­zio­ni e di­ra­ma­zio­ni. Per risolvere questo problema ci sono varie opzioni, ad esempio l’uso dell’operatore booleano AND, per inserire le due con­di­zio­ni in un’unica istru­zio­ne if. Un altro approccio che funziona con numerose con­di­zio­ni consiste nell’usare “guard clauses”.

N.B.

La va­lu­ta­zio­ne “ec­ces­si­va­men­te annidato” si basa su un criterio sog­get­ti­vo. La regola di base è: le istru­zio­ni di controllo do­vreb­be­ro essere situate al massimo a tre livelli di in­den­ta­zio­ne, quattro in si­tua­zio­ni davvero ec­ce­zio­na­li. Oltre non è quasi mai con­si­glia­bi­le né ne­ces­sa­rio. Se si sente la necessità di annidare il codice più in pro­fon­di­tà, è il caso di prendere in con­si­de­ra­zio­ne il re­fac­to­ring.

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 espres­sio­ni, ma queste sono in­di­pen­den­ti. Per scrivere funzioni pulite ci vuole una certa espe­rien­za. Vi mostriamo i prin­ci­pa­li code smell a livello di funzione.

Trat­ta­men­to mancato delle eccezioni

Le funzioni ricevono dei valori come argomenti. In molti casi solo de­ter­mi­na­ti valori o in­ter­val­li di valore sono validi. È re­spon­sa­bi­li­tà di chi programma ve­ri­fi­ca­re che i valori inseriti siano validi, trattando le eccezioni di con­se­guen­za.

Accesso alla variabile da un in­ter­val­lo di vi­si­bi­li­tà superiore nella funzione

Gli in­ter­val­li di vi­si­bi­li­tà (anche detti scope) sono presenti nella maggior parte dei linguaggi di pro­gram­ma­zio­ne: sta­bi­li­sco­no quali nomi (di variabili o funzioni) sono definiti in quali punti del codice. A tal proposito gli in­ter­val­li di vi­si­bi­li­tà inferiori ereditano da quelli superiori. Se all’interno di una funzione si accede a una variabile definita ester­na­men­te, ci troviamo di fronte a un code smell. Questo avviene perché il valore potrebbe essersi mo­di­fi­ca­to nell’in­ter­val­lo fra la de­fi­ni­zio­ne della variabile e la chiamata della funzione.

Ideal­men­te, all’interno delle funzioni si accede solo a valori che vengono trasmessi come argomenti. Per non dover passare sempre lo stesso valore, si sta­bi­li­sce un parametro pre­de­fi­ni­to.

N.B.

L’accesso a una variabile dall’in­ter­val­lo di vi­si­bi­li­tà di una funzione interna crea una “chiusura”. Anche questo è un code smell. Le chiusure (o closure) sono un concetto im­por­tan­te nella pro­gram­ma­zio­ne Ja­va­Script.

Code smell a livello di classe

La pro­gram­ma­zio­ne orientata agli oggetti (OOP) può aiutare a in­cre­men­ta­re il riuso di codice e a di­mi­nuir­ne la com­ples­si­tà. Purtroppo, durante il design delle classi è facile com­met­te­re degli errori, che suc­ces­si­va­men­te ri­com­pa­io­no sot­to­for­ma 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 fun­zio­na­li­tà che in realtà non sarebbero destinate a stare insieme, con­trad­di­cen­do quindi il principio della “se­pa­ra­zio­ne dei compiti”.

Spesso, nel contesto dell’ere­di­ta­rie­tà ge­rar­chi­ca, si ve­ri­fi­ca­no dei code smell. A volte il codice viene suddiviso senza necessità su più livelli di ere­di­ta­rie­tà. Spesso si commette anche l’errore di usare l’ere­di­ta­rie­tà al posto della com­po­si­zio­ne come approccio primario per il rag­grup­pa­men­to di oggetti.

Anche l’utilizzo non voluto di “classi di dati” è da con­si­de­rar­si un code smell. In questo caso si tratta di classi che non im­ple­men­ta­no un com­por­ta­men­to proprio. Se non sono definiti altri metodi all’infuori di Getter e Setter generici, bi­so­gne­reb­be prendere in con­si­de­ra­zio­ne di uti­liz­za­re una struttura di dati più semplice come Dict o Struct.

Code smell a livello di ap­pli­ca­zio­ne

È l’incubo di ogni pro­gram­ma­to­re o pro­gram­ma­tri­ce: si inizia a lavorare sul codice di un’ap­pli­ca­zio­ne esistente e im­me­dia­ta­men­te salta all’occhio che l’intera ap­pli­ca­zio­ne è contenuta in un unico file gigante. Non è chiaro come i singoli com­po­nen­ti del codice stiano in relazione l’uno all’altro. Come in un piatto di pasta, nel co­sid­det­to “spaghetti code”, il codice sorgente è tutto in­trec­cia­to.

Manca qualsiasi tipo di astra­zio­ne, non ci sono sud­di­vi­sio­ni 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 fa­sti­dio­so. Poiché le variabili globali possono essere mo­di­fi­ca­te po­ten­zial­men­te in qualsiasi punto del codice, si crea il pre­sup­po­sto perfetto per errori suc­ces­si­va­men­te 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 ap­pli­ca­zio­ne è la mancata sud­di­vi­sio­ne dei compiti. So­prat­tut­to per quanto riguarda i progetti in PHP è possibile mescolare fa­cil­men­te markup, fun­zio­na­li­tà e rap­pre­sen­ta­zio­ne in un solo file. Questo rende il riu­ti­liz­zo del codice e il re­fac­to­ring molto com­pli­ca­ti. Si consiglia di sud­di­vi­de­re i compiti in modo chiaro, ad esempio uti­liz­zan­do il modello “Model-View-Con­trol­ler” (MVC).

Come si elimina un code smell?

Di per sé è pre­fe­ri­bi­le mantenere il codice sempre pulito. Per farlo si può seguire questo schema:

  1. Prototype: creare un modello
  2. Test: testare la fun­zio­na­li­tà
  3. Refactor: rior­di­na­re il codice
  4. Ship: inviare il codice all’ambiente di pro­du­zio­ne

Purtroppo, il terzo passaggio viene spesso ignorato, spe­cial­men­te quando a decidere è un manager senza alcuna espe­rien­za nella pro­gram­ma­zio­ne. L’ar­go­men­ta­zio­ne è: “il codice funziona, perché con­ti­nua­re a perderci del tempo?” Più passa il tempo, più il codice inizia a puzzare; si ac­cu­mu­la­no i debiti tecnici.

Se un codebase contiene code smell ma non può essere riscritto da capo, è ne­ces­sa­rio rior­di­na­re il codice. Il modo migliore per farlo è procedere in modo in­cre­men­ta­le e limitarsi a lavorare su quelle aree all’interno del codebase re­la­ti­va­men­te facili da pulire. Si isolano le com­po­nen­ti fun­zio­nan­ti o mi­glio­ra­bi­li dagli “angoli bui” che è meglio non toccare.

Separare le parti meno ma­leo­do­ran­ti da quelle che lo sono di più di­mi­nui­sce la com­ples­si­tà. In questo modo risulta più facile testare il codice e procedere al debugging. Possono venire in aiuto strumenti di revisione del codice au­to­ma­tiz­za­ti, che con­tri­bui­sco­no a in­di­vi­dua­re i code smell e for­ni­sco­no sug­ge­ri­men­ti o ausilio per la pulizia del codice.

Un approccio efficace per eliminare i code smell è il re­fac­to­ring. La fun­zio­na­li­tà del codice, con­si­sten­te in un’unica funzione, viene suddivisa in funzioni più piccole. Eliminare le parti di codice superflue e mi­glio­rar­ne la de­no­mi­na­zio­ne al­leg­ge­ri­sce il codebase. A tal proposito con­si­glia­mo 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.

Vai al menu prin­ci­pa­le