Il codice spaghetti (codice spaghetti, codice pastasciutta) — è una struttura di programma aggrovigliata e caotica in cui i blocchi logici sono intrecciati senza alcun ordine. Secondo lo studio del TIOBE Index (2024), i progetti con un alto livello di codice spaghetti richiedono 2,5 volte più tempo per implementare nuove funzionalità. Il termine è nato nell’era della programmazione primordiale, quando l’istruzione goto permetteva di saltare tra qualsiasi punto del programma, creando costrutti illeggibili.
Punti chiave
Il codice spaghetti — è una metafora per descrivere un codice la cui struttura assomiglia a un piatto di spaghetti: singoli fili (blocchi logici) sono aggrovigliati, incollati e inseparabili l’uno dall’altro. In tale codice, è impossibile isolare livelli, moduli o componenti — tutto è mescolato in un’unica grande massa.
A differenza del codice scadente, che può essere semplicemente sciatto, il codice spaghetti è un problema architetturale fondamentale. Anche un codice perfettamente formattato con buoni nomi di variabili può essere codice spaghetti se la sua architettura è caotica. Il problema sta a livello della struttura del programma, non dello stile di scrittura.
Secondo IEEE (2022), circa il 35% di tutti gli errori nei grandi progetti sono causati proprio dalla struttura aggrovigliata del codice, non da errori logici dello sviluppatore. Lo sviluppatore commette un errore non perché ha frainteso il compito, ma perché non è riuscito a tracciare il flusso di esecuzione nel codice spaghetti.
Se il codice scadente è codice scadente alla scala di una singola funzione o file, allora il codice spaghetti è architettura scadente alla scala dell’intera applicazione. La pastasciutta può consistere in funzioni singolarmente ben scritte, ma la loro interazione è caotica e imprevedibile.
Il termine “codice spaghetti” è apparso negli anni ’70 insieme alla critica dell’istruzione goto. Nei primi linguaggi di programmazione (BASIC, FORTRAN, COBOL), goto era il modo principale per controllare il flusso di esecuzione. Un programma era una sequenza di righe numerate e goto permetteva di saltare a qualsiasi di esse. Questo creava un “groviglio” di salti impossibile da districare.
Nel 1968, Edsger Dijkstra pubblicò la sua famosa lettera “Go To Statement Considered Harmful”, che segnò l’inizio dell’era della programmazione strutturata. Dijkstra dimostrò che qualsiasi algoritmo può essere implementato senza goto, usando solo tre costrutti: sequenza, ramificazione (if) e ciclo (while). Questo divenne il fondamento della programmazione moderna.
La programmazione strutturata non eliminò completamente il problema. Il codice spaghetti passò a un nuovo livello — invece di goto fisici, gli sviluppatori iniziarono a creare “goto” logici: variabili globali, callback hell in JavaScript, catene di chiamate complesse e dipendenze implicite tra componenti. Il problema è rimasto, è cambiata solo la forma.
Callback hell in JavaScript, Promise profondamente annidate, async/await senza gestione degli errori, eventi di cui nessuno capisce chi o quando li attiva — tutte queste sono varianti moderne del codice spaghetti. L’antipattern vive e prospera, solo che ora non usa l’istruzione goto.
Mancanza di livelli — il primo e principale segno. Nel codice spaghetti, logica di business, operazioni sul database, markup HTML e comunicazione di rete sono tutti mescolati in un unico file o addirittura in un unico metodo. Modificare una query del database può rompere la visualizzazione dell’interfaccia perché il codice di questi livelli non è separato.
Variabili globali e singleton — il secondo segno evidente. Quando lo stato dell’applicazione è memorizzato in oggetti globali, il flusso di esecuzione diventa imprevedibile. Qualsiasi funzione può modificare lo stato globale e rintracciare dove e quando è successo è praticamente impossibile.
God class e god function — il terzo segno. Una classe con 2000+ righe che gestisce logica di business, visualizzazione e operazioni sui dati — questo è tipico codice spaghetti. Una funzione che prende 10 parametri e fa 5 cose diverse — anche.
| Segno | Descrizione | Esempio |
|---|---|---|
| Mescolanza di livelli | Query SQL dentro codice UI | Controller con scrittura diretta al DB |
| Variabili globali | Stato accessibile da ovunque | static SessionManager in ogni classe |
| God class | Una classe fa tutto | OrderManager con 3000 righe |
| Metodi lunghi | Funzioni senza scomposizione | Metodo di 200 righe con 5 responsabilità |
| Callback hell | Callback annidati all’infinito | 6 livelli di annidamento in JavaScript |
Se non puoi scrivere un test unitario per una funzione senza creare 15 oggetti mock — quello è codice spaghetti. Se testare un singolo modulo richiede l’avvio dell’intera infrastruttura dell’applicazione — quello è codice spaghetti. La non testabilità è un indicatore oggettivo di architettura aggrovigliata.
Mancanza di progettazione architetturale — la causa più comune. Quando un team inizia a scrivere codice senza un piano, scegliendo l’architettura “al volo”, il risultato diventa inevitabilmente spaghetti. Ogni nuova funzionalità viene aggiunta dove è “conveniente ora”, non dove appartiene logicamente.
Sviluppo evolutivo — la seconda causa. Un progetto inizia come un piccolo script, poi cresce con funzionalità, poi diventa un’applicazione e poi un monolito. Nel frattempo, l’architettura non viene riconsiderata. Ciò che funzionava per 100 righe di codice diventa un disastro per 100.000 righe.
Violazione dei principi SOLID — la terza causa. Soprattutto il principio di responsabilità unica (S) e il principio di inversione delle dipendenze (D). Quando una classe è responsabile di tutto, le dipendenze sono rigide e i moduli sono strettamente accoppiati — ottieni codice spaghetti.
Scadenze e cultura dell’hotfix — catalizzatori del codice spaghetti. Quando “serviva ieri”, gli sviluppatori inseriscono codice nel primo posto disponibile senza pensare all’architettura. Dieci hotfix di questo tipo — e l’architettura dell’applicazione è distrutta.
La principale conseguenza — perdita di controllo sulla base di codice. Gli sviluppatori smettono di capire come funziona l’applicazione nel suo insieme. Un cambiamento in un punto ne rompe un altro, apparentemente non correlato. Ogni patch crea due nuovi bug. Il team entra in uno stato di “paura dei cambiamenti”.
La produttività del team crolla esponenzialmente. Microsoft Research (2023) ha mostrato che il tempo per aggiungere una nuova funzionalità nel codice spaghetti cresce quadraticamente rispetto alla dimensione della base di codice. Per architettura pulita, questa crescita è lineare. La differenza diventa critica a 50.000+ righe di codice.
Sicurezza — un’altra vittima. Nel codice spaghetti, è facile trascurare un’eccezione non gestita, una validazione di input errata o una fuga di dati. L’audit di sicurezza in un progetto con architettura aggrovigliata è praticamente impossibile — trovare tutti i punti in cui viene utilizzato l’input utente è irrealizzabile.
Il turnover nei progetti con codice spaghetti è superiore alla media. Gli sviluppatori esperti se ne vanno perché non vogliono lavorare con la “pastasciutta”. I nuovi dipendenti non riescono a capire il codice e se ne vanno nei primi mesi. Il progetto perde competenze, il che peggiora ulteriormente la qualità del codice — un circolo vizioso.
Primo — inizia separando i livelli. Dividi il codice in tre livelli: presentazione (UI, controller), logica di business (servizi, casi d’uso) e accesso ai dati (repository, DAO). Anche una separazione parziale migliora immediatamente la struttura e rende il codice testabile.
Secondo — implementa dependency injection. Sostituisci la creazione diretta di dipendenze con il passaggio attraverso costruttori o parametri. Questo rompe le connessioni rigide tra i componenti e permette di testare ogni modulo isolatamente.
Terzo — estrai god class e god function. Suddividile in piccole classi e metodi con un’unica responsabilità. Usa il pattern Facade per semplificare sottosistemi complessi. Ricorda: una classe di 20 righe è più chiara di una classe di 2000 righe.
// spaghetti — tutto in un metodo
function handleRequest(req, res) {
const db = new Database("mysql://...");
const user = db.query("SELECT * FROM users WHERE id =" + req.params.id);
let html = "";
html += ""
+ user.name + "";
html += "Balance: "
+ user.balance + "";
html += "";
res.send(html);
}
// architettura pulita — livelli separati
class UserController {
constructor(userService) {
this.userService = userService;
}
async getUser(req, res) {
const user = await this.userService.findById(req.params.id);
res.json(new UserResponse(user));
}
}
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}
async findById(id) {
return await this.userRepository.findById(id);
}
}
Non tentare di riscrivere l’intera base di codice in una volta — è un fallimento garantito. Scegli un modulo, scrivi test di caratterizzazione che catturano il comportamento attuale, e solo dopo rifattorizza. Gradualmente, modulo dopo modulo, districherai gli spaghetti.
Pianificazione architetturale — la base della prevenzione. Prima di iniziare lo sviluppo, approva uno stile architetturale: MVC, MVVM, Clean Architecture, VIPER o altro. Scrivi un ADR (Architecture Decision Record) che giustifichi la scelta. Richiedi il rispetto dell’architettura nelle revisioni del codice.
Il principio di inversione delle dipendenze (DIP) — un potente strumento contro il codice spaghetti. I moduli di alto livello non dovrebbero dipendere da moduli di basso livello. Entrambi dovrebbero dipendere da astrazioni. Dependency Injection è l’implementazione pratica di questo principio.
I test — la migliore prevenzione. Se scrivi test prima del codice (TDD), inevitabilmente progetti componenti debolmente accoppiati. Il codice testabile è codice ben strutturato. Il codice non testabile è quasi sempre codice spaghetti.
SonarQube — tiene traccia della complessità ciclomatica, profondità di ereditarietà, dimensione dei metodi. JDepend (Java) — misura le dipendenze tra i pacchetti. PhpMetrics — fornisce un indice di manutenibilità per progetti PHP. Monitora le metriche in CI/CD — previeni la comparsa della pastasciutta piuttosto che combatterla dopo il fatto.
Domande frequenti
Sì, il refactoring graduale è preferibile. Usa il metodo Strangler Fig — sostituisci gradualmente i vecchi componenti con nuovi senza fermare l’applicazione. Inizia separando il livello dei dati o la logica di business. Copri il vecchio codice con test prima delle modifiche per non perdere funzionalità.
Il codice spaghetti — è un intreccio caotico di tutti i livelli dell’applicazione. Il codice lasagna è un’architettura rigorosamente multistrato, ma ogni livello è così isolato che il trasferimento dei dati tra loro diventa burocratico. Entrambi gli antipattern sono dannosi, ma il codice spaghetti è più pericoloso — rende il codice imprevedibile.
Guarda le dipendenze: se un modulo importa moduli da tutti i livelli dell’applicazione — è sospetto. Presta attenzione alla dimensione dei metodi — più di 30 righe di solito è male. Verifica se una funzione mescola lavoro UI, logica di business e dati. Se sì — è codice spaghetti.
Clean Architecture di Robert Martin e Architettura Esagonale (Ports & Adapters) — i due migliori approcci. Entrambi garantiscono separazione dei livelli, indipendenza della logica di business dai framework e testabilità. Per lo sviluppo mobile — MVVM con il pattern Repository.
Parzialmente. Metriche come la complessità ciclomatica (McCabe), l’accoppiamento dei moduli e la profondità dell’albero di ereditarietà (DIT) indicano potenziale codice spaghetti. SonarQube, CodeClimate e PhpMetrics calcolano automaticamente queste metriche. Tuttavia, la diagnosi completa richiede un’analisi architetturale umana.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche