Go to main content
Schema evolution and change management - official lesson image on GinnyTech, created by AD

Schema evolution and change management

How to manage schema evolution in a data warehouse without breaking dashboards and ETL.

AD
Created byAndrii Dyshkantiuk
Lesson 95 / 236Level: AdvancedDuration: 18 minPrerequisites: 1

What you will learn

  • Mappare i consumer downstream e i job che toccano una tabella prima di proporre una modifica
  • Scegliere tra colonna additiva, vista intermedia e versionamento esplicito per la compatibilità
  • Applicare contratti di schema in build e monitorare dashboard e job con rollback pronto

Schema evolution and change management

Lo schema di un warehouse non è mai un oggetto isolato: sopra vivono dashboard, query salvate, modelli statistici e job ETL che si aspettano colonne con nome, tipo e significato stabili. Una colonna che sembra inutile può alimentare il report mensile di un altro team. Uno schema cambia in sicurezza solo quando sai chi lo usa e quale contratto non deve essere violato, e in questa lezione impari a governare quel cambiamento.

L’idea in una frase

La schema evolution governa ogni cambio di tabella senza rompere dashboard, job e analisi storiche. Uno schema cambia in sicurezza solo quando sai chi lo usa e quale contratto non deve essere violato.

La sequenza di gestione del cambiamento

  1. Mappa i consumer downstream e i job che toccano la tabella prima di proporre la modifica.
  2. Scegli la strategia compatibile tra colonna additiva, vista intermedia o versione esplicita con transizione.
  3. Applica il contratto di schema in build per bloccare violazioni prima del deploy in produzione.
  4. Esegui la migrazione con doppia esposizione e finestra di transizione comunicata ai consumer.
  5. Monitora dashboard e job dopo il rilascio e tieni pronto il rollback con soglia di rientro scritta.

Il problema concreto

Lo schema di un warehouse non è mai un oggetto isolato. Sopra vivono dashboard, query salvate, modelli statistici e job ETL che si aspettano colonne con nome, tipo e significato stabili. Modificare lo schema tocca un contratto implicito con tutti questi consumer. Una colonna che sembra inutile può alimentare il report mensile di un altro team. Uno schema cambia in sicurezza solo quando sai chi lo usa e quale contratto non deve essere violato.

Prima di toccare una tabella, rispondi in sequenza. Quale decisione operativa abilita il cambiamento? Quali consumer sono impattati? Esiste una variante retrocompatibile? Se non esiste, quale piano di migrazione chiude la transizione?

StepQuestion to askExpected output
DecisionChe cosa cambia se modifico lo schema in questo modo?Scelta esplicita
SignalQuali consumer e quali job toccano questo campo?Mappa di lineage
BaselineLo stato attuale funziona per tutti i consumer?Credible comparison
VincoloChe cosa si rompe se il cambiamento non è retrocompatibile?Assunzione da dichiarare
ActionQuale passo di migrazione segue, e con quale rollback?Piano controllabile
ElementRequested specification
Unit of analysistable, fact, dimension, grain, or data model
Primary signalgrain corretto, integrità, performance, costo query, tracciabilità
Baselineversione attuale dello schema e consumer che ne dipendono
Decisionschema, mart, query pattern, or architectural choice
Riskrompere un consumer che non sapevi esistesse

Evolution strategies

Tre approcci coprono la maggior parte dei casi reali. Il primo è additive-only, il più sicuro: aggiungi colonne e non ne rimuovi mai, con le vecchie marcate come deprecate. I nuovi consumer vedono la nuova colonna, i vecchi la ignorano senza accorgersi di nulla. Funziona in Snowflake, BigQuery e Postgres.

Il secondo è la vista intermedia: esponi una vista invece della tabella fisica e aggiorna la vista quando migri lo schema sottostante. La vista diventa il contratto stabile e la tabella fisica cambia sotto di essa.

Il terzo è il versionamento esplicito con tabelle come orders_v1 e orders_v2 in coesistenza temporanea. I consumer migrano quando sono pronti. È il più complesso e serve quando hai molti consumer indipendenti non coordinabili in un solo deploy.

In sintesi: usa additive-only come default, la vista intermedia come contratto stabile e il versionamento esplicito solo per migrazioni non coordinabili.

Verdetto: additive-only vince come default, la vista intermedia come contratto stabile e il versionamento esplicito solo per migrazioni non coordinabili.

Contracts and automated tests

Con i model contract di dbt, coperti nel modulo di analytics engineering, blocchi in build ogni modifica che viola lo schema atteso. Sposti la rottura dal runtime al build time: invece di una dashboard muta vedi una CI rossa che impedisce il deploy.

-- esempio di model contract dbt (semplificato)
-- la build fallisce se la colonna manca o cambia tipo
models:
  - name: orders
    config:
      contract:
        enforced: true
    columns:
      - name: order_id
        data_type: integer
      - name: status
        data_type: varchar

Prima del cambio passa la checklist: consumer impattati verificati sul lineage graph, retrocompatibilità confermata per tutti, piano di migrazione con finestra scritta se non compatibile, test automatici sul nuovo schema, rollback pronto in produzione.

SQL example: building a control view

Il pattern seguente è eseguibile nella maggior parte dei warehouse moderni e crea una base con metrica, segmento e finestra temporale per confrontare periodi e gruppi senza riscrivere la logica.

WITH base_events AS (
  SELECT
    user_id,
    account_id,
    event_type,
    event_time,
    DATE_TRUNC('week', event_time) AS week,
    source,
    device_type
  FROM events
  WHERE event_time >= CURRENT_DATE - INTERVAL '180 days'
    AND user_id IS NOT NULL
),
weekly_user_metrics AS (
  SELECT
    week,
    user_id,
    COALESCE(source, 'unknown') AS source,
    COALESCE(device_type, 'unknown') AS device_type,
    COUNT(*) AS total_events,
    COUNT(DISTINCT DATE(event_time)) AS active_days,
    COUNT(DISTINCT event_type) AS event_diversity,
    MAX(CASE WHEN event_type IN ('purchase', 'subscribe', 'activation') THEN 1 ELSE 0 END) AS reached_key_outcome
  FROM base_events
  GROUP BY week, user_id, source, device_type
)
SELECT
  week,
  source,
  device_type,
  COUNT(DISTINCT user_id) AS users,
  ROUND(AVG(active_days), 2) AS avg_active_days,
  ROUND(AVG(event_diversity), 2) AS avg_event_diversity,
  ROUND(AVG(reached_key_outcome) * 100, 2) AS key_outcome_rate
FROM weekly_user_metrics
GROUP BY week, source, device_type
ORDER BY week, source, device_type;

Quando lo schema cambia, è questa superficie di trend e segmenti a dirti subito se qualcosa si è incrinato.

Python example: checking stability and anomalies


# df contiene: week, segment, users, key_outcome_rate
# key_outcome_rate espresso in percentuale, es. 12.4

df = df.sort_values(['segment', 'week']).copy()
df['previous_rate'] = df.groupby('segment')['key_outcome_rate'].shift(1)
df['wow_change_pp'] = df['key_outcome_rate'] - df['previous_rate']
df['rolling_mean'] = df.groupby('segment')['key_outcome_rate'].transform(
    lambda s: s.rolling(4, min_periods=2).mean()
)
df['rolling_std'] = df.groupby('segment')['key_outcome_rate'].transform(
    lambda s: s.rolling(4, min_periods=2).std()
)
df['z_score'] = (df['key_outcome_rate'] - df['rolling_mean']) / df['rolling_std']

anomalies = df[df['z_score'].abs() >= 2].sort_values('z_score')
print(anomalies[['week', 'segment', 'key_outcome_rate', 'wow_change_pp', 'z_score']])

Il controllo con z_score vale doppio dopo una migrazione: evita reazioni al rumore e segnala le variazioni che meritano indagine.

La lezione di chi ha governato il cambiamento per decenni

Nel luglio 2008 Google pubblica open source Protocol Buffers 2, il sistema di serializzazione usato internamente per far evolvere migliaia di servizi senza fermare il mondo a ogni cambio di campo. Il meccanismo centrale è la compatibilità backward e forward tramite numeri di campo stabili: un campo rinominato resta leggibile e un campo aggiunto non rompe i lettori vecchi. Da allora il principio diventa il riferimento per ogni discussione di schema evolution tabellare. Rinominare una colonna senza alias, cambiare un tipo senza cast o aggiungere un valore a un enum senza avviso equivale a rompere un contratto. Gli schemi evolvono comunque, la differenza è tra chi governa il cambiamento e chi lo subisce.

Domande per ripassare

  1. Quali consumer downstream toccano la tabella prima della modifica?
  2. Quale strategia tra additiva, vista e versione garantisce compatibilità?
  3. Quale contratto in build blocca una colonna mancante o un tipo cambiato?
  4. Quale soglia di monitoraggio fa scattare il rollback dopo il deploy?
Serve una mano concreta?

Bloccato su questo argomento o vuoi applicarlo al tuo caso? Prenota una call di 15 minuti con un analista esperto.

Book a call