Salta al contenuto principale

Buone pratiche

Progetta ogni task per essere idempotente

Un task è idempotente se rieseguirlo produce lo stesso risultato di eseguirlo una volta, requisito non negoziabile perché le pipeline falliscono e gli orchestratori riprovano. Motivazione: retry, catchup e backfill sono ordinaria amministrazione, e i passi non idempotenti duplicano o corrompono i dati alla seconda esecuzione. Fai: sovrascrivi una partizione deterministica o fai MERGE su una chiave stabile. Non fare: append cieco di righe, che duplica silenziosamente i dati ogni volta che un task riparte dopo un guasto parziale.

Preferisci l'ELT e conserva i dati grezzi

Fai atterrare prima i dati grezzi non trasformati e trasformali sul posto usando il calcolo del warehouse, mantenendo il livello grezzo come record immutabile. Motivazione: lo storage cloud economico rende sostenibile conservare i dati grezzi, e ti permette di rieseguire o correggere le trasformazioni senza ri-estrarre da sistemi sorgente fragili. Fai: mantieni un livello bronze/raw e ricostruisci le tabelle a valle da esso. Non fare: scartare i dati sorgente dopo la trasformazione, restando incapace di rielaborare quando la logica cambia o si trova un bug.

Partiziona sulla colonna su cui filtri, ed evita partizioni minuscole

Scegli una chiave di partizione (di solito una data) che corrisponda al filtro di query più comune così i motori possono potare e scansionare meno. Motivazione: la potatura delle partizioni è una delle leve di costo e performance più grandi nell'analytics, ma l'over-partitioning crea il problema dei small files e overhead di metadati. Fai: partiziona per data di ingestione o evento a una granularità sensata e compatta i file piccoli. Non fare: partizionare su una chiave ad alta cardinalità come user_id, che esplode in milioni di partizioni minuscole.

Testa i dati come testi il codice

Codifica le aspettative di qualità dei dati come test automatici (unicità, not-null, integrità referenziale, intervalli ammessi) che girano nella pipeline. Motivazione: i dati cattivi sono un difetto, e coglierli alla sorgente è molto più economico che debuggare una dashboard sbagliata settimane dopo. Fai: fai fallire o metti in quarantena i dati che violano un contratto prima che raggiungano i consumatori. Non fare: affidarti ai consumatori per notare le anomalie, trasformando ogni problema di dati in un incendio a valle.

Rendi espliciti i cambiamenti di schema con i data contract

Tratta lo schema e la semantica di un dataset pubblicato come un contratto versionato tra produttore e consumatori. Motivazione: i cambiamenti silenziosi a monte sono la causa più comune di pipeline rotte, e un contratto rende l'interfaccia un artefatto testabile e negoziabile. Fai: versiona lo schema, aggiungi test lato produttore e annuncia i cambiamenti breaking con una finestra di migrazione. Non fare: rinominare o riutilizzare una colonna sul posto lasciando che ogni consumatore a valle la scopra rompendosi.

Separa l'orchestrazione dal calcolo

Lascia che l'orchestratore decida cosa gira, in che ordine e cosa succede in caso di guasto, mentre un motore separato fa l'elaborazione pesante. Motivazione: mescolare le responsabilità rende le pipeline difficili da scalare, testare e ragionare, e accoppia la logica di scheduling a quella dei dati. Fai: definisci dipendenze e retry in Airflow/Dagster/Prefect ed esegui il lavoro in Spark, nel warehouse o in dbt. Non fare: nascondere logica critica di scheduling e retry dentro script ad hoc lanciati da cron senza osservabilità.

Elabora per tempo evento e usa i watermark nello streaming

Nello stream processing, aggrega sul tempo in cui un evento è effettivamente avvenuto, non su quando è stato elaborato, e usa i watermark per limitare quanto a lungo attendi i dati in ritardo. Motivazione: reti e producer consegnano eventi fuori ordine, quindi le aggregazioni per tempo di elaborazione sono non deterministiche e sbagliate. Fai: definisci finestre a tempo evento con un watermark e una policy documentata per i dati in ritardo. Non fare: usare finestre sul tempo di elaborazione assumendo che gli eventi arrivino in ordine, cosa che conta male sotto carico o in replay.

Gestisci deliberatamente i dati in ritardo e fuori ordine

Decidi esplicitamente cosa succede agli eventi che arrivano dopo il watermark della loro finestra: scartare, output laterale o aggiornamenti limitati. Motivazione: i dati in ritardo sono inevitabili, e una policy non dichiarata significa che il sistema decide al tuo posto, spesso scartando silenziosamente i record. Fai: instrada gli eventi in ritardo a un percorso di dead-letter o di correzione e monitorane il volume. Non fare: assumere che i dati in ritardo non esistano, per poi scoprire i buchi solo quando i totali non quadrano.

Classifica e proteggi i dati sensibili fin dall'ingestione

Identifica PII e dati regolamentati al punto di ingestione e applica controllo degli accessi, mascheramento e retention di conseguenza. Motivazione: gli obblighi di privacy (come il GDPR) scattano nel momento in cui memorizzi dati personali, e ritrofittare la protezione su un lake sparso è costoso e soggetto a errori. Fai: tagga le colonne sensibili nel catalog, limita l'accesso per ruolo e maschera in non-produzione. Non fare: copiare dati di produzione grezzi con PII in ambienti analitici aperti "temporaneamente".

Cattura lineage e metadati automaticamente

Registra da dove viene ogni dataset, come è stato trasformato e chi lo possiede, idealmente a livello di colonna, in un catalog. Motivazione: il lineage abilita analisi d'impatto, debugging e conformità, e la documentazione manuale marcisce sempre. Fai: emetti il lineage dai tuoi strumenti di trasformazione e orchestrazione verso un catalog come DataHub o OpenMetadata. Non fare: affidarti a conoscenza tribale e pagine wiki obsolete per spiegare da dove viene un numero.

Monitora la salute dei dati, non solo il successo dei job

Osserva freschezza, volume, schema drift e distribuzioni dei valori, non solo se un job è uscito con zero. Motivazione: una pipeline può avere successo tecnicamente pur producendo dati vuoti, stantii o anomali, e i consumatori lo notano molto prima di qualunque alert. Fai: imposta soglie su conteggio righe, freschezza e tasso di null e fai alert sulle anomalie. Non fare: trattare un DAG verde come prova che i dati siano corretti.

Rendi i caricamenti incrementali il default, non i full reload

Elabora solo i dati nuovi o modificati usando change data capture, colonne high-watermark o esecuzioni con ambito di partizione. Motivazione: i full reload sprecano calcolo, allungano le pipeline e aumentano il carico sui sistemi sorgente man mano che i dati crescono. Fai: traccia un watermark o usa CDC e fai upsert idempotenti dei cambiamenti. Non fare: truncate-and-reload di grandi tabelle a ogni esecuzione, cosa lenta, costosa e fragile col crescere dei volumi.

Gestisci il costo come una preoccupazione ingegneristica di prima classe

Tratta la spesa di calcolo e storage come una metrica ingegneristica da misurare e ottimizzare, specie su piattaforme cloud a consumo. Motivazione: query inefficienti, full scan e cluster inattivi dominano silenziosamente la bolletta, e il costo è un vincolo reale su ciò che la piattaforma può fare. Fai: pota le partizioni, seleziona solo le colonne necessarie, dimensiona correttamente i cluster e imposta retention/TTL sui dati freddi. Non fare: eseguire scan SELECT * illimitati su tabelle di molti terabyte o lasciare grandi cluster accesi inattivi.

Tieni le trasformazioni versionate e revisionate

Definisci le trasformazioni come codice in version control con test, code review e CI, esattamente come il software applicativo. Motivazione: l'SQL ad hoc eseguito a mano è irriproducibile, non testato e impossibile da auditare o annullare. Fai: modella le trasformazioni in dbt o equivalente, revisiona i cambiamenti ed esegui i test in CI prima del deploy. Non fare: modificare tabelle di produzione con script una-tantum che non lasciano storia né copertura di test.