---
title: "cargo-semver-checks: SemVer na CI | Rust Brasil"
url: "https://rustlang.com.br/blog/cargo-semver-checks-rust-api-ci-2026/"
markdown_url: "https://rustlang.com.br/blog/cargo-semver-checks-rust-api-ci-2026.MD"
description: "Use cargo-semver-checks para detectar mudanças incompatíveis em APIs Rust antes do release. Guia com baseline, workspace, GitHub Actions e limites práticos."
date: "2026-08-05"
author: "Equipe Rust Brasil"
---

# cargo-semver-checks: SemVer na CI | Rust Brasil

Use cargo-semver-checks para detectar mudanças incompatíveis em APIs Rust antes do release. Guia com baseline, workspace, GitHub Actions e limites práticos.


**O `cargo-semver-checks` é uma das formas mais práticas de impedir que uma mudança aparentemente pequena quebre usuários de uma crate Rust.** Ele compara a API pública da versão em desenvolvimento com uma versão de referência — normalmente a última publicada — e sinaliza alterações incompatíveis antes do release. O fluxo básico é instalar a ferramenta, rodar `cargo semver-checks check-release` e colocar a mesma verificação na CI.

Isso não elimina a necessidade de testes nem decide sozinho qual versão publicar. O ganho é transformar parte da revisão de **versionamento semântico (SemVer)** em uma checagem reproduzível: se uma função pública sumiu, um método mudou de assinatura ou uma variante importante deixou de existir, a pull request recebe um alerta antes que consumidores descubram a quebra em produção.

Este guia mostra como usar o `cargo-semver-checks`, escolher uma baseline, integrar a análise ao workspace e à CI, interpretar os resultados e separar **quebra estrutural de API** de **mudança comportamental**. Ele é especialmente útil para quem mantém bibliotecas no crates.io, SDKs internos, crates compartilhadas entre serviços ou componentes públicos de um monorepo.

## Por que SemVer é difícil mesmo com o Cargo

O arquivo `Cargo.toml` permite declarar uma versão como `1.4.2`, mas o número só tem valor se o projeto mantiver um contrato previsível:

- **patch** (`1.4.2` → `1.4.3`): correções compatíveis;
- **minor** (`1.4.2` → `1.5.0`): funcionalidades novas sem quebrar consumidores;
- **major** (`1.4.2` → `2.0.0`): mudanças incompatíveis na API ou no contrato.

O problema é que uma alteração pequena no diff pode ter um impacto grande para quem depende da crate. Remover um `pub`, restringir um tipo genérico, mudar um retorno, retirar uma implementação de trait ou adicionar requisitos a uma função pode fazer milhares de builds falharem.

O compilador protege o projeto atual, mas não compila automaticamente todos os consumidores que existem fora do repositório. O [Cargo](/ecossistema/cargo/) resolve dependências e respeita intervalos de versões; ele não prova que a nova API continua compatível com a anterior. É nessa lacuna que entra o `cargo-semver-checks`.

## O que é cargo-semver-checks

`cargo-semver-checks` é um subcomando do Cargo voltado à compatibilidade de API. Em termos práticos, ele:

1. constrói uma representação da API pública da versão atual;
2. obtém ou recebe uma **baseline** para comparação;
3. executa verificações sobre itens públicos, assinaturas e contratos estruturais;
4. informa mudanças que podem violar a política SemVer;
5. retorna falha quando encontra incompatibilidades relevantes, permitindo bloquear a CI.

A ferramenta trabalha sobre informações da documentação e da API Rust, não sobre uma comparação textual simples de arquivos. Isso reduz falsos sinais causados por refactors internos: reorganizar uma função privada pode produzir um diff enorme sem afetar consumidores, enquanto remover uma única variante pública pode ser uma quebra real.

### Que tipos de problema ela ajuda a encontrar?

Exemplos típicos incluem:

- remoção de função, struct, enum, trait ou módulo público;
- mudança incompatível na assinatura de uma função;
- remoção de método ou campo que fazia parte do contrato público;
- alterações em variantes de enum;
- remoção de implementações públicas relevantes;
- mudança de visibilidade que torna um item inacessível;
- restrições novas que impedem código consumidor de compilar.

A cobertura evolui com a ferramenta e com o próprio Rust. Portanto, trate o relatório como uma rede de segurança forte, não como uma prova matemática de compatibilidade total.

## Instalação e primeiro check

Instale o binário usando o próprio Cargo:

```bash
cargo install cargo-semver-checks --locked
cargo semver-checks --version
```

Na raiz de uma biblioteca Rust, execute:

```bash
cargo semver-checks check-release
```

Em um fluxo comum, a ferramenta compara o código atual com uma versão anterior apropriada da crate. Antes de automatizar, confira as opções da versão instalada:

```bash
cargo semver-checks check-release --help
```

Esse passo é importante porque projetos diferentes precisam de baselines diferentes: última versão publicada, tag Git, branch principal ou artefato gerado localmente.

### Teste controlado para entender o relatório

Crie uma biblioteca pequena:

```bash
cargo new --lib calculadora-api
cd calculadora-api
```

Versão inicial:

```rust
pub fn somar(a: i64, b: i64) -> i64 {
    a + b
}
```

Agora imagine que alguém altera a função para:

```rust
pub fn somar(valores: &[i64]) -> i64 {
    valores.iter().sum()
}
```

A implementação nova pode ser melhor para o projeto, mas é incompatível com chamadas existentes como `somar(2, 3)`. Um teste unitário reescrito junto com a função passaria; uma comparação de API deve apontar que consumidores antigos deixam de compilar.

A correção SemVer não precisa ser desistir da nova interface. Você pode preservar a função antiga e adicionar outra:

```rust
pub fn somar(a: i64, b: i64) -> i64 {
    a + b
}

pub fn somar_lista(valores: &[i64]) -> i64 {
    valores.iter().sum()
}
```

Ou pode assumir a quebra, documentá-la e preparar uma versão major.

## Como escolher a baseline correta

A qualidade do resultado depende da referência escolhida. A pergunta não é “o código mudou desde ontem?”, mas sim: **“a versão que pretendo publicar continua compatível com a versão que os usuários instalaram?”**

### Última versão publicada

É a baseline mais natural para crates no crates.io. Ela representa o contrato que consumidores realmente podem estar usando. Esse fluxo é ideal para uma checagem executada antes do release.

### Tag Git do último release

Funciona bem em SDKs privados e bibliotecas que não são publicadas no crates.io. Exige disciplina: cada release precisa de uma tag imutável e fácil de localizar.

### Branch principal

Comparar uma PR com `main` ajuda a impedir que uma quebra entre no repositório. Porém, `main` pode conter alterações ainda não publicadas. Use essa comparação como feedback de revisão, não necessariamente como decisão final do número da próxima versão.

### Baseline local

É útil em ambientes sem acesso à rede ou em pipelines que geram artefatos de API em uma etapa anterior. Também facilita reproduzir o mesmo resultado entre notebooks e CI.

A regra operacional é simples: documente no repositório **qual baseline cada job usa**. Sem isso, dois checks podem estar tecnicamente corretos e responder a perguntas diferentes.

## Integração com GitHub Actions e Gitea Actions

Um job conceitual de CI pode ser enxuto:

```yaml
name: semver

on:
  pull_request:
  push:
    tags: ["v*"]

jobs:
  semver-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Instalar Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Instalar cargo-semver-checks
        run: cargo install cargo-semver-checks --locked

      - name: Verificar compatibilidade da API
        run: cargo semver-checks check-release
```

O mesmo desenho funciona em Gitea Actions quando o runner oferece actions compatíveis. O `fetch-depth: 0` é útil se a estratégia de baseline depende de tags ou do histórico Git.

Em CI de produção, prefira pinar uma versão conhecida da ferramenta ou instalar um binário verificado. Compilar com `cargo install` em todo job é simples, mas aumenta tempo e introduz variação. Para reduzir esse custo, combine cache com [sccache](/blog/sccache-rust-cache-compilacao-ci-2026/) ou use uma imagem de ferramentas já preparada.

### Deve bloquear toda pull request?

Para uma biblioteca pública madura, geralmente sim. Para um projeto em migração, você pode começar com um job informativo e promover para obrigatório depois de revisar os falsos positivos e organizar a baseline.

Uma política prática:

1. **PR comum:** check obrigatório contra a referência definida pelo time;
2. **quebra intencional:** label ou aprovação de mantenedor, acompanhada de plano de versão major;
3. **release:** nova execução contra a última versão publicada;
4. **resultado:** changelog e número da versão devem concordar com a mudança.

A CI não deve permitir um `--ignore` permanente sem justificativa. Exceções precisam ser pequenas, revisáveis e ligadas a uma decisão explícita.

## cargo-semver-checks em workspaces

Workspaces complicam a análise porque nem todo pacote tem o mesmo público:

- uma crate pode ser publicada no crates.io;
- outra pode ser uma biblioteca interna compartilhada;
- uma terceira pode ser apenas um binário;
- algumas crates privadas usam `publish = false`.

Em [workspaces Cargo e monorepos](/blog/cargo-workspaces-monorepos-rust-2026/), defina primeiro quais pacotes possuem contrato de compatibilidade. Depois, execute a verificação para cada crate publicável ou para o conjunto suportado pela configuração adotada.

Exemplo de política:

| Tipo de pacote | Check SemVer | Baseline |
|---|---|---|
| Biblioteca pública | Obrigatório | Última versão publicada |
| SDK interno usado por vários times | Obrigatório | Última tag de release |
| Crate privada de implementação | Opcional | Branch principal |
| Binário sem API Rust pública | Normalmente não | Testes de CLI/configuração |

Não transforme o workspace inteiro em “API pública” por acidente. Isso congela detalhes internos e torna refactors legítimos mais caros.

## Mudanças que parecem inocentes, mas podem quebrar usuários

### Remover um reexport

```rust
pub use modulo_interno::Cliente;
```

Mesmo que `Cliente` continue existindo em outro caminho, remover o reexport quebra imports dos consumidores. Caminhos públicos também são API.

### Adicionar variante a um enum

Adicionar uma variante parece apenas aditivo, mas pode quebrar `match` exaustivo em código externo. A decisão depende do desenho do enum e de atributos como `#[non_exhaustive]`. Planeje extensibilidade antes do primeiro release estável.

### Alterar bounds genéricos

```rust
pub fn processar<T: Read>(entrada: T) { /* ... */ }
```

Trocar para um conjunto mais restritivo de traits pode excluir tipos aceitos anteriormente. Mesmo quando o corpo da função fica melhor, a superfície pública encolhe.

### Remover implementação de trait

Consumidores podem depender de `Clone`, `Send`, `Sync`, `From`, `IntoIterator` ou traits do domínio. Retirar uma implementação pode quebrar código que nunca chama um método da sua crate diretamente.

### Mudar comportamento sem mudar assinatura

```rust
pub fn normalizar(valor: &str) -> String
```

A assinatura pode continuar idêntica enquanto o comportamento muda de “preserva espaços” para “remove espaços”, ou passa a retornar outro formato. Essa quebra **não é necessariamente detectável por análise estrutural**. É por isso que testes de contrato e documentação continuam obrigatórios.

## O que a ferramenta não substitui

`cargo-semver-checks` não substitui:

- **testes unitários e de integração**, cobertos no nosso [guia de testes Rust](/blog/testes-rust-estrategias-boas-praticas-2026/);
- testes de comportamento observável e snapshots bem revisados;
- revisão de segurança e [supply chain](/blog/rust-seguranca-supply-chain-cargo-deny-sbom-2026/);
- documentação de migração;
- changelog e notas de release;
- benchmark quando o contrato inclui performance;
- revisão humana sobre intenção e experiência do usuário.

Uma biblioteca pode passar no check de API e ainda quebrar consumidores ao mudar formato JSON, mensagens, arquivos, protocolo de rede, feature defaults ou requisitos mínimos do compilador (MSRV). SemVer é maior do que a lista de símbolos públicos.

## Fluxo recomendado antes de publicar uma crate

Use esta sequência:

1. rode `cargo fmt --check` e [Clippy](/ecossistema/clippy/);
2. execute testes com `cargo test` ou [cargo-nextest](/blog/cargo-nextest-testes-rust-2026/);
3. gere a documentação com `cargo doc --no-deps`;
4. rode `cargo semver-checks check-release` contra a baseline correta;
5. revise alterações de features, arquivos, formatos e comportamento;
6. atualize changelog e guia de migração;
7. confirme se o incremento será patch, minor ou major;
8. execute `cargo publish --dry-run` antes da publicação real.

Para pipelines mais amplos, conecte esse check ao guia de [CI/CD para projetos Rust](/artigos/ci-cd-rust/) e à estratégia de [release engineering para binários, CLIs e serviços](/blog/rust-release-engineering-binaries-cli-servicos-2026/).

## Checklist de adoção no time

- [ ] crates com contrato público estão identificadas;
- [ ] a política SemVer está documentada no README ou em `CONTRIBUTING.md`;
- [ ] a baseline de PR e a baseline de release estão definidas;
- [ ] `cargo-semver-checks` roda localmente e na CI;
- [ ] tags e versões publicadas são imutáveis;
- [ ] quebra intencional exige aprovação e guia de migração;
- [ ] mudanças comportamentais têm testes próprios;
- [ ] MSRV, features e formatos de dados entram na revisão manual;
- [ ] changelog e número da versão refletem o impacto real.

## Perguntas frequentes

### O que é cargo-semver-checks?

É uma ferramenta do ecossistema Cargo que compara a API pública de duas versões de uma crate e aponta alterações potencialmente incompatíveis com SemVer.

### Como rodar cargo-semver-checks?

Instale com:

```bash
cargo install cargo-semver-checks --locked
```

Depois, na crate:

```bash
cargo semver-checks check-release
```

Consulte `cargo semver-checks check-release --help` para configurar baseline, pacote e cenário do seu repositório.

### Ele decide automaticamente a próxima versão?

Não. O relatório informa possíveis quebras. O mantenedor ainda precisa avaliar o contrato, mudanças comportamentais e a política de versões da crate.

### Funciona para aplicações binárias?

O foco é API Rust de bibliotecas. Para binários, compatibilidade costuma envolver flags de CLI, códigos de saída, arquivos de configuração, endpoints e formatos de dados; esses contratos precisam de testes específicos.

### Posso usar em uma crate 0.x?

Sim, mas documente a política. Antes de `1.0.0`, o componente minor costuma carregar mudanças incompatíveis no ecossistema Cargo. A ferramenta ajuda a identificar a quebra; o time decide como versioná-la e comunicá-la.

## Conclusão

O `cargo-semver-checks` transforma uma pergunta difícil — “esta mudança quebra quem usa minha crate?” — em uma etapa objetiva do pipeline. Ele encontra várias incompatibilidades que passam por testes locais porque o próprio repositório já foi atualizado para a nova API.

A melhor adoção não é instalar a ferramenta e confiar cegamente. É combinar **baseline correta, CI obrigatória, testes de comportamento, changelog e revisão humana**. Assim, o time preserva liberdade para evoluir a biblioteca sem empurrar surpresas para consumidores.

Para continuar, revise o [Cargo e suas ferramentas essenciais](/artigos/cargo-ferramentas-essenciais/), organize o projeto com [workspaces](/blog/cargo-workspaces-monorepos-rust-2026/) e aplique o check no pipeline de [CI/CD Rust](/artigos/ci-cd-rust/). Uma API confiável não é a que nunca muda; é a que muda com contrato, versão e caminho de migração claros.
