modulo 08 / lezione 45

Debugging di dati, backend e integrazioni

Config, dipendenze e ambienti

Trovare differenze tra locale, preview e produzione dovute a env, versioni, cache, build, lock file o servizi collegati.

55 min Debugging

In parole semplici

Il classico “funziona sul mio computer”. Tre cause coprono il 90% dei casi:

  1. Variabili d’ambiente diverse (chiavi, URL, flag).
  2. Dipendenze con versioni o lock file divergenti.
  3. Build, cache, servizi collegati con configurazioni differenti.

Diagnosi sistematica: una matrice ambiente che mette in colonna locale, preview, produzione e in riga ciò che può differire.

Parole nuove

  • environment variable (env): variabile letta dal processo all’avvio (es. DATABASE_URL, API_KEY).
  • lock file: package-lock.json, yarn.lock, Gemfile.lock — versioni esatte di tutte le dipendenze.
  • build artifact: output del builder (bundle, immagine docker).
  • service dependency: servizio esterno (DB, cache, API).

Cosa impari

  • Costruire una matrice ambiente.
  • Verificare lock file e versioni.
  • Diagnosticare cache e build artifact disallineati.

La matrice ambiente

AspettoLocalePreviewProduzione
Node version20.1120.1020.9
DATABASE_URLpostgres://localhostpostgres://previewpostgres://prod
STRIPE_KEYsk_test_sk_test_sk_live_
FEATURE_NEW_UItruefalsefalse
package-lock.json SHAabcabcdef
CDN cachen/aaggressivaaggressiva
DB schema version474746

Discrepanze visibili a colpo d’occhio. Cella diversa = sospetto immediato.

Variabili d’ambiente

Convenzioni:

  • File .env per dev locale (mai committato).
  • File .env.example (committato) con chiavi, senza valori.
  • Sistema di secret management (Vercel, GitHub Actions, AWS Secrets Manager) per preview/produzione.

Bug ricorrenti:

  • Chiave mancante: il codice legge process.env.X che è undefined. Aggiungi default? Lancia errore? Decisione esplicita.
  • Chiave con typo: PROCESS_ENV vs process.env.PROCESS_END. Linter o validazione runtime.
  • Chiave con valore vuoto: presente ma "". if (env.X) falsy.

Validazione runtime con zod (TS):

import { z } from "zod";

const envSchema = z.object({
  DATABASE_URL: z.string().url(),
  STRIPE_KEY: z.string().min(1),
  PORT: z.coerce.number().default(3000),
});

export const env = envSchema.parse(process.env);

Se manca o è invalida, il processo non parte. Fail-fast.

Lock file

Senza lock file, ogni npm install può tirare giù versioni diverse. Con lock file, tutti hanno lo stesso albero.

Bug ricorrenti:

  • npm install senza lock: dipendenza minore aggiornata, comportamento cambiato.
  • Lock file divergente tra team: due rami con stesso package.json ma package-lock.json diversi → versioni diverse.
  • Conflitti su lock file: la tentazione di “risolvere con la mia versione” senza capire. Quasi sempre porta bug.

Buona pratica:

# in CI e in fresh setup
npm ci          # installa esattamente da lock, fallisce se divergenze

# in dev quando aggiungi dipendenze
npm install nuova-libreria  # aggiorna lock
git diff package-lock.json  # rivedi cosa è cambiato

Versioni transitive

Una dipendenza diretta può cambiare di poco, ma una sotto-dipendenza cambia molto. Esempio: aggiorni vite da 5.1 a 5.2. Sotto, esbuild passa da 0.20 a 0.21. Comportamento del build cambia.

Diagnosi:

npm ls esbuild
# vedi quale versione effettiva è installata e da chi viene tirata

Build artifact e cache

Bug: “lo stesso codice produce due bundle diversi tra locale e CI”. Cause:

  • Cache del build (Vite .vite/, Next .next/, Turbo .turbo/) sporca.
  • Tempo di sistema usato nel build (es. timestamp embeddato).
  • Dipendenze native ricostruite con versioni diverse.

Diagnosi:

  • Pulisci cache (rm -rf node_modules/.vite, rm -rf .next).
  • Confronta SHA dei file di output.

Servizi collegati: produzione vs staging

DB, cache Redis, queue, S3 hanno configurazioni diverse per ambiente. Bug tipici:

  • Connessione a DB di staging dal codice di produzione (catastrofico).
  • Bucket S3 sbagliato (perdita dati).
  • Region AWS diversa (latenza, costo).

Verifica al boot:

console.log("DB host:", new URL(env.DATABASE_URL).host);
console.log("Bucket:", env.S3_BUCKET);

Mai stampare credenziali. Stampa identificatori.

Procedura di diagnosi “works on my machine”

  1. Riproduci in locale lo stato di produzione: imposta NODE_ENV=production, costruisci il bundle, esegui.
  2. Confronta le env: printenv | sort > local.env, fai lo stesso in produzione, diff.
  3. Confronta lock file: git diff package-lock.json con la versione di produzione.
  4. Confronta schema DB: versione di migrazione applicata.
  5. Confronta versioni runtime: node --version, npm --version.

Una di queste differenze contiene il bug.

Esercizio guidato

Per un tuo progetto piccolo:

  1. Stampa al boot tutte le env rilevanti (senza segreti).
  2. Aggiungi validazione zod per le env.
  3. Avvia con una env mancante: deve fallire chiaramente.
  4. Confronta npm install con npm ci: differenze in node_modules SHA?
  5. Costruisci due volte di seguito: gli artefatti sono identici (deterministic build)?

Errori frequenti del principiante

  • Default silenziosi su env mancanti. Bug latenti.
  • .env committato con valori reali.
  • Saltare npm ci in CI.
  • Diagnosi “fideistica”: “deve essere uguale”. Verifica con diff.