Il codice si scrive una volta e si legge centinaia di volte. Raramente lo sviluppatore che scrive una funzione è colui che la debugga sei mesi dopo, la estende per soddisfare un nuovo requisito o deve comprenderne i casi limite durante un problema in produzione. Ogni decisione presa durante la scrittura del codice, il nome di una variabile, la lunghezza di una funzione, la struttura di una classe, rende il lavoro di chi lo leggerà in seguito più facile o più difficile. Il codice pulito è la disciplina che permette di semplificarlo sistematicamente.
Il concetto è stato formalizzato da Robert C. Martin, detto "Uncle Bob", in Clean Code: A Handbook of Agile Software Craftsmanship (2008), che rimane il testo di riferimento canonico sull'argomento. Refactoring: Improving the Design of Existing Code di Martin Fowler affronta il problema complementare: cosa fare quando il codice esistente non è pulito e necessita di esserlo. Insieme, queste due opere definiscono le basi intellettuali della pratica del codice pulito. Questa guida illustra i loro principi chiave con esempi di codice funzionanti nei linguaggi più diffusi, tra cui RPG e PL/I, per gli sviluppatori che si occupano della manutenzione di sistemi legacy aziendali.
Violazioni del codice pulito che non puoi vedere
SMART TS XL Individua complessità, duplicazioni e codice inutilizzato in linguaggi come COBOL, Java, Python, RPG e altri.
Maggiori InformazioniChe cos'è il Clean Code?
Il codice pulito è un codice sorgente facile da leggere, facile da comprendere, facile da testare e facile da modificare. In questa definizione, il termine "facile" assume un significato preciso. Il codice accettato da un compilatore non è necessariamente codice pulito. Il codice che supera tutti i test non è necessariamente codice pulito. Il codice è pulito quando un altro sviluppatore, qualcuno che non lo ha scritto, in un contesto non previsto al momento della sua stesura, può comprenderne rapidamente l'intento, modificarlo con sicurezza ed estenderlo senza conseguenze inattese.
La definizione più citata è quella di Martin Fowler: "Chiunque può scrivere codice comprensibile a un computer. I bravi programmatori scrivono codice comprensibile agli esseri umani". La misura della pulizia del codice è la comprensibilità da parte dell'uomo, non l'esecuzione automatica.
Il codice pulito non riguarda l'estetica. Non si tratta di seguire una particolare guida di stile fine a se stessa. Riguarda le proprietà strutturali del codice che determinano quanto impegno richiederà ogni futura modifica. Una codebase che viola i principi del codice pulito accumula costantemente debito tecnico, il costo cumulativo delle scorciatoie passate, fino a quando ogni nuova funzionalità richiede la comprensione e un'attenta gestione della complessità esistente prima di aggiungere qualsiasi cosa di nuovo.
Principi del codice pulito: Guida rapida
I principi del codice pulito di Robert C. Martin possono essere riassunti in un insieme di linee guida pratiche. La tabella seguente associa ciascun principio alla sua regola principale e al problema che previene:
| Principio | Regola fondamentale | Problema che impedisce |
|---|---|---|
| Nomi significativi | I nomi dovrebbero rivelare l'intento, le variabili, le funzioni, le classi | Decodifica cognitiva dall'alto cosa x, tmp, o obj rappresenta effettivamente |
| Piccole funzioni | Le funzioni svolgono una sola funzione; si adattano a una sola schermata | Monoliti inverificabili e illeggibili che mescolano le responsabilità. |
| Responsabilità unica | Ogni classe/modulo ha un motivo per cambiare | Classi divine che si interrompono quando cambia una qualsiasi di una dozzina di cose non correlate |
| ASCIUTTO (non ripetere te stesso) | Ogni elemento di conoscenza ha una rappresentazione | Correzioni di bug applicate in una copia ma non nelle altre; deriva logica |
| KISS (Keep It Simple, ovvero "Mantienilo semplice") | Preferisci la soluzione più semplice che funziona | Codice eccessivamente complesso che risolve problemi che nessuno ha |
| YAGNI (Non ne avrai bisogno) | Non implementare funzionalità finché non sono necessarie. | Codice speculativo inutile che aggiunge complessità senza alcun beneficio |
| Principio aperto/chiuso | Aperto per estensione, chiuso per modifica | Codice che interrompe il comportamento esistente quando si aggiunge un nuovo comportamento |
| Separazione degli interessi | Responsabilità diverse in luoghi diversi | Codice intricato in cui la modifica di un elemento compromette elementi non correlati. |
| Evita di commentare chiedendoti cosa; usa i commenti chiedendoti perché. | Il codice spiega cosa; i commenti spiegano il perché. | Commenti obsoleti che traggono più in inganno che informazione |
| Regola degli scout | Lascia il codice più pulito di come lo hai trovato | Degrado graduale della qualità dovuto a negligenza progressiva |
I principi fondamentali del codice pulito spiegati
ASCIUTTO, non ripeterti
Il principio DRY (Don't Repeat Yourself) afferma che ogni informazione deve avere una rappresentazione unica, univoca e autorevole all'interno di un sistema. Quando la stessa logica compare in più punti, queste rappresentazioni inevitabilmente divergono. La modifica di una regola aziendale si trasforma in un'operazione di ricerca e sostituzione in più file, e la mancanza di una sola copia genera un bug.
Il principio DRY (Don't Repeat Yourself) non si applica solo al codice copiato e incollato. Si applica alla configurazione, alla documentazione e agli schemi dei dati. Se le stesse informazioni devono essere mantenute in due posizioni diverse, si viola il principio DRY, indipendentemente dal fatto che il codice sia stato copiato letteralmente.
KISS, Keep It Simple
Il principio KISS sostiene che i sistemi funzionano al meglio quando sono mantenuti semplici e che la semplicità dovrebbe essere un obiettivo primario nella progettazione. La complessità non è indice di sofisticazione, bensì indica che qualcosa avrebbe potuto essere espresso in modo più chiaro.
L'implicazione pratica: quando due soluzioni risolvono lo stesso problema, è preferibile quella più semplice. La soluzione più complessa potrebbe gestire casi limite che non hai ancora incontrato. Renderà sicuramente il codice più difficile da comprendere per chiunque lo incontri.
YAGNI, non ne avrai bisogno
Il principio YAGNI scoraggia l'aggiunta di funzionalità finché non sono effettivamente necessarie. Rappresenta una risposta all'impulso comune di costruire sistemi flessibili ed estensibili per esigenze future che potrebbero non concretizzarsi mai. Ogni astrazione, interfaccia e opzione di configurazione esistente per un ipotetico caso d'uso futuro rappresenta un sovraccarico cognitivo per ogni sviluppatore che legge il codice oggi.
Nomi significativi
I nomi sono il principale meccanismo di comunicazione nel codice sorgente. Una funzione denominata process() non comunica nulla su cosa elabora, quando o cosa restituisce. Una funzione denominata calculateMonthlyInterest() comunica con precisione.
I criteri fondamentali per la scelta di un nome: Riesci a capire cosa rappresenta senza leggerne l'implementazione? Il nome ne rivela lo scopo, i parametri e il valore di ritorno? Avresti bisogno di un commento per spiegarne la funzione?
Piccole funzioni
La regola empirica di Robert C. Martin: le funzioni dovrebbero essere piccole, più piccole di quanto si ritenga necessario. Una funzione che svolge una sola operazione può essere denominata chiaramente, testata in isolamento e compresa senza leggerne l'implementazione. Una funzione che svolge diverse operazioni richiede invece la comprensione simultanea di tutte.
Il principio di responsabilità unica applicato alle funzioni: una funzione dovrebbe fare una sola cosa, farla bene e farla soltanto. Se hai bisogno della congiunzione "e" per descrivere cosa fa una funzione, probabilmente fa troppe cose.
Codice pulito in Java
La verbosità di Java rende la disciplina del codice pulito particolarmente importante. Il codice standard richiesto dal linguaggio può nascondere l'intento del codice se non gestito con attenzione.
Giava
// Before: unclear names, mixed responsibilities, magic numbers
public double calc(int x, int y) {
double r = 0;
if (y > 1000) {
r = x * y * 0.1;
} else {
r = x * y * 0.05;
}
return r;
}
// After: meaningful names, single responsibility, named constants
private static final double PREMIUM_DISCOUNT_RATE = 0.10;
private static final double STANDARD_DISCOUNT_RATE = 0.05;
private static final int PREMIUM_THRESHOLD = 1000;
public double calculateDiscount(int quantity, int unitPrice) {
double subtotal = quantity * unitPrice;
return isPremiumOrder(unitPrice)
? subtotal * PREMIUM_DISCOUNT_RATE
: subtotal * STANDARD_DISCOUNT_RATE;
}
private boolean isPremiumOrder(int unitPrice) {
return unitPrice > PREMIUM_THRESHOLD;
}
Le principali pratiche di codice pulito per Java includono: privilegiare la composizione rispetto all'ereditarietà, utilizzare gli stream per l'elaborazione dei dati invece di cicli prolissi, estrarre i valori numerici magici in costanti denominate e mantenere le classi focalizzate su una singola responsabilità. I principi di codice pulito di Robert C. Martin per Java includono la regola che le classi dovrebbero essere piccole, misurate non in righe ma in responsabilità.
Giava
// Clean Java: streams over imperative loops
List<String> activeUserEmails = users.stream()
.filter(User::isActive)
.map(User::getEmail)
.collect(Collectors.toList());
Codice pulito in Python
La filosofia di progettazione di Python, secondo cui l'esplicito è meglio dell'implicito e il semplice è meglio del complesso, si allinea naturalmente con i principi del codice pulito. PEP 8 è la guida di stile ufficiale di Python e il punto di riferimento per un codice Python pulito.
python
# Before: vague names, long function, no separation
def do_stuff(d):
res = []
for i in d:
if i['a'] > 18:
res.append(i['n'].upper())
return res
# After: meaningful names, separated concerns, Pythonic style
def get_adult_names_uppercase(users: list[dict]) -> list[str]:
return [
user["name"].upper()
for user in users
if user["age"] > 18
]
python
# Clean Python: context managers for resource handling
# Before: manual, error-prone
f = open("data.txt")
data = f.read()
f.close()
# After: guaranteed cleanup, self-documenting intent
with open("data.txt") as f:
data = f.read()
Il codice pulito in stile Python utilizza list comprehension al posto dei cicli manuali per le trasformazioni semplici, suggerimenti di tipo per firme di funzioni autoesplicative, gestori di contesto per la gestione delle risorse e classi di dati o tuple nominate invece di semplici dizionari per i dati strutturati.
Codice pulito in JavaScript e TypeScript
La flessibilità di JavaScript è al tempo stesso il suo punto di forza e la principale sfida per la scrittura di codice pulito. Senza disciplina, i codebase JavaScript accumulano schemi incoerenti, conversioni di tipo implicite e catene di callback intricate.
javascript
// Before: var, callback hell, no error handling
function getUser(id, cb) {
db.query('SELECT * FROM users WHERE id = ' + id, function(err, rows) {
if (err) cb(err);
cb(null, rows[0]);
});
}
// After: async/await, parameterized query, proper error handling
async function getUserById(userId: number): Promise<User | null> {
const [rows] = await db.execute(
'SELECT * FROM users WHERE id = ?',
[userId]
);
return rows[0] ?? null;
}
dattiloscritto
// Clean TypeScript: explicit types replace implicit any
// Before
function process(data) {
return data.map(x => x.v * 2);
}
// After
interface DataPoint {
value: number;
label: string;
}
function doubleValues(dataPoints: DataPoint[]): number[] {
return dataPoints.map(point => point.value * 2);
}
JavaScript pulito utilizza const per impostazione predefinita, let quando è necessario rilegare, e mai varLe funzioni pure, in cui lo stesso input produce sempre lo stesso output e non presentano effetti collaterali, rappresentano il fondamento più pulito per la logica JavaScript.
Pulisci codice in C#
C# offre potenti funzionalità per scrivere codice pulito ed espressivo. L'evoluzione del linguaggio, con LINQ, record, pattern matching e tipi di riferimento nullable, si muove costantemente verso una sintassi più dichiarativa e leggibile.
nitido
// Before: magic numbers, verbose loop, mutable state
public double CalculateTotal(List<OrderItem> items)
{
double total = 0;
foreach (var item in items)
{
if (item.Quantity > 10)
total += item.UnitPrice * item.Quantity * 0.9;
else
total += item.UnitPrice * item.Quantity;
}
return total;
}
// After: named constant, LINQ, single expression
private const double BulkDiscountRate = 0.9;
private const int BulkDiscountThreshold = 10;
public double CalculateTotal(IEnumerable<OrderItem> items) =>
items.Sum(item => item.Quantity > BulkDiscountThreshold
? item.UnitPrice * item.Quantity * BulkDiscountRate
: item.UnitPrice * item.Quantity);
Principi di codice pulito C#: utilizzare proprietà anziché campi pubblici per l'incapsulamento, sfruttare LINQ per operazioni dichiarative sui dati, utilizzare record per strutture dati immutabili, preferire le interfacce ai tipi concreti nelle firme dei metodi e utilizzare tipi di riferimento nullable (string? vs string) per rendere esplicita la sicurezza nulla.
Codice pulito in Kotlin
Il design di Kotlin riduce specificamente il codice ripetitivo che rende difficile mantenere pulito il codice Java, aggiungendo al contempo funzionalità, classi di dati, funzioni di estensione e gestione dei valori nulli, che supportano naturalmente la scrittura di codice pulito.
Kotlin
// Before: verbose Java-style Kotlin
class User {
var name: String = ""
var email: String = ""
var age: Int = 0
}
fun processUsers(users: List<User>): List<String> {
val result = mutableListOf<String>()
for (user in users) {
if (user.age >= 18) {
result.add(user.email)
}
}
return result
}
// After: idiomatic clean Kotlin
data class User(val name: String, val email: String, val age: Int)
fun getAdultEmails(users: List<User>): List<String> =
users.filter { it.age >= 18 }.map { it.email }
Kotlin's data class Fornisce automaticamente i metodi equals, hashCode, copy e toString, eliminando il codice ripetitivo che rende le classi dati Java prolisse e soggette a errori. Le funzioni di estensione consentono di aggiungere metodi di utilità puliti alle classi esistenti senza ereditarietà.
Codice pulito in RPG e PL/I: linguaggi legacy, principi moderni
I principi del codice pulito si applicano a ogni linguaggio, compresi i linguaggi aziendali che gestiscono sistemi finanziari, piattaforme assicurative e applicazioni governative in tutto il mondo. RPG (Report Program Generator) e PL/I sono ancora attivamente mantenuti in molte organizzazioni, e la stessa disciplina che rende Java o Python "puliti" rende RPG e PL/I sostenibili.
Codice pulito in RPG (ILE RPG) :
rpg
// Before: cryptic two-character names, magic numbers
C EVAL D = Q * P * 1.05
C IF Q > 100
C EVAL D = Q * P * 0.95
C ENDIF
// After: meaningful names, named constants, clear intent
/free
dcl-c BULK_DISCOUNT_THRESHOLD 100;
dcl-c BULK_DISCOUNT_RATE 0.95;
dcl-c STANDARD_RATE 1.05;
dcl-proc CalculateOrderTotal;
dcl-pi *N packed(15:2);
quantity packed(7:0) value;
unitPrice packed(9:2) value;
end-pi;
if quantity > BULK_DISCOUNT_THRESHOLD;
return quantity * unitPrice * BULK_DISCOUNT_RATE;
else;
return quantity * unitPrice * STANDARD_RATE;
endif;
end-proc;
/end-free
Pratiche chiave per giochi di ruolo puliti: utilizzare il formato libero di ILE RPG (/free) sintassi piuttosto che formato fisso per leggibilità, denominare le procedure in modo descrittivo, sostituire i valori hardcoded con costanti denominate utilizzando dcl-ce scomporre i programmi lunghi in procedure mirate utilizzando dcl-proc.
Codice pulito in PL/I :
pli
/* Before: single-letter names, no structure */
CALC: PROC(X, Y) RETURNS(FLOAT);
DCL (X, Y, R) FLOAT;
IF Y > 1000 THEN R = X * Y * 0.1;
ELSE R = X * Y * 0.05;
RETURN(R);
END CALC;
/* After: meaningful names, named constants, clear intent */
DCL PREMIUM_THRESHOLD FIXED DECIMAL(7) INIT(1000);
DCL PREMIUM_RATE FLOAT INIT(0.10);
DCL STANDARD_RATE FLOAT INIT(0.05);
CALCULATE_DISCOUNT: PROC(QUANTITY, UNIT_PRICE) RETURNS(FLOAT);
DCL (QUANTITY, UNIT_PRICE) FLOAT;
DCL SUBTOTAL FLOAT;
SUBTOTAL = QUANTITY * UNIT_PRICE;
IF UNIT_PRICE > PREMIUM_THRESHOLD
THEN RETURN(SUBTOTAL * PREMIUM_RATE);
ELSE RETURN(SUBTOTAL * STANDARD_RATE);
END CALCULATE_DISCOUNT;
I principi di codice pulito di PL/I rispecchiano quelli dei linguaggi moderni: identificatori significativi (il limite di 31 caratteri di PL/I è sufficiente per nomi descrittivi), costanti con nome anziché numeri magici, procedure focalizzate su un singolo compito e gestione esplicita degli errori tramite il sistema di condizioni ON.
Strumenti per la pulizia del codice e analisi statica
Applicare manualmente i principi del codice pulito tramite la revisione del codice è necessario, ma non sufficiente su larga scala. Gli strumenti di analisi statica applicano automaticamente le metriche del codice pulito, segnalando le violazioni prima che vengano integrate.
| Chiavetta | Lingue disponibili | Cosa impone |
|---|---|---|
| SonarQube / SonarCloud | 30+ lingue | Complessità, duplicazione, odori di codice, sicurezza |
| Stile di controllo | Java | Convenzioni di denominazione, formattazione, struttura |
| PMD | Java, Apex | Codice duplicato, variabili inutilizzate, complessità |
| ESLint + typescript-eslint | JavaScript, dattiloscritto | Stile, complessità, codice inutilizzato, modelli asincroni |
| Pylint + Radon | Python | PEP 8, complessità, indice di manutenibilità |
| ReShaper | C# | Stile del codice, codice ridondante, suggerimenti per il refactoring |
| Clippy | Ruggine | Modelli idiomatici, errori comuni |
| SMART TS XL | COBOL, RPG, PL/I, Java, Python e altro ancora | Complessità tra linguaggi diversi, duplicazione, codice morto |
Le metriche di codice pulito più comuni misurate dagli strumenti sono: complessità ciclomica (numero di rami decisionali, sopra 10 è un avviso, sopra 20 è critico), complessità cognitiva (quanto è difficile comprendere il codice, una metrica specifica di SonarQube che migliora la complessità ciclomica per la misurazione della leggibilità) e tasso di duplicazione (percentuale di codice duplicato, sopra il 3% richiede attenzione).
Come SMART TS XL Garantisce un codice pulito in tutte le codebase aziendali.
Gli strumenti per la pulizia del codice sopra elencati operano all'interno di un singolo linguaggio. Negli ambienti aziendali in cui coesistono servizi Java, pipeline Python, programmi batch COBOL, moduli RPG e flussi di job JCL, è necessario misurare simultaneamente le violazioni della pulizia del codice di ciascun linguaggio, e le relazioni tra i componenti di linguaggi diversi sono importanti tanto quanto la qualità all'interno di un singolo file.
SMART TS XL'S analisi statica del codice Applica metriche di codice pulito a tutti i linguaggi presenti nell'ambiente contemporaneamente. La complessità ciclomica, i tassi di duplicazione, l'identificazione del codice morto e le metriche di accoppiamento strutturale vengono calcolate per i programmi COBOL utilizzando la stessa metodologia impiegata per le classi Java, producendo misurazioni di qualità comparabili e unificate per l'intero portfolio di applicazioni.
La funzionalità di analisi dell'impatto rende le violazioni del codice pulito gestibili a livello architetturale: quando un paragrafo COBOL presenta un forte accoppiamento con decine di altri programmi, l'analisi dell'impatto mostra esattamente quali componenti sono interessati prima ancora di iniziare qualsiasi refactoring. Questo trasforma la paralisi da refactoring che i team sperimentano in codebase di grandi dimensioni in un programma di correzione strutturato con un ambito definito.
La mappatura delle dipendenze delle applicazioni identifica le violazioni architetturali del principio di codice pulito a livello di sistema: quali componenti sono diventati classi onnipotenti a livello aziendale, dove le dipendenze circolari violano la separazione delle responsabilità tra i diversi linguaggi di programmazione e dove la stessa logica di business è stata implementata indipendentemente in più sistemi senza che nessuna copia fosse a conoscenza dell'altra. Questa violazione del principio DRY (Don't Repeat Yourself) tra linguaggi diversi, ovvero la stessa regola di business mantenuta separatamente in un programma COBOL, un servizio Java e una pipeline Python, rappresenta la categoria più costosa di violazione del principio di codice pulito nei sistemi aziendali ed è invisibile a qualsiasi strumento monolingue.
Per i team che applicano i principi del codice pulito durante modernizzazione dell'eredità programmi, SMART TS XL Fornisce l'analisi preliminare al refactoring che rende il lavoro gestibile: il codice obsoleto viene escluso dall'ambito prima dell'inizio della conversione, i componenti più complessi vengono identificati per un'attenzione prioritaria e il grafico completo delle dipendenze è disponibile prima di apportare qualsiasi modifica.
Il codice pulito è una disciplina di squadra, non una pratica individuale.
I principi di questa guida sono più facili da enunciare che da mantenere. Ogni singolo sviluppatore può scrivere una funzione pulita in isolamento. La sfida sta nel mantenere un codice pulito all'interno di un team, nel tempo e in una codebase che cresce man mano che i suoi collaboratori cambiano. Ciò richiede tre cose: standard condivisi che tutti conoscono e hanno concordato, strumenti che impongano automaticamente tali standard e una cultura di miglioramento incrementale, la regola dello scout applicata in modo coerente, che rende ogni parte del codice leggermente più pulita di come è stata trovata.
La formulazione di Robert C. Martin rimane la più utile: "Il codice pulito sembra sempre scritto da qualcuno che ci tiene". La prova che qualcuno ci tiene non è l'assenza di bug, ma la presenza di nomi leggibili, funzioni mirate, una struttura chiara e l'assenza di sorprese. È questa la prova che rende un codebase degno di essere utilizzato, a cui vale la pena contribuire e che vale la pena di essere mantenuto nel corso degli anni e dei cambiamenti di team che qualsiasi sistema di successo accumulerà.


