- Conventional Commits tenta dar significado às mensagens de commit com o formato
<type>[optional scope]: <description>, mas coloca o tipo de mudança na frente e deixa o escopo como opcional, empurrando para trás a informação realmente necessária para navegação
- Colaboradores, debuggers e equipes de resposta a incidentes procuram no log de commits a área do código afetada pela mudança, e como bugs podem surgir em qualquer tipo de alteração, o escopo (scope) é mais importante que o tipo
- Em
fix(compiler): prevent namespaced SVG <style> elements from being stripped, só a descrição já indica que se trata de uma correção de bug, e em refactor(core): Update webmcp support to use document.modelContext, um único commit pode abranger correção, refatoração e adição de funcionalidade, então o type é redundante e limitado
- A geração automática de CHANGELOG e a decisão de incremento de versão semântica têm leitores diferentes para log de commits e changelog, e os resultados podem divergir por causa de reversões, quebras acidentais de compatibilidade e correções posteriores dessas quebras
- Mensagens de commit com prefixo de escopo mostram primeiro o alvo da mudança, e condições de build e deploy também funcionam melhor quando se baseiam nos arquivos alterados via
git diff, e não no tipo do título
Prioridades erradas
- Conventional Commits tem como objetivo dar significado às mensagens de commit para ajudar desenvolvedores e usuários finais a entender as mudanças
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
- A linha de título é composta por um
<type> como fix, feat, chore, docs, refactor, um scope opcional e uma descrição
- O defeito central é a estrutura priorizar o type, isto é, a natureza da mudança, em vez do scope, que é o alvo da mudança
- Uma estrutura em que o scope é opcional permite que a informação mais importante do commit fique ausente, e colocar o type no começo do título inverte a prioridade
Por que scope é mais importante que type
- Colaboradores leem o log de commits para encontrar mudanças desde a última contribuição, o fluxo geral do projeto e commits que podem entrar em conflito com o trabalho em andamento durante um pull ou rebase
- Quem está fazendo debug procura mudanças que mexeram na área relacionada ao componente onde o bug apareceu, e como bugs podem surgir em alterações de qualquer type, essa informação não ajuda
- Equipes de resposta a incidentes vasculham o log de commits perto do momento da falha para encontrar a área que causou o problema; se houver um commit com scope
auth no ponto em que erros na API de entrada aumentaram, ele vira um forte candidato à causa
- Para quem lê o log de commits, o importante não é que tipo de mudança foi feita, mas em que área ela mexeu
A redundância e os limites do type
Os limites da promessa de automação
- A ideia de gerar automaticamente um CHANGELOG a partir dos commits com ferramentas como git-cliff ou conventional-changelog esbarra no fato de que o log de commits e o changelog têm leitores diferentes
- O CHANGELOG é voltado para usuários e foca em entender diferenças funcionais e de negócio entre versões
- O log de commits é voltado para desenvolvedores e foca em entender como a base de código mudou ao longo do tempo e em acompanhar esse fluxo pela ótica do scope
- Em projetos com complexidade moderada ou maior, uma funcionalidade relevante costuma entrar por vários commits; para desenvolvedores, o processo de implementação é útil, mas para usuários finais importa apenas a funcionalidade pronta
- Commits de reversão (
revert) são importantes para desenvolvedores no fluxo do log, mas para o usuário final uma mudança revertida equivale a uma mudança que nunca foi feita
- Incrementar versão semântica com base no type do commit pode gerar problemas como subir major mesmo quando a quebra de compatibilidade foi revertida, subir minor/patch por engano porque a quebra só foi percebida depois, ou marcar como quebra algo que desapareceu ao ser combinado com commits posteriores
- Nessas situações, até dá para corrigir o histórico com rebase, mas o workflow pode impedir isso ou torná-lo frágil, além de reduzir a confiabilidade do fluxo transmitido pelo log de commits
- Se o processo de build ou deploy for disparado pelo type no título do commit, uma mensagem como
docs: fix typos pode driblar a automação mesmo inserindo uma vulnerabilidade no subsistema de autenticação
- É melhor definir condições de build e deploy identificando os arquivos alterados com
git diff do que usando o título do commit
Problemas de adoção e alternativas
- Conventional Commits incentiva cada projeto a definir seu próprio conjunto de types, mas muitos projetos simplesmente adotam os types padrão do commitlint, que podem não combinar bem com as características específicas de cada projeto
- A especificação de Conventional Commits tecnicamente define apenas
fix e feat; os demais types ficam a cargo de cada projeto
- Em ambientes corporativos, exigências de gestão de mudanças e auditoria às vezes obrigam a incluir o número do ticket em toda mensagem de commit; se o
<scope> for usado para isso, perde-se um metadado realmente útil
- Linux, FreeBSD, Git, Go, NixOS e Node.js usam mensagens de commit com prefixo de scope adaptado ao projeto
- No kernel Linux, o subsystem é um scope natural; no projeto Go, é o caminho do pacote; em arquiteturas de microsserviços, tende a ser o nome do microsserviço
- scopedcommits.com propõe voltar a um formato de mensagem de commit centrado em scope e separar a geração de CHANGELOG da gestão do log de commits
- As vantagens de Conventional Commits não se converteram em benefícios reais, e sua popularidade em projetos open source, somada à tendência de AIs de adotá-lo como padrão, está espalhando mensagens de commit com antipadrões misturados
1 comentários
Opiniões no Lobste.rs
Fico feliz de ver um texto que organiza de forma lógica uma crítica aos conventional commits, e não só como uma rejeição instintiva
Eu não tinha pensado muito a fundo sobre por que não gosto disso, e achei que talvez fosse por eu ter passado a associar com código gerado por LLM. Especialmente
chore:é o que eu mais detesto; espero que não reinventem a notação húngara. Isso nem deveria ter sido criado em primeiro lugarchore:nem existe mais no guia de estilo de commits do Angular e, talvez por terem percebido o quão vago era, acabou sendo absorvido porbuild:Mesmo na época em que existia no estilo do Angular, a descrição de
chore:sugeria usos bem específicos, mas em alguns projetos open source parece que colocam isso no clima, literalmente para tarefas que soam como coisas que deu preguiça de fazerEu não gosto de conventional commits, mas acho que a alternativa proposta deixa passar o motivo de o scope ser opcional
Em projetos pequenos, sem muitos módulos bem definidos, o conceito de “scope” não é muito útil. Uma prática útil que ambos deixam de fora é colocar números de issue ou ticket no título do commit, porque isso facilita entender o contexto adicional da mudança e ajuda especialmente na revisão de código. Dito isso, eu não gosto da ideia de tornar número de ticket obrigatório, porque isso só faz surgir tickets inúteis para mudanças triviais; mas, se a mudança trata de um bug ou tarefa específica, ela deveria estar ligada a esse bug ou tarefa
Ainda é melhor do que um “type” de commit redundante, que deveria ficar evidente só de olhar a linha de título
Se a mudança corresponde claramente a um ticket, use um commit com “número de ticket”; se não, use outro formato. Algumas mudanças se encaixam melhor no type do que no scope, e outras no contrário, então também dá para misturar scoped commits com conventional commits
Dá vontade de dizer: “não usem fonte monoespaçada em texto corrido”
Ainda assim, em geral concordo com a premissa do texto
Mesmo que as mensagens de commit não sejam grandes coisas, para ter noção do escopo das mudanças eu recomendo usar com frequência
git log --name-onlyougit log --statVer os nomes dos arquivos ajuda bastante a entender o que mudou sem precisar abrir cada commit individualmente
O formato de que eu realmente gosto é exigir estilo de conventional commit no título do PR
O título do PR pode ser editado pelo maintainer mesmo depois do merge, não exige reescrever o histórico de commits e, junto com ferramentas como release-drafter, permite automatizar changelogs significativos nas releases do GitHub. Isso oferece a granularidade apropriada para os stakeholders mencionados pelo autor — separando funcionalidade, correção e breaking changes — e também automatiza um semver razoável para o próximo rascunho de release no GitHub
A crítica do texto de que componentes como
parse-libnão deveriam ser opcionais está correta, e também concordo que forçar conventional commits desencoraja novas contribuições. Mas as alternativas também não parecem exatamente melhoresAinda assim, um identificador de breaking change como
fix!(parse-lib): Don't leave sparse holes when parsing JSON arraystraz bastante informação. Mostra que é uma correção de bug em um componente específico, que a correção inevitavelmente veio acompanhada de uma breaking change e carrega um significado como um incremento minor de semver. Isso dá para usar no título do PRAdmito que me empolguei demais com conventional commits como forma de incentivar disciplina nos commits, e no fim isso virou hábito
Hoje, às vezes, me parece limitador e arbitrário. Em alguns projetos, sem eu nem saber se aquilo era a convenção real, acabei me aproximando mais do estilo Linux/Go/Node; e, em monorepos com várias configurações, parecia mais natural escrever
[service]: [what changed]do que inventar um type à força. Daqui para frente, pretendo experimentar mais com meu estilo pessoal de commits com base no que parece útil, em vez de tentar me encaixar numa convenção rígida, e scoped commits parecem um bom ponto de partidachore(lobsters): add my 2 cents on conventionals commits [JIRA-69420]Concordo com quase tudo, mas vejo de forma diferente a parte sobre “mostrar aos contribuidores um registro revisionista que reduz a confiabilidade da história contada pelo log de commits”. O autor parece estar falando principalmente de branches públicas, e nesse caso é um conselho razoável. Mas isso não deveria valer para branches privadas. Basta tornar o resultado final fácil de entender para quem vai revisar a mudança — ou seja, o maintainer ou eu mesmo daqui a 10 anos —, sem necessidade de deixar um fluxo de pensamento inconsistente ou, pior ainda, uma pilha de commits
address reviewA resposta para “por que scope é opcional?” é que, em projetos pequenos, simplesmente o projeto inteiro é o scope
Concordo que o “type” do commit não é tão útil assim, mas também não vejo muita diferença entre scoped commits e conventional commits. Scoped seria só um conventional sem o “type”, e a distinção entre fix·feat·refactor·chore também é aceitável
Se todo mundo só pega o padrão do commitlint e usa como está, talvez a questão não seja simplesmente fazer as pessoas lidarem melhor com isso?