Web Design and Engineering23 luglio 20267 min di lettura

Proliferazione delle dipendenze npm: quanto costa e come ridurla

Un progetto Next.js appena creato installa oltre 1.000 pacchetti prima del codice. Ecco quanto costa la proliferazione delle dipendenze npm e come ridurla.

a bunch of blue wires connected to each other

La proliferazione delle dipendenze npm è la crescita fuori controllo dei pacchetti che un progetto frontend installa, sia quelli scelti sia le centinaia che non hai mai visto, e gonfia i tempi di installazione, il peso del bundle e l'esposizione a rischi di sicurezza senza aggiungere una sola funzione. È il motivo per cui un progetto Next.js appena creato porta con sé oltre mille pacchetti prima ancora di scrivere una riga di codice, ed è il motivo per cui la cartella node_modules è l'oggetto più pesante del repository.

Ogni team ci arriva allo stesso modo. Aggiungi una libreria per risolvere un problema. Quella libreria ne aggiunge dodici sue. Sei mesi dopo nessuno sa più cosa faccia metà dell'albero, npm install impiega novanta secondi e arriva un avviso di sicurezza su un pacchetto profondo tre livelli che non hai mai deciso di usare. Qui analizziamo quanto costa davvero questa proliferazione e le mosse che usiamo per tenerla sotto controllo.

Quanto ti costa la proliferazione delle dipendenze npm

I numeri sono peggiori di quanto molti team immaginino. Un progetto npm medio trascina circa 79 dipendenze transitive, pacchetti che nessuno sviluppatore ha mai controllato direttamente (Gecko Security). Su un'app reale il conto sale in fretta: un'applicazione React con 50 dipendenze dirette ne risolve tipicamente da 800 a 1.200 transitive, e un progetto Next.js parte oltre le mille prima che esista il codice applicativo.

Quel peso è fisico. Christoph Nakazawa, in passato alla guida dei team di React Native e Jest, ha misurato la node_modules del proprio sito: oltre 40.000 file e più di 570 MiB (cpojer.net). Moltiplica per ogni branch, ogni run di CI, ogni portatile e ogni build in container. Tempi di installazione, minuti di CI a cache fredda e spazio su disco crescono tutti su un albero che in gran parte non verrà mai richiamato.

Poi c'è la parte che toglie il sonno a chi si occupa di sicurezza. Il numero di dipendenze è superficie d'attacco. Nel 2026 oltre il 99% di tutto il malware open source prende di mira npm, che diventa così l'ecosistema di pacchetti più attaccato al mondo (Sonatype). Le pubblicazioni di pacchetti malevoli sono cresciute del 73% in un anno (Adyog). Gran parte di quel rischio entra dai pacchetti transitivi, quelli che non hai scelto e che non sai nemmeno nominare. La versione 12 di npm ha iniziato a bloccare per impostazione predefinita gli script di installazione, definiti la più grande superficie di esecuzione di codice dell'ecosistema (TechTimes).

Il peso del bundle è l'ultimo costo, quello che sentono gli utenti. Il codice morto nell'albero diventa codice morto nel bundle inviato al browser, che diventa caricamenti più lenti e Core Web Vitals peggiori. La versione a runtime di questo problema l'abbiamo trattata negli errori frontend che affossano il punteggio Lighthouse.

Perché "basta lanciare npm audit" non risolve nulla

La reazione istintiva è lanciare npm audit, correggere ciò che segnala e andare avanti. Ma così curi i sintomi. npm audit dice quali pacchetti installati hanno avvisi noti. Non dice quali pacchetti non ti servono, quali si duplicano a vicenda o quale micro-pacchetto a funzione singola potrebbe essere quattro righe scritte da te. Un albero può superare l'audit senza errori ed essere comunque grande il doppio del necessario.

Nemmeno cancellare i pacchetti a mano funziona su larga scala. Nessuno ricorda perché una dipendenza sia stata aggiunta, quindi resta lì per paura. Il risultato è un meccanismo a cricchetto: l'albero può solo crescere. La soluzione non è una pulizia una tantum. È un insieme di abitudini che rendono l'aggiunta di una dipendenza una decisione e non un riflesso.

Cosa tiene davvero sotto controllo la proliferazione

Misura il costo reale prima di installare

Quasi tutte le dipendenze si aggiungono alla cieca. L'abitudine che cambia le cose è controllare il costo transitivo completo prima di npm install, non dopo. BundlePhobia mostra il peso reale che un pacchetto aggiunge al bundle, incluso tutto ciò che si trascina dietro, così confronti due librerie sul costo prima di sceglierne una (BundlePhobia). Un pacchetto che nel README sembra piccolo può portare un megabyte di peso transitivo. Meglio saperlo prima che entri nel lockfile, non durante una revisione delle prestazioni.

Trova ed elimina le dipendenze morte in automatico

Non puoi tagliare ciò che non vedi. Due strumenti individuano i pacchetti inutilizzati con l'analisi statica. Knip è diventato lo standard del 2026: supporta oltre 50 framework, tra cui Next.js, Vite e Astro, e in un solo passaggio trova file, export e dipendenze inutilizzati (Knip). Depcheck è più mirato, segnala solo le voci inutilizzate del package.json, ma è collaudato con 400.000 download a settimana. Fanne girare uno in CI a ogni pull request. Intercettare una dipendenza di troppo in fase di revisione costa poco. Trovarla due anni dopo è archeologia.

Preferisci la piattaforma al micro-pacchetto

Una bella fetta di qualsiasi albero è fatta di pacchetti che duplicano ciò che la piattaforma ormai fa da sola. Formattazione delle date, copia profonda, suddivisione di array, generazione di UUID: tutto già incluso nel JavaScript moderno e nel browser. Ogni micro-dipendenza che sostituisci con una chiamata alla libreria standard elimina non solo quel pacchetto ma tutto il suo sottoalbero. È la stessa logica dietro la scelta di togliere Tailwind dalla produzione e di trattare le librerie di componenti di terze parti come una scorciatoia costosa: la dipendenza che non aggiungi è quella che non dovrai mai controllare, aggiornare o inserire nel bundle.

Blocca l'albero e chiudi il buco degli script di installazione

Metti il lockfile sotto controllo di versione, installa con npm ci in CI così l'albero è riproducibile e, su npm 12 o versioni successive, tieni attivo il blocco degli script di installazione. Se un pacchetto ha bisogno di uno script post-installazione per funzionare, è un segnale da esaminare, non da lasciar passare. Fissa le versioni e lascia che uno strumento pianificato come Dependabot o Renovate proponga gli aggiornamenti in lotti verificabili, invece di far derivare l'albero da solo.

Tratta le dipendenze come una voce di bilancio

Fissa una regola che tutto il team possa applicare: una nuova dipendenza richiede una giustificazione di una riga nella pull request. Cosa fa, quanto costa in peso transitivo e perché scriverla da soli sarebbe peggio. Sembra burocrazia. Richiede dieci secondi ed è l'abitudine a più alto rendimento per tenere l'albero piatto, perché sposta la decisione nell'unico momento in cui qualcuno capisce davvero il compromesso.

Come si presenta nella pratica

In un audit recente su una dashboard Next.js di medie dimensioni lo schema era da manuale. L'app aveva 61 dipendenze dirette e poco meno di 1.300 nell'albero risolto. Knip ha segnalato 14 pacchetti diretti senza alcun riferimento, avanzi di funzioni poi rimosse. C'erano tre diverse librerie per le date, installate perché tre sviluppatori avevano scelto ciascuno la propria. Due pacchetti di utilità reimplementavano funzioni che ormai il linguaggio include. Rimuovere i 14 pacchetti morti e unificare i doppioni ha richiesto un pomeriggio, ha ridotto l'installazione di una quota significativa e ha alleggerito il bundle inviato al browser. Niente di tutto questo ha richiesto di riscrivere il codice di prodotto. Ha richiesto di guardare.

Anche il risultato di Nakazawa punta nella stessa direzione: applicare una potatura disciplinata all'albero di React Native ha ridotto di un ordine di grandezza il peso delle dipendenze di terze parti e migliorato i tempi di installazione nella stessa misura (cpojer.net). La proliferazione non è inevitabile. È l'impostazione predefinita che ottieni quando nessuno tiene d'occhio l'albero, e si inverte nel momento in cui qualcuno lo fa.

Foto di Scott Rodgerson su Unsplash

Domande frequenti

Quante dipendenze npm sono troppe?

Non esiste un numero fisso, ma il segnale utile è il rapporto tra dipendenze transitive e dirette e quante riesci davvero a giustificare. Un'app React che risolve da 800 a 1.200 pacchetti transitivi partendo da 50 diretti è normale; le stesse 50 dirette che ne trascinano 3.000 sono un campanello d'allarme. Guarda l'andamento più del valore assoluto: se l'albero cresce ogni trimestre e nessuno rimuove nulla, il numero è già troppo alto. Lancia Knip o depcheck e vedi quanti pacchetti sono inutilizzati. Qualsiasi valore sopra lo zero vale la pena tagliarlo.

Rimuovere le dipendenze inutilizzate rende davvero più veloce un sito?

Dipende da dove viveva la dipendenza. Rimuovere un pacchetto che finiva nel bundle del browser riduce il JavaScript da scaricare, interpretare ed eseguire, e questo migliora direttamente i tempi di caricamento e i Core Web Vitals. Rimuovere una dipendenza solo di build o di sviluppo non cambia il bundle ma velocizza installazioni e CI. In entrambi i casi riduci la superficie di sicurezza. Il guadagno in prestazioni è massimo quando il pacchetto morto stava nel percorso verso il browser.

La proliferazione delle dipendenze è un rischio di sicurezza anche se npm audit non segnala nulla?

Sì. npm audit riporta solo gli avvisi noti sulle versioni installate. Un pacchetto malevolo appena pubblicato, o uno appena compromesso, supera l'audit finché l'avviso non viene diffuso, cosa che può accadere giorni dopo l'attacco. Ogni pacchetto transitivo è codice che gira con i privilegi della tua app, quindi un albero più grande è una superficie d'attacco più grande a prescindere da ciò che l'audit dice ora. È per questo che npm 12 blocca gli script di installazione per impostazione predefinita e per questo tagliare i pacchetti inutilizzati conta quanto correggere quelli segnalati.

Conviene passare a pnpm o yarn per risolvere il gonfiore di node_modules?

pnpm agisce sul sintomo, non sulla causa. Il suo archivio con indirizzamento per contenuto collega tramite hard link i pacchetti condivisi tra progetti, così lo spazio su disco e i tempi di installazione a freddo calano nettamente, un sollievo reale su una macchina con molti repository. Ma pnpm non rimuove i pacchetti che non ti servono: l'albero resta grande uguale a livello logico, solo memorizzato in modo più efficiente. Usa pnpm per il vantaggio su disco e velocità, e continua a lanciare Knip per tagliare i pacchetti che non dovrebbero proprio esserci.

Articoli correlati

Studio

Inizia un progetto.

Un partner unico per il prodotto digitale che devi costruire. Produzione più veloce, tecnologie moderne, costi ridotti. Un team, una fattura.