Pular para o conteúdo principal

Semantic Commit

· 6 min para ler
Leandro Andrade
Leandro Andrade
Software Developer

Mensagens de commit são a documentação viva de um repositório. Adotar uma convenção semântica transforma o histórico do git em algo legível por pessoas e por máquinas.

O que é um commit semântico​

Semantic Commit (ou Conventional Commits) é uma convenção que padroniza a mensagem de commit em uma estrutura previsível. Em vez de mensagens genéricas como ajustes ou correções finais, cada registro passa a declarar o que mudou e qual o impacto da mudança.

A estrutura básica é:

<tipo>[escopo opcional]: <descrição>

[corpo opcional]

[rodapé opcional]

Um exemplo completo:

feat(auth): adiciona login com JWT

Substitui a autenticação por sessão em memória pela emissão de
tokens JWT com expiração de 15 minutos e refresh token.

Refs: #142
Regra de ouro

A descrição deve completar a frase: "Se aplicado, este commit irá...". Use o imperativo (adiciona, corrige, remove) e evite o passado (adicionado, corrigido).

Tipos de commit​

A tabela a seguir resume os tipos mais utilizados no dia a dia:

TipoPrefixoUsoExemplo
FuncionalidadefeatAdição de nova funcionalidadefeat(auth): adiciona login com JWT
CorreçãofixCorreção de bugfix(api): corrige erro 500 ao buscar usuários
RefatoraçãorefactorAlteração interna sem mudar comportamentorefactor(service): simplifica lógica de pagamento
ManutençãochoreTarefas gerais (build, configurações, dependências)chore: atualiza dependências do projeto
DocumentaçãodocsAlterações em documentaçãodocs: atualiza README com instruções de uso
EstilostyleFormatação, lint, espaços (sem impacto funcional)style: ajusta formatação com prettier
TestestestCriação ou ajuste de testestest(auth): adiciona testes para login
Breaking changefeat!Mudança que quebra compatibilidadefeat!: altera estrutura de resposta da API

Além desses, dois tipos costumam aparecer em projetos com integração contínua:

  • perf: melhoria de desempenho sem alteração de comportamento;
  • ci: mudanças em pipelines e automações de build.
Atenção ao refactor

Se o comportamento observável mudou, não é refactor. Um refactor que altera a resposta de um endpoint deveria ser feat ou fix — classificar errado quebra a rastreabilidade e induz o versionamento automático ao erro.

Escopo​

O escopo é opcional e indica a área afetada pela mudança: um módulo, um pacote em um monorepo ou um domínio de negócio.

git commit -m "fix(orders): valida quantidade negativa no carrinho"

Escolha escopos estáveis e reutilizáveis (auth, orders, billing). Escopos criados livremente a cada commit perdem a função de agrupar mudanças.

Breaking changes​

Existem duas formas equivalentes de sinalizar uma quebra de compatibilidade:

feat!: altera estrutura de resposta da API

ou, de forma mais descritiva, no rodapé:

feat(api): altera estrutura de resposta

BREAKING CHANGE: o campo `data` passa a ser um objeto e não mais
um array. Clientes que iteram sobre a resposta precisam ser ajustados.
Impacto em contratos

Em arquiteturas distribuídas, uma breaking change em um serviço propaga-se para todos os consumidores. Antes de marcar !, avalie se é possível evoluir o contrato de forma aditiva — mantendo o campo antigo por um período de transição — em vez de quebrá-lo.

Por que isso importa na arquitetura​

Padronizar o commit não é preciosismo estético. O histórico passa a ser uma fonte de dados confiável para automações.

Versionamento semântico​

A convenção conversa diretamente com o Semantic Versioning (MAJOR.MINOR.PATCH):

Tipo de commitIncrementoExemplo
fixPATCH1.4.2 → 1.4.3
featMINOR1.4.2 → 1.5.0
BREAKING CHANGEMAJOR1.4.2 → 2.0.0
chore, docs, stylenenhum1.4.2 → 1.4.2

Ferramentas como semantic-release e standard-version leem o histórico e decidem a próxima versão sem intervenção manual, eliminando a discussão recorrente sobre "qual número subir".

Changelog automático​

Com tipos e escopos consistentes, o CHANGELOG.md deixa de ser escrito à mão:

npx conventional-changelog -p angular -i CHANGELOG.md -s

Revisão e rastreabilidade​

Em uma pull request com dezenas de arquivos, o histórico semântico permite responder rapidamente: quais mudanças são funcionais, quais são cosméticas e quais alteram contratos. Isso reduz o custo de revisão e acelera a investigação de incidentes — um git log --oneline --grep "^fix(orders)" mostra o histórico de correções de um domínio específico.

Aplicando no projeto​

Validando localmente​

O commitlint valida a mensagem antes que o commit seja criado:

npm i -D @commitlint/cli @commitlint/config-conventional husky
npx husky init
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg

Com a configuração:

commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional']
};

Uma mensagem fora do padrão é rejeitada:

$ git commit -m "ajustes"
⧗ input: ajustes
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]

Validando na integração contínua​

O hook local pode ser ignorado com --no-verify, por isso a validação deve ser repetida no pipeline:

.github/workflows/commitlint.yml
name: commitlint
on: [pull_request]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npx commitlint --from origin/main --to HEAD --verbose
Squash merge

Em times que usam squash merge, o que vai para a main é o título da pull request, não os commits individuais. Nesse caso, valide o título da PR — caso contrário, o histórico da branch principal continuará fora do padrão mesmo com o commitlint configurado.

Boas práticas​

  • Um commit, uma intenção: se a mensagem precisa de "e" para descrever a mudança, provavelmente são dois commits;
  • Descrição curta: até 72 caracteres na primeira linha, sem ponto final;
  • Corpo para o "porquê": o diff já mostra o "como"; use o corpo para registrar o contexto e as alternativas descartadas;
  • Rodapé para referências: Refs: #142, Closes: #87, Co-authored-by:;
  • Idioma único: escolha português ou inglês e mantenha a decisão em todo o repositório.
Evite

Mensagens como wip, ajustes, correções finais ou funcionando agora não sobrevivem a um git bisect daqui a seis meses. Se o trabalho está incompleto, use git commit --amend ou um rebase interativo antes de abrir a pull request.

Conclusão​

O commit semântico é uma convenção de baixo custo e retorno alto: exige apenas disciplina na escrita da mensagem, mas habilita versionamento automático, geração de changelog, revisões mais rápidas e um histórico que sustenta a investigação de incidentes.

O ganho real aparece quando a convenção deixa de ser um acordo verbal e passa a ser verificada por ferramentas — commitlint no ambiente local e no pipeline de integração contínua. A partir daí, o histórico do git deixa de ser um registro burocrático e se torna parte da documentação e da automação do projeto.