Clean Code

Clean Code è un libro che ogni sviluppatore dovrebbe leggere. In questo articolo riporto, a grandi linee, alcuni degli importanti concetti espressi nel libro.

Annunci

Clean Code è stato scritto da Rober Martin (noto anche come Uncle Bob) e alcuni coautori.
È un libro che ogni sviluppatore dovrebbe leggere, magari dopo aver lavorato per qualche tempo.
Personalmente vorrei averlo letto prima, perché mi ha aiutato, nel tempo, a cambiare il mio modo di scrivere codice, permettendomi, di realizzare prodotti migliori faticando di meno.

In questo articolo riporto, a grandi linee, alcuni degli importanti concetti espressi nel libro.

All’inizio l’autore descrive una situazione che tutti abbiamo sperimentato e cioè che, man mano che un programma cresce, diventa sempre più complesso e disordinato, e ogni nuova modifica impiega un tempo sempre maggiore per essere portata a termine.
Questa situazione è conosciuta come code entropy, e dipende ma molti fattori, tra cui:

  • Codice scritto velocemente perché deve andare in produzione in fretta
  • Codice scritto da più persone che non comunicano bene tra loro
  • Decisioni facili al momento ma non sostenibili quando il progetto cresce di dimensioni (debito tecnologico)

Per evitare questa situazione occorre una continua attenzione alla pulizia del codice, con frequenti refactoring laddove il programma sia disordinato e poco leggibile.

I primi capitoli puntano l’attenzione sullo stile: molto spazio viene dedicato alla nomenclatura di variabili, metodi e classi, perché la leggibilità del codice è fondamentale. Quindi occorre evitare nomi troppo corti, non esplicativi, non legati al dominio del progetto, o con prefissi inutili.
Si parla anche di indentazione e di spaziatura: sembrano cose banali, ma permettono di migliorare la leggibilità del codice.

Interessante anche discorso sui commenti: Martin sostiene che i commenti dovrebbero essere utilizzati solo per dichiarare l’intento, avvisare sull’uso di certe funzionalità o amplificare il significato di qualche parte del codice a cui, altrimenti, non si darebbe la dovuta importanza.
Negli altri casi i commenti andrebbero evitati, perché il codice dovrebbe essere auto esplicativo: utilizzando la corretta nomenclatura per variabili, metodi e classi, la maggior parte dei commenti diventerebbero ridondanti, quindi inutili.

Particolare enfasi viene data a come scrivere le funzioni: il mantra è “una funzione dovrebbe fare una sola cosa“, quindi se una funzione fa più di una cosa dovrebbe essere spezzata; inoltre, una funzione dovrebbe riferirsi ad un solo livello di astrazione e dovrebbe avere il minimo numero possibile di parametri (meglio se nessuno).

Ci sono tantissimi consigli su come scrivere dei metodi il più possibile espressivi e comprensibili: leggendo il codice si dovrebbe riuscire a capire il flusso del programma senza essere obbligati ad entrare troppo nel dettaglio.
I metodi dovrebbero essere inoltre apparire nel sorgente per livello di astrazione decrescente: dopo una funzione dovrebbero seguire quelle di livello inferiore da essa utilizzate, e così via.

Le idee espresse sulle funzioni vengono sviluppate per le classi, che pure dovrebbero essere il più piccole possibile. Per capire se e quando dobbiamo spezzare una classe in classi più piccole, possiamo provare a descrivere che cosa fa quella classe: se nella descrizione c’è almeno una congiunzione “e”, allora probabilmente la classe va spezzata.
Altro metodo è quello del valutare la coesione dei metodi: quest’ultima è tanto maggiore quanto maggiore è il numero di variabili di istanza utilizzate da un metodo. Se una funzione ne usa poche, probabilmente non c’entra molto con quella classe e andrebbe messa in una classe a parte.

Nella scrittura delle classi è cruciale il Single Responsibility Principle (SRP), in base al quale un modulo dovrebbe avere una sola ragione per cambiare. Questo concetto è strettamente legato alla coesione. L’autore fa un interessante esempio in cui una classe per la generazione di numeri primi viene spezzata in più classi ad elevata coesione.

Altro principio estremamente importante, che serve per isolare le classi dai cambiamenti esterni, è il Dependency Inversion Principle (DIP): questo afferma che per isolare una classe dai cambiamenti, essa dovrebbe dipendere non da classi concrete (implementazioni), ma da astrazioni (interfacce). La dipendenza dovrebbe poi essere “iniettata” dall’esterno (Dependency Injection).

Il capitolo sulla gestione degli errori contiene interessanti consigli su come affrontare queste delicata materia. Si ricollega alla scrittura dei metodi il fatto che la gestione degli errori non dovrebbe essere mescolata al codice vero e proprio: infatti una funzione deve fare una cosa sola, e la gestione degli errori è appunto una cosa. In questo modo le funzioni che descrivono gli algoritmi restano pulite e separate dal resto.

I margini, i confini delle nostre applicazioni sono i punti in cui utilizziamo componenti di terze parti (framework e librerie varie). Secondo Uncle Bob questi punti sono da controllare e difendere accuratamente, perché dobbiamo evitare di dipendere troppo da componenti esterni. Quindi una buona pratica è quella di racchiudere le parti esterne dentro dei wrapper, in modo da isolarle dal resto del nostro codice. In questo modo, per sostituire i componenti esterni dovremo soltanto intervenire sui wrapper, mentre il resto del nostro codice sarà al sicuro.

L’autore parla diffusamente dei test: questi sono essenziali non solo per assicurarci che le nostre applicazioni funzionino correttamente, ma anche per permetterci di eseguire in sicurezza le modifiche evolutive, le correzioni e i necessari refactoring.

Egli pone inoltre l’accento sul fatto che il codice di test deve essere pulito come il codice di produzione: infatti, se non adeguatamente curato, anche il codice di test tende a corrompersi e a diventare ingestibile; a questo punto non possiamo più utilizzarlo e i test perdono lo loro utilità.
Gli unici “peccati” che possiamo compiere con il codice di test riguardano l’uso della memoria e le prestazioni.

Il concetto di clean code si può applicare anche a livello più alto, cioè a livello sistema, o architetturale. Un sistema è un’interconnessione di componenti che collaborano tra loro.
Il punto chiave per ottenere un’architettura pulita è mantenere separate le responsabilità: per esempio è molto utile separare la costruzione del sistema (cioè la generazione degli oggetti che lo compongono) dall’uso degli stessi.

Il meccanismo di base per ottenere separazione di responsabilità è la Dependency Injection (strettamente legata all’Inversion of Control).

Esistono framework che servono di costruire sistemi sulla base di questi principi. Uno di questi è Spring, che permette anche un elevato livello di disaccoppiamento del codice dal framework stesso: infatti non occorre derivare le classi da un oggetto base del framework e bastano poche righe per generare gli oggetti attraverso le classi e i metodi del framework (queste righe possono essere facilmente isolate e modificate nel caso si decida di cambiare framework).

Successivamente l’autore parla di multithreading, che, in alcuni casi, permette di migliorare le prestazioni: questo è vero solo quando ci sono tempi di attesa che si possono sfruttare per interrompere un thread e farne partire un altro (questo, ad esempio, si verifica se sono coinvolte connessioni di rete, oppure, in misura maggiore, quando c’è interazione con qualche utente).

Il multithreading non si può improvvisare, altrimenti si incorre in comportamenti inspiegabili e imprevedibili. Allora occorre progettare i programmi in modo accurato, dividendo il codice che gestisce la concorrenza dall’altro codice, riducendo al minimo il numero di oggetti condivisi e limitandone lo scope e rendendo i thread il più possibile indipendenti.
Occorre inoltre conoscere molto bene le librerie che si utilizzano, prediligendo le classi thread-safe, e soprattutto bisogna conoscere bene gli algoritmi coinvolti.

Fatto questo occorre eseguire i test in modo molto accurato e mirato: il codice normale (non di multithreading) deve essere testato a parte, mentre quello che gestisce il multithreading deve essere stressato in modo da far emergere eventuali debolezze, cercando di forzare il più possibile il passaggio da un thread all’altro (esistono strumenti che permettono di automatizzare queste operazioni).

Un’ampia sezione del libro è dedicata all’analisi di pezzi di codice e al refactoring, per ripulirlo e migliorarlo. Sono capitoli impegnativi, ma vale davvero la pena di analizzare il codice e il modo in cui viene modificato per renderlo migliore. L’autore ha raggruppato una serie di regole che servono per individuare le code smells (punti in cui il codice è disordinato, poco comprensibile, male organizzato, ecc.) e applicare i correttivi necessari: queste regole hanno un codice, che viene richiamato laddove vengono applicate al codice.

Personalmente ho trovato molto utile analizzare i miei programmi con davanti questo elenco di regole, e cercare di applicarle una dopo l’altra (almeno le più importanti): i risultati sono sorprendenti.

A questo punto, non mi resta che augurarvi buona lettura.

Una risposta a "Clean Code"

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...