Versões Semânticas (SemVer) Explicado: Como Funciona e Exemplos

Versões Semânticas (SemVer) Explicado: Como Funciona e Exemplos

“`html




Versões semânticas (SemVer) explicado

Boas práticas de versionamento para APIs e bibliotecas

Versões semânticas (SemVer) explicado

Semântica de versão não é “número bonito”: é um contrato. Neste post eu organizo o raciocínio do SemVer,
mostro como decidir entre major, minor e patch, e explico
como tratar pré-lançamentos e compatibilidade real.

1) O que é SemVer e por que ele existe

O SemVer (Semantic Versioning) define um formato padronizado para a versão do software,
normalmente usado por bibliotecas e APIs. A ideia central é que a mudança de versão comunique, com
previsibilidade, o impacto para quem usa.

Regra prática: se você muda algo que quebra consumidores, você deve “gritar” no
major. Se você adiciona sem quebrar, você sobe minor. Se você corrige sem mudar comportamento,
sobe patch.

O SemVer reduz atrito entre equipes porque elimina adivinhação: o consumidor consegue entender a
compatibilidade potencial antes de atualizar.

2) Como interpretar “MAJOR.MINOR.PATCH” com critérios objetivos

A versão segue o padrão: MAJOR.MINOR.PATCH (com opcionais para pré-lançamento e build).
A parte mais importante é decidir quais tipos de mudanças justificam cada incremento.

  • PATCH (z):
    correções de bugs, ajustes internos e melhorias que não alteram a API/contrato
    nem o comportamento observável de forma incompatível.
  • MINOR (y):
    adição de funcionalidades compatíveis para consumidores existentes.
    Tipicamente: novos recursos opcionais, novos endpoints com comportamento não-breaking, sobrecargas,
    novos campos com semântica compatível (ex.: retornos adicionais).
  • MAJOR (x):
    mudanças incompatíveis com versões anteriores. Se um consumidor precisa ajustar código
    para voltar a funcionar, é MAJOR.

Um detalhe que eu sempre reforço: “compatível” significa compatível na prática.
Se o consumidor quebra por causa de uma mudança na forma esperada, a versão não pode ser minor ou
patch — é major.

3) Compatibilidade real: o que quebra alguém (mesmo sem mudar endpoints)

Em APIs e SDKs, “não quebrou” é relativo ao contrato que o consumidor usa. Às vezes você altera algo
que parece interno, mas muda comportamento observável.

Exemplos clássicos de mudanças incompatíveis (MAJOR):

  • Remoção de endpoint, parâmetro obrigatório, campo ou evento usado por consumidores.
  • Mudança de semântica: antes “X”, agora “Y” com o mesmo formato.
    Mesmo mantendo o tipo/shape, muda a interpretação.
  • Alteração de tipos de retorno/contratos que invalida parsing do consumidor
    (ex.: antes retornava string, agora retorna object).
  • Regras de validação que deixam entradas antes aceitas como inválidas.
  • Mudança de ordenação, idempotência, timeouts e garantias que alteram o comportamento dependente.
Regra de bolso: se você não consegue explicar a compatibilidade para um consumidor
existente em poucas frases, provavelmente é caso de MAJOR.

Para evitar surpresas, eu costumo validar mudanças com testes de contrato e cenários de consumidor:
simulo o uso real (cliente, serialização, erros, estados) e vejo se a atualização “passa” sem ajustes.

4) Pré-lançamentos, build metadata e como escolher a próxima versão

Além de MAJOR.MINOR.PATCH, o SemVer permite identificadores opcionais:

  • Pré-lançamento: MAJOR.MINOR.PATCH-PRERELEASE
    (ex.: 1.4.0-beta.2). Indica instabilidade e pode quebrar compatibilidade.
    Consumidores normalmente tratam isso como “não estável”.
  • Build metadata: MAJOR.MINOR.PATCH+BUILD
    (ex.: 1.4.0+20260429). É informativo; não deve afetar compatibilidade.

Na prática, existe um ciclo típico:

  • Se a mudança é compatível: incrementa MINOR e, se aplicável, prepara beta.
  • Se é correção compatível: incrementa PATCH (e sobe pré se estiver em ciclo de teste).
  • Se é quebra: incrementa MAJOR e volta o MINOR/PATCH para valores base,
    seguindo seu padrão de release.
Importante: o SemVer não garante que você “se protege” contra break.
Ele só descreve o que deveria quebrar/ não quebrar conforme a sua decisão.

Exemplo técnico: decidir o incremento de versão

Use um “checklist” simples antes do release. Abaixo vai um pseudocódigo que eu uso como
guia mental para escolher o tipo de mudança.

function nextVersion(current, change) {
  // current: { major, minor, patch }
  // change: { breaksContract, addsCompatibleFeatures, fixesBugs, prerelease, buildMeta }

  if (change.breaksContract) {
    return `${current.major + 1}.0.0${change.prerelease ? '-beta.1' : ''}${change.buildMeta ? '+' + change.buildMeta : ''}`;
  }

  if (change.addsCompatibleFeatures) {
    return `${current.major}.${current.minor + 1}.0${change.prerelease ? '-beta.1' : ''}${change.buildMeta ? '+' + change.buildMeta : ''}`;
  }

  if (change.fixesBugs) {
    return `${current.major}.${current.minor}.${current.patch + 1}${change.prerelease ? '-rc.1' : ''}${change.buildMeta ? '+' + change.buildMeta : ''}`;
  }

  // Nenhuma mudança relevante no contrato/comportamento
  return `${current.major}.${current.minor}.${current.patch}${change.buildMeta ? '+' + change.buildMeta : ''}`;
}

O que torna isso útil não é o “código”: é a disciplina.
Repare no primeiro caso (breaksContract) — ele domina todos os outros.

Quer continuar melhorando seu processo?

Se você gostou do raciocínio por trás de SemVer, vale a pena ler mais posts técnicos aqui no
yurideveloper.com.br sobre versionamento, contratos de API e releases com previsibilidade.

Dica final: trate SemVer como documentação executável do seu contrato — e mantenha a compatibilidade coerente com a versão.



“`