È uscito il Corso Java Completo
Torna al blog

Conventional commits e semantic versioning: la guida

Come scrivere messaggi di commit con una convenzione, cosa significano i numeri di versione tipo 2.4.1 e come le due cose si collegano tra loro.

Edoardo Midali

Edoardo Midali

Developer · Content Creator

6 min di lettura

Aprire lo storico di un progetto e trovare venti commit chiamati "fix", "update" e "modifiche" è un'esperienza comune e frustrante: la storia c'è, ma non dice niente. I conventional commits sono una convenzione per rendere quei messaggi leggibili — e si collegano direttamente a come si numerano le versioni. In questo articolo trovi entrambe le cose e il punto in cui si incastrano.

Il problema

Un messaggio di commit ha un solo scopo: spiegare perché una modifica è stata fatta.

Il cosa si vede dal codice. Il perché esiste solo nella testa di chi ha scritto — e sparisce in poche settimane. È il motivo per cui i messaggi generici sono uno spreco: occupano lo spazio dell'unica documentazione che non diventa mai obsoleta.

I conventional commits

È una convenzione che dà ai messaggi una struttura fissa, così che siano leggibili da una persona e interpretabili da uno strumento.

Il formato:

tipo(ambito): descrizione breve

corpo opzionale con più dettagli

footer opzionale

Esempi concreti:

feat(auth): aggiunge login con Google

fix(checkout): corregge il calcolo IVA per ordini esteri

docs: aggiorna le istruzioni di installazione

refactor(api): estrae la validazione in un modulo separato

I tipi principali

TipoQuando
featNuova funzionalità
fixCorrezione di un bug
docsSolo documentazione
refactorRiscrittura senza cambiare il comportamento
testAggiunta o modifica di test
choreManutenzione, dipendenze, configurazione
perfMiglioramento di prestazioni
styleFormattazione, senza effetti sul codice

I due che contano davvero sono feat e fix, per il motivo che vedremo tra poco. Gli altri sono utili ma non hanno conseguenze automatiche.

Le modifiche che rompono la compatibilità

Si segnalano con un punto esclamativo o con una nota nel footer:

feat(api)!: rimuove il parametro legacy dalla funzione di ricerca

BREAKING CHANGE: il parametro `oldFormat` non è più supportato.
Usare `format: 'legacy'` al suo posto.

È l'informazione più importante che un commit possa contenere, perché indica a chi usa il tuo codice che aggiornare richiederà delle modifiche.

Come scrivere la descrizione

Poche regole che rendono lo storico leggibile:

  • All'imperativo presente: "aggiunge", non "aggiunto" o "aggiungo"
  • Minuscola all'inizio, niente punto finale
  • Sotto i 72 caratteri
  • Descrivi il perché nel corpo, se non è ovvio dal titolo

Un test pratico: la descrizione dovrebbe completare la frase "Questo commit, se applicato, ...".

Il semantic versioning

Qui si collega la seconda metà.

Il semantic versioning è una convenzione per numerare le versioni in modo che il numero stesso dica cosa è cambiato.

MAJOR . MINOR . PATCH
  2   .   4   .   1
ParteSi incrementa quandoEsempio
MAJORRompi la compatibilità2.4.1 → 3.0.0
MINORAggiungi funzionalità compatibili2.4.1 → 2.5.0
PATCHCorreggi bug senza rompere nulla2.4.1 → 2.4.2

Le regole pratiche da ricordare:

  • Incrementando una parte, quelle a destra tornano a zero
  • Lo zero iniziale (0.x.y) significa "instabile": tutto può cambiare, nessuna promessa di compatibilità
  • La 1.0.0 è il momento in cui dichiari che l'interfaccia è stabile

Il valore del sistema è che chi usa il tuo codice sa cosa aspettarsi: una patch si aggiorna senza pensarci, una minor pure, una major richiede di leggere cosa è cambiato.

È anche il motivo per cui i gestori di dipendenze usano notazioni come ^2.4.1 — che significa "accetta tutti gli aggiornamenti fino alla prossima major".

Dove le due cose si incontrano

Ed è qui il vero motivo per cui vale la pena adottare i conventional commits.

La corrispondenza è diretta:

CommitIncrementa
fix:PATCH
feat:MINOR
BREAKING CHANGEMAJOR

Se i commit seguono la convenzione, la versione successiva si calcola da sola. Esistono strumenti che, a ogni integrazione nel ramo principale, leggono i commit, determinano il nuovo numero di versione, generano il registro delle modifiche e pubblicano il rilascio — senza che nessuno decida nulla a mano.

Il collegamento naturale è con le automazioni che descrivo in cosa sono le GitHub Actions: il rilascio diventa una conseguenza dei commit, non un'attività separata.

Il registro delle modifiche generato automaticamente è l'altro beneficio concreto: raggruppato per tipo, con i riferimenti ai commit, senza che nessuno debba scriverlo.

Vale la pena adottarli?

Una valutazione onesta, perché non è sempre sì.

Sì, quasi certamente, se:

  • Pubblichi una libreria o un pacchetto che altri usano
  • Lavori in un team di più persone
  • Vuoi generare rilasci e registro modifiche automaticamente
  • Il progetto vivrà per anni

Probabilmente no se:

  • È un progetto personale che non pubblicherai
  • Sei da solo e il progetto è breve
  • Il team non lo seguirà davvero

Il punto critico è l'ultimo. Una convenzione seguita a metà è peggio di nessuna convenzione: dà l'illusione di una struttura che non c'è, e gli strumenti automatici producono risultati sbagliati.

Se il team è d'accordo, un controllo automatico che verifica il formato dei messaggi lo rende semplice da mantenere.

Il minimo che vale sempre

Anche senza adottare la convenzione formale, due abitudini valgono per qualunque progetto:

Scrivi messaggi che spiegano il perché. "corregge il calcolo IVA sugli ordini extra-UE" batte "fix" in qualunque contesto, con o senza convenzioni.

Fai commit che contengono una cosa sola. Un commit che tocca tre argomenti diversi è illeggibile qualunque prefisso gli metti — ed è anche impossibile da annullare selettivamente.

Sono le stesse due qualità che rendono buona una pull request: uno scopo, e una spiegazione del motivo.

In sintesi

I conventional commits danno ai messaggi una struttura tipo(ambito): descrizione, dove i tipi che contano davvero sono feat, fix e la segnalazione delle rotture di compatibilità.

Il semantic versioning numera le versioni come MAJOR.MINOR.PATCH, dove il numero stesso dice se un aggiornamento è sicuro o richiede attenzione.

Le due cose si incastrano: con i commit strutturati, la versione successiva e il registro delle modifiche si generano da soli, e il rilascio diventa una conseguenza automatica del lavoro fatto.

Vale la pena adottarli su librerie pubbliche, progetti di squadra e cose destinate a durare. Su un progetto personale breve, sono complessità che non ripaga.

Ma le due abitudini che valgono sempre, con o senza convenzione, sono: spiega il perché, e fai commit che contengono una cosa sola.

Se i concetti base di Git non ti sono ancora familiari, il punto di partenza è cos'è il version control.