Esegui `ls node_modules | wc -l` nel tuo progetto. Se il numero ti mette a disagio, non sei solo. Il progetto JavaScript medio trascina centinaia di dipendenze transitive, e ognuna è un rischio: vulnerabilità di sicurezza, breaking change, manutenzione abbandonata o semplicemente bloat.
Dopo anni alla guida di team frontend in aziende come AXA, Migros, Vontobel e UBS, ho sviluppato un'opinione forte: ogni dipendenza deve guadagnarsi il suo posto. Se puoi scriverla in 20 righe, probabilmente dovresti farlo.
Il costo nascosto di npm install
Le dipendenze non sono gratuite. Ognuna comporta costi che non compaiono nel tuo analizzatore di dimensioni del bundle:
Superficie di attacco per la sicurezza — ogni pacchetto è codice che non hai scritto e che non puoi controllare completamente. L'incidente event-stream, il dirottamento di ua-parser-js, il sabotaggio di colors.js — non sono casi limite, sono la conseguenza naturale dell'affidare la propria supply chain a migliaia di sconosciuti.
Onere di manutenzione — i pacchetti si aggiornano, e quegli aggiornamenti possono rompere le cose. Più pacchetti hai, più tempo passi sugli aggiornamenti delle dipendenze invece di sviluppare funzionalità. Ho visto team passare sprint interi solo a gestire gli alert di Dependabot.
Sovraccarico cognitivo — i nuovi sviluppatori che entrano nel tuo progetto devono capire non solo il tuo codice ma ogni astrazione introdotta dalle tue dipendenze. Quella libreria di utilità 'utile' con 200 funzioni significa 200 cose che qualcuno potrebbe usare al posto dell'approccio standard.
Impatto sul tempo di build — da Migros, gestivamo un monorepo PNPM con Turbo per Bikeworld e Micasa. Quando abbiamo verificato il nostro albero delle dipendenze e rimosso i pacchetti non necessari, la nostra pipeline CI è diventata significativamente più veloce. È tempo di sviluppo risparmiato su ogni singola PR.
Prima di installare, poniti tre domande
Impongo una semplice checklist nei miei team prima di aggiungere qualsiasi dipendenza:
1. Possiamo scriverlo noi stessi in meno di 50 righe? Se sì, scrivilo. Un piccolo utility che possiedi è meglio di un pacchetto che non controlli. Lo capisci completamente, ha zero dipendenze transitive e fa esattamente ciò di cui hai bisogno.
2. Questo pacchetto è mantenuto attivamente? Controlla la data dell'ultimo commit, le issue aperte e il bus factor. Un pacchetto con un solo maintainer che ha fatto l'ultimo commit 8 mesi fa è una bomba a orologeria.
3. Qual è il conteggio delle dipendenze transitive? Installalo in isolamento e controlla cosa trascina con sé. Ho visto pacchetti 'semplici' portarsi dietro più di 40 dipendenze transitive. Sono 40 pacchetti che possono rompersi, essere dirottati o venire abbandonati.
# Check what a package actually pulls in
mkdir /tmp/dep-check && cd /tmp/dep-check
npm init -y && npm install <package-name>
ls node_modules | wc -l
# If this number surprises you, reconsiderPacchetti comuni di cui non hai bisogno
Ecco esempi reali da progetti che ho ereditato e ripulito:
lodash — Il JavaScript moderno ha Array.flat(), Array.flatMap(), Object.entries(), Object.fromEntries(), structuredClone() e l'operatore di optional chaining. Importa la funzione lodash specifica se ne hai davvero bisogno (lodash/debounce), mai l'intera libreria.
moment.js — Usa l'API nativa Intl.DateTimeFormat, o al massimo date-fns con tree-shaking. Moment aggiunge 300 KB al tuo bundle ed è ufficialmente in modalità manutenzione.
axios — La Fetch API è disponibile ovunque ormai, incluso Node.js 18+. Non hai più bisogno di un wrapper attorno a XMLHttpRequest.
uuid — Se hai solo bisogno di ID univoci, crypto.randomUUID() è integrato in ogni runtime moderno.
classnames / clsx — Se usi Tailwind, probabilmente hai già un utility cn() o puoi scriverne uno in 3 righe con i template literal.
// Instead of: import { v4 as uuid } from 'uuid'
const id = crypto.randomUUID();
// Instead of: import axios from 'axios'
const res = await fetch('/api/data');
const data = await res.json();
// Instead of: import _ from 'lodash'
const unique = [...new Set(items)];
const grouped = Object.groupBy(items, (i) => i.type);Quando le dipendenze valgono la pena
Non sto dicendo di evitare tutti i pacchetti. Alcuni problemi necessitano genuinamente di soluzioni esterne:
Crittografia — non scrivere mai la tua. Usa librerie consolidate e verificate.
Parsing complesso — analisi di date con gestione dei fusi orari, parsing CSV/XML, rendering markdown. Sono domini in cui i casi limite ti divoreranno.
Integrazioni di framework — se usi Next.js, React o Vue, i pacchetti del loro ecosistema esistono per buone ragioni. next/image ottimizza le immagini meglio di qualsiasi cosa scriveresti tu stesso.
Da Vontobel, abbiamo mantenuto il nostro numero di dipendenze ridotto costruendo una piattaforma finanziaria complessa con prezzi WebSocket in tempo reale. Le dipendenze chiave — React, Next.js e alcune librerie specializzate per la formattazione finanziaria — si sono tutte guadagnate il loro posto. Tutto il resto lo abbiamo scritto noi o abbiamo usato le API della piattaforma.
Un approccio più sano
La mia regola empirica: tratta le dipendenze come le assunzioni. Ognuna dovrebbe passare attraverso un processo di revisione. Risolve un problema genuinamente difficile (crittografia, generazione PDF, elaborazione immagini)? Tienila. Ti fa risparmiare 15 minuti per scrivere una funzione di utilità? Scrivi la funzione.
Esegui un audit delle dipendenze ogni trimestre. Rimuovi i pacchetti inutilizzati. Sostituisci i pacchetti pesanti con alternative più leggere o API native. Rendilo un'abitudine di team, non una pulizia una tantum.
Il risultato è un progetto più veloce da installare, più veloce da buildare, più facile da verificare e — soprattutto — più facile da mantenere a lungo termine per il tuo team. Il tuo io futuro ti ringrazierà quando non starai debuggando un problema in produzione causato da una dipendenza transitiva cinque livelli più in basso di cui non sapevi nemmeno l'esistenza.

