{ } Programmazione

Versionamento semantico: cosa significano i numeri di versione

Il versionamento semantico spiegato: MAJOR.MINOR.PATCH, modifiche incompatibili, versioni 0.x, pre-release e come indicare le dipendenze senza sorprese.

Una libreria passa dalla versione 2.4.1 alla 2.5.0, poi alla 3.0.0. Che cosa ci dicono questi numeri? Se seguono il versionamento semantico, ci dicono molto: se l'aggiornamento è sicuro, se aggiunge funzioni, se richiede di modificare il nostro codice. È una convenzione semplice che, quando viene rispettata, rende la gestione delle dipendenze molto più prevedibile.

La struttura MAJOR.MINOR.PATCH

La specifica del versionamento semantico, nota come SemVer, prevede un numero di versione in tre parti:

Parte Quando aumenta Esempio
MAJOR Modifiche incompatibili con le versioni precedenti da 2.5.3 a 3.0.0
MINOR Nuove funzionalità compatibili da 2.5.3 a 2.6.0
PATCH Correzioni di bug compatibili da 2.5.3 a 2.5.4

Quando si incrementa una parte, quelle alla sua destra tornano a zero: dopo la 2.5.3, una nuova funzionalità porta alla 2.6.0, non alla 2.6.3.

La promessa per chi usa la libreria è chiara:

  • aggiornare la PATCH dovrebbe essere sempre sicuro;
  • aggiornare la MINOR dovrebbe essere sicuro e può offrire funzioni nuove;
  • aggiornare la MAJOR può richiedere modifiche al proprio codice.

Cos'è una modifica incompatibile

Il punto delicato è capire che cosa rompe la compatibilità. SemVer si basa sul concetto di API pubblica: l'insieme di funzioni, classi, parametri e comportamenti che la libreria dichiara di offrire. Sono modifiche incompatibili, per esempio:

  • rimuovere o rinominare una funzione pubblica;
  • aggiungere un parametro obbligatorio a una funzione esistente;
  • cambiare il tipo o il formato di un valore restituito;
  • modificare un comportamento documentato, come l'ordine dei risultati o la gestione di un errore;
  • smettere di supportare una versione del linguaggio o del sistema operativo.

Sono invece compatibili, e quindi MINOR, l'aggiunta di una nuova funzione o di un parametro facoltativo con un valore predefinito che mantiene il comportamento precedente.

C'è un caso sottile: la correzione di un bug su cui qualcuno faceva affidamento. Tecnicamente è una PATCH, ma per chi dipendeva dal comportamento errato è una rottura. In questi casi conviene documentare bene la modifica e, se l'impatto è ampio, valutare una versione MAJOR.

Deprecare prima di rimuovere

Un buon progetto non rimuove le funzioni all'improvviso. Il percorso tipico:

  1. in una versione MINOR la funzione viene segnata come deprecata: continua a funzionare ma emette un avviso, indicando l'alternativa;
  2. la documentazione e le note di rilascio spiegano come migrare;
  3. nella successiva versione MAJOR la funzione viene rimossa.

In Python l'avviso si emette con il modulo warnings:

import warnings

def carica_dati(percorso):
    warnings.warn(
        "carica_dati è deprecata, usa leggi_dati",
        DeprecationWarning,
        stacklevel=2,
    )
    return leggi_dati(percorso)

Le versioni 0.x e le pre-release

Secondo la specifica, le versioni con MAJOR uguale a zero, come la 0.8.2, indicano uno sviluppo iniziale: l'API pubblica non è considerata stabile e può cambiare in qualunque momento. Usare una libreria 0.x in produzione è possibile, ma bisogna aspettarsi rotture anche tra versioni MINOR.

Le pre-release si indicano con un trattino e un identificativo: 3.0.0-alpha.1, 3.0.0-beta.2, 3.0.0-rc.1. Hanno precedenza inferiore rispetto alla versione finale: 3.0.0-rc.1 viene prima di 3.0.0. Servono a far provare le novità a chi lo desidera, senza che gli altri le ricevano automaticamente.

Indicare le dipendenze

Il versionamento semantico diventa davvero utile quando si dichiarano le dipendenze di un progetto. Gli strumenti di gestione dei pacchetti permettono di indicare intervalli di versioni accettabili:

Notazione Significato tipico
==2.5.3 Esattamente questa versione
>=2.5,<3 Qualunque versione 2.x a partire dalla 2.5
~=2.5 (Python) Compatibile con la 2.5, quindi da 2.5 fino a prima della 3.0
^2.5.3 (npm) Da 2.5.3 fino a prima della 3.0.0

Un intervallo come >=2.5,<3 esprime fiducia nella promessa di SemVer: accetto correzioni e nuove funzioni, ma non modifiche incompatibili. Per le applicazioni, poi, si affianca un lock file che registra le versioni esatte installate, così l'intervallo descrive ciò che è accettabile e il lock file ciò che è stato effettivamente testato.

SemVer non è una garanzia

Il versionamento semantico è un accordo tra persone, non un controllo automatico. Un manutentore può sbagliare e pubblicare una modifica incompatibile come MINOR. Per questo restano necessari test automatici eseguiti a ogni aggiornamento delle dipendenze e la lettura delle note di rilascio, soprattutto per le librerie più importanti del progetto.

Va anche ricordato che non tutti i progetti seguono SemVer: alcuni usano il versionamento basato sul calendario, come 2024.03, utile quando ciò che conta è la data del rilascio più della compatibilità. L'importante è che il progetto dichiari quale schema adotta.

In sintesi

  • SemVer usa tre numeri: MAJOR per modifiche incompatibili, MINOR per nuove funzioni compatibili, PATCH per correzioni.
  • La compatibilità si valuta rispetto all'API pubblica dichiarata dalla libreria.
  • Depreca con un avviso prima di rimuovere, e rimuovi solo in una versione MAJOR.
  • Le versioni 0.x non promettono stabilità; le pre-release precedono la versione finale.
  • Dichiara intervalli di dipendenze coerenti con SemVer, ma affidati comunque a test e lock file.
MM
Marco Marzaduri

Marco Marzaduri si occupa di cybersecurity, informatica forense e sviluppo web. Laureato in informatica e in psicologia, unisce l'analisi tecnica alla comprensione del fattore umano.

Leggi anche