Performance

Regressioni di performance in CI: il controllo che costa 70 ms

Settanta file di build, 172 KB di JavaScript compresso e 36 KB di CSS: il controllo di budget li legge in 61-77 ms e blocca il merge quando il peso cresce.

In sintesi

  • Un budget di peso in CI costa 61-77 ms su 70 file: si può mettere su ogni push.
  • Il JavaScript della build misura 511 KB non compressi e 172 KB dopo gzip.
  • Due chunk superano i 44 KB compressi: sono i primi da guardare quando il totale cresce.
  • Il budget sul peso non misura il tempo di esecuzione: una regressione di CPU passa lo stesso.

Una regressione di performance si blocca in CI con un budget sul peso degli asset, non con un test Lighthouse a ogni push. Su 70 file di build il controllo legge tutto in 61-77 ms e fallisce se il JavaScript compresso supera la soglia. La build attuale misura 172 KB di JavaScript gzip e 36 KB di CSS.

Il budget, con i numeri di oggi

VoceMisuratoSogliaCosa blocca
JavaScript totale, gzip172 KB200 KBun import pesante in più
CSS totale, gzip36 KB50 KBun tema o un framework intero
Chunk singolo, gzip44 KB50 KBuna libreria che finisce nel chunk vendor
Tempo del controllo61-77 ms2 snulla: è solo il costo del gate

Il budget su 200 KB lascia 28 KB di margine, cioè circa un componente pesante o due piccoli. Non è una soglia arbitraria: è il numero misurato più il margine che vuoi concedere alla prossima feature. Un budget senza margine si rompe alla prima settimana e viene disattivato.

Il controllo

const fs = require("fs"), zlib = require("zlib"), path = require("path");
const walk = (d, acc = []) => {
  for (const e of fs.readdirSync(d, { withFileTypes: true })) {
    const f = path.join(d, e.name);
    e.isDirectory() ? walk(f, acc) : acc.push(f);
  }
  return acc;
};
let js = 0, worst = 0;
for (const f of walk("client/dist")) {
  const gz = zlib.gzipSync(fs.readFileSync(f)).length;
  if (f.endsWith(".js")) { js += gz; worst = Math.max(worst, gz); }
}
console.log(Math.round(js / 1024) + " KB JS gzip, chunk peggiore " + Math.round(worst / 1024) + " KB");
if (js > 200 * 1024 || worst > 50 * 1024) process.exit(1);

Lo script richiede solo Node e la cartella client/dist già costruita: nessuna dipendenza, nessun browser. Esce con codice 1 quando supera una soglia, e in una pipeline questo ferma il merge. Mettilo dopo il passo di build e prima del deploy: un budget che gira sul codice vecchio non serve.

Cosa il budget non vede

Il peso non è il tempo: un chunk da 44 KB può diventare tre volte più lento senza cambiare dimensione, se cambia il lavoro che fa al primo render. Il budget intercetta la causa più comune, la dipendenza in più, e lascia passare la regressione più insidiosa, quella algoritmica. Per quella servono i dati di campo o una traccia in CI, che costano minuti invece di millisecondi.

Il limite da dichiarare

I 172 KB sono la somma di tutti i chunk, non quello che una singola pagina scarica: il caricamento reale dipende dal routing e dal code splitting. Un budget per rotta è più preciso e più difficile da mantenere, perché richiede di sapere quali chunk carica ogni pagina. In più gzip non è brotli: con brotli i numeri assoluti scendono, il confronto tra due build resta valido.

Dove continuare

Il catalogo EneUI indicizza 111.985 componenti, 1.556 blocchi e il record splite, un componente che si installa con le sue dipendenze e si vede subito nel budget. Per le soglie in pipeline leggi Lighthouse CI con soglie, per il peso delle librerie UI bundle size delle UI library, per caricare meno codice code splitting per route, e per il confronto tra architetture framework frontend a confronto.

Domande frequenti

Meglio un budget sul peso o Lighthouse CI?

Il budget sul peso costa millisecondi e intercetta la causa più comune, una dipendenza in più. Lighthouse CI costa minuti e vede anche il tempo, ma è rumoroso su runner condivisi. Insieme coprono cause diverse.

Quanto margine lascio sopra il valore misurato?

Tra il 10% e il 20%, abbastanza per una feature piccola e non abbastanza per una libreria intera. Un budget senza margine fallisce su ogni modifica e viene disattivato.

Il budget deve essere per file o per pagina?

Per pagina è più preciso, perché misura quello che l'utente scarica. Per file è più semplice e stabile: si calcola sulla build senza sapere quali chunk carica il routing.

Gzip o brotli nel budget?

Quello che serve davvero ai client. Brotli comprime meglio e dà numeri più bassi; l'importante è usare lo stesso algoritmo per la soglia e per la misura, altrimenti il confronto tra build non vale.

Come evito che il budget blocchi un hotfix urgente?

Con un'eccezione esplicita e registrata, non disattivando il controllo. Alza la soglia in una pull request a parte, con la data e il motivo, e riportala giù dopo il rilascio.