Semantic Commit
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
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:
| Tipo | Prefixo | Uso | Exemplo |
|---|---|---|---|
| Funcionalidade | feat | Adição de nova funcionalidade | feat(auth): adiciona login com JWT |
| Correção | fix | Correção de bug | fix(api): corrige erro 500 ao buscar usuários |
| Refatoração | refactor | Alteração interna sem mudar comportamento | refactor(service): simplifica lógica de pagamento |
| Manutenção | chore | Tarefas gerais (build, configurações, dependências) | chore: atualiza dependências do projeto |
| Documentação | docs | Alterações em documentação | docs: atualiza README com instruções de uso |
| Estilo | style | Formatação, lint, espaços (sem impacto funcional) | style: ajusta formatação com prettier |
| Testes | test | Criação ou ajuste de testes | test(auth): adiciona testes para login |
| Breaking change | feat! | Mudança que quebra compatibilidade | feat!: 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.
refactorSe 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.
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 commit | Incremento | Exemplo |
|---|---|---|
fix | PATCH | 1.4.2 → 1.4.3 |
feat | MINOR | 1.4.2 → 1.5.0 |
BREAKING CHANGE | MAJOR | 1.4.2 → 2.0.0 |
chore, docs, style | nenhum | 1.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:
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:
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
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.
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.
