Guide

Liquibase: gestione delle modifiche al database e automazione CI/CD

Dario Fadda Maggio 24, 2026

La gestione delle modifiche allo schema di un database è uno degli aspetti più delicati e sottovalutati nei progetti software moderni. Script SQL sparsi, modifiche manuali non documentate, disallineamenti tra ambienti di sviluppo, staging e produzione: questi problemi sono comuni in quasi ogni team di sviluppo. Liquibase risolve questi problemi portando il controllo di versione al database, proprio come Git fa con il codice sorgente.

Cos’è Liquibase e perché usarlo

Liquibase è uno strumento open source per il database schema change management. Permette di definire, versionare e distribuire le modifiche allo schema del database tramite file di configurazione (chiamati changelog), supportando oltre 30 database diversi: Oracle, MySQL, PostgreSQL, SQL Server, DB2 e molti altri.

Il problema che Liquibase risolve è semplice ma critico: applicare modifiche al database con script SQL tradizionali è un processo manuale, error-prone e difficile da tracciare. Questi script spesso mancano di versioning, rendendo quasi impossibile sapere quale versione dello schema è in produzione rispetto allo sviluppo. Liquibase standardizza questo processo tramite file di configurazione versionati, tracciamento automatico via checksum MD5 e supporto nativo al rollback.

Concetti fondamentali

Prima di entrare nell’implementazione, è essenziale comprendere i quattro pilastri architetturali di Liquibase:

Changelog: è il file principale che contiene tutte le modifiche al database, organizzate in sequenza. Può essere in formato XML, YAML, JSON o SQL puro. Tipicamente si usa un file master che include sotto-changelog organizzati per release o sprint.

ChangeSet: è l’unità atomica di modifica, identificata univocamente da una coppia id + author. Ogni changeset viene eseguito una sola volta e tracciato nella tabella DATABASECHANGELOG. Una regola fondamentale: non modificare mai un changeset già eseguito; crea sempre un nuovo changeset per le correzioni.

Preconditions: verifiche condizionali che devono passare prima dell’esecuzione di un changeset. Permettono di rendere sicure le migrazioni anche in scenari complessi (es. verificare che una colonna non esista già prima di aggiungerla).

Contexts e Labels: meccanismi di filtraggio per controllare quali changeset vengono eseguiti in quale ambiente (dev, staging, prod). Indispensabili per gestire dati di test o configurazioni ambiente-specifiche.

Struttura base di un changelog YAML

Ecco un esempio pratico di changelog in formato YAML per la creazione di una tabella transazioni con supporto al rollback:

databaseChangeLog:
  - changeSet:
      id: create-payment-table
      author: devops.team
      changes:
        - createTable:
            tableName: payment_transaction
            columns:
              - column:
                  name: id
                  type: varchar(50)
                  constraints:
                    primaryKey: true
                    nullable: false
              - column:
                  name: amount
                  type: decimal(15,2)
                  constraints:
                    nullable: false
              - column:
                  name: currency_code
                  type: char(3)
                  constraints:
                    nullable: false
              - column:
                  name: transaction_date
                  type: timestamp
                  defaultValueComputed: CURRENT_TIMESTAMP
              - column:
                  name: status
                  type: varchar(20)
                  constraints:
                    nullable: false
      rollback:
        - dropTable:
            tableName: payment_transaction

Il blocco rollback è fondamentale: definisce esplicitamente come annullare la modifica, rendendo ogni deployment reversibile in modo prevedibile.

Configurazione per ambienti multipli

Un file liquibase.properties separato per ogni ambiente permette di gestire connessioni e contesti in modo sicuro:

# liquibase-dev.properties
changeLogFile=db/changelog.yaml
url=jdbc:postgresql://localhost:5432/devdb
username=dev_user
password=${DB_PASSWORD}
contexts=dev,test
defaultSchemaName=DEV_SCHEMA
logLevel=DEBUG

Nota importante: le credenziali non devono mai essere committate nel repository. Usa variabili d’ambiente, HashiCorp Vault, AWS Secrets Manager o Kubernetes Secrets per la gestione dei segreti.

Precondizioni per deployment sicuri

Le precondizioni rendono Liquibase robusto in ambienti dove lo schema potrebbe trovarsi in stati intermedi. Questo esempio in SQL nativo verifica che una colonna non esista prima di aggiungerla:

--liquibase formatted sql

--changeset devops.team:add-merchant-id
--preconditions onFail:MARK_RAN onError:HALT
--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'payment_transaction' AND column_name = 'merchant_id'

ALTER TABLE payment_transaction 
ADD COLUMN merchant_id VARCHAR(50);

--rollback ALTER TABLE payment_transaction DROP COLUMN merchant_id;

Il comportamento onFail:MARK_RAN istruisce Liquibase a segnare il changeset come già eseguito (senza eseguirlo) se la precondizione fallisce — comportamento ideale quando la colonna esiste già per altri motivi.

Integrazione in pipeline CI/CD

La vera potenza di Liquibase emerge quando viene integrato in una pipeline di deployment automatizzato. Ecco un esempio completo con Jenkins:

pipeline {
    agent any
    
    stages {
        stage('Validate') {
            steps {
                sh 'liquibase validate'
            }
        }
        
        stage('Deploy Staging') {
            steps {
                sh 'liquibase --contexts=staging update'
            }
        }
        
        stage('Deploy Production') {
            when { 
                branch 'main' 
            }
            steps {
                input 'Deploy to Production?'
                sh 'liquibase --contexts=prod update'
            }
        }
    }
    
    post {
        failure {
            sh 'liquibase rollbackCount 1'
        }
    }
}

Il blocco post { failure } garantisce il rollback automatico dell’ultimo changeset in caso di errore — un salvagente fondamentale in produzione.

Pattern Kubernetes: Init Container

Per architetture cloud-native, il pattern degli init container è ideale: Liquibase viene eseguito come container di inizializzazione prima dell’avvio dell’applicazione, garantendo che lo schema sia sempre aggiornato prima che il servizio inizi a ricevere traffico:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
spec:
  template:
    spec:
      initContainers:
      - name: liquibase-migration
        image: liquibase/liquibase:4.25.0
        command:
        - liquibase
        - --url=jdbc:postgresql://$(DB_HOST):5432/$(DB_NAME)
        - --username=$(DB_USER)
        - --password=$(DB_PASSWORD)
        - update
        env:
        - name: DB_HOST
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: host
      containers:
      - name: payment-service
        image: payment-service:latest
        ports:
        - containerPort: 8080

Best practice per ambienti enterprise

Alcune regole fondamentali per adottare Liquibase in contesti di produzione:

  • Principio del minimo privilegio: l’utente Liquibase deve avere solo i permessi DDL necessari, mai privilegi DBA completi.
  • Revisione obbligatoria: ogni modifica al changelog deve passare per una Pull Request con review da un secondo sviluppatore prima del merge.
  • Test su dati realistici: prima del deployment in produzione, eseguire le migrazioni in un ambiente staging con volumi di dati simili a quelli di produzione per individuare problemi di performance.
  • Mai modificare changeset già eseguiti: il checksum MD5 rileverei la modifica e bloccherebbe il deployment. Crea sempre un nuovo changeset per le correzioni.
  • Crea indici dopo i bulk insert: creare indici prima di caricare grandi quantità di dati aumenta enormemente il tempo di migrazione senza benefici pratici.

Monitoraggio con Prometheus e Grafana

Per ambienti enterprise, integrare le metriche Liquibase in un sistema di osservabilità permette di rilevare problemi prima che impattino la produzione. Le metriche chiave da tracciare includono:

  • Durata per changeset: identifica migrazioni lente che potrebbero causare downtime
  • Lock contention: conflitti sulla tabella DATABASECHANGELOGLOCK in ambienti multi-istanza
  • Checksum failures: modifica non autorizzata a changeset già eseguiti
  • Rollback rate: indicatore di qualità del processo di testing prima del deployment
# Esempio metriche Prometheus esportate da Liquibase
liquibase_deployment_success_total{environment="prod"} 145
liquibase_changeset_duration_seconds_bucket{id="1",database="prod",le="0.5"} 120
liquibase_rollback_total{environment="prod"} 3
liquibase_deployment_failure_total{environment="prod",reason="validation_error"} 2

Conclusione

Liquibase trasforma la gestione del database da processo manuale e rischioso a pratica ingegneristica controllata e auditabile. L’integrazione con i sistemi CI/CD permette di applicare agli schemi database gli stessi standard di qualità già applicati al codice applicativo: versionamento, review, test automatizzati e rollback prevedibile.

Per team che adottano DevOps o pratiche di continuous delivery, Liquibase non è un’opzione ma una necessità: è il tassello mancante tra il deployment del codice e quello del database.

Fonte: Liquibase: Database Change Management and Automated Deployments — DZone

💬 Unisciti alla discussione!


Questo è un blog del Fediverso: puoi trovare quindi questo articolo ovunque con @blog@spcnet.it e ogni commento/risposta apparirà qui sotto.

Se vuoi commentare su Liquibase: gestione delle modifiche al database e automazione CI/CD, utilizza la discussione sul Forum.
Condividi la tua esperienza, confrontati con altri professionisti e approfondisci i dettagli tecnici nel nostro 👉 forum community