Dos conventional commits a um changelog
6 min de leitura atualizado em
Conventional commits dão de graça a um changelog três coisas: o tipo de cada mudança, a parte do sistema que ela tocou, e se quebra algo. Não dão mais nada. Formulação, agrupamento e seleção, que são o changelog, ficam completamente abertos, e um pipeline que finge o contrário entrega um git log formatado.
feat(exports): add CSV column selection
fix(auth): reject expired refresh tokens
chore(deps): bump node-pg to 8.11
Três commits no formato Conventional Commits. A partir desses, uma máquina consegue dizer que um é um recurso, um é uma correção, um é limpeza, e qual parte do sistema cada um tocou. Isso é genuinamente útil, e é toda a promessa da convenção: um histórico de commits que pode ser lido por algo além de uma pessoa. O erro é pensar que isso te dá um changelog. Te dá a matéria-prima.
O que a convenção especifica?
Um tipo, um scope opcional, e uma descrição: type(scope): description. Os tipos são
convencionalmente feat, fix, chore, docs, refactor, test, perf, build, ci. Duas
coisas marcam uma mudança que quebra algo: um ! antes dos dois pontos, ou um footer
BREAKING CHANGE:. As ferramentas se baseiam em feat e fix para incrementos de versão minor e
patch, e no marcador breaking para um major.
| O commit te dá | O changelog precisa de | Quem preenche a lacuna |
|---|---|---|
feat / fix / chore | Added / Fixed / interno | Um mapeamento, automático |
(scope) | Um agrupamento que o leitor reconhece | Uma pessoa, uma vez por scope |
! ou BREAKING CHANGE: | Quem quebra, até quando, e o que fazer | Uma pessoa, toda vez |
| A descrição, escrita para uma revisora | O resultado, escrito para uma cliente | Uma pessoa, cada entrada |
| Um commit | Uma mudança, que pode ser vários commits | Regras de squash, ou uma pessoa |
O marcador conta para a ferramenta; não conta para quem chama, que é o assunto de como depreciar uma API e o que é uma mudança que quebra algo. É uma especificação pequena e vale a pena segui-la mesmo que você nunca gere nada a partir dela, porque força uma decisão por commit: isso é uma mudança que os usuários veem, ou não.
Onde os conventional commits param?
Eles param na frase. Tudo que a convenção captura é metadado sobre uma mudança; a mudança em si ainda é descrita no vocabulário de uma revisora.
Mensagens de commit são escritas para revisoras. fix(auth): reject expired refresh tokens
está correto e não diz nada a uma cliente. A leitora de um changelog quer “você será deslogado
quando uma sessão realmente tiver expirado, em vez de ver 401 intermitentes”.
Scopes são internos. exports, auth, ingest são nomes de módulos. São estáveis, o que os
torna bons para agrupar, e sem significado para quem está fora da base de código.
Uma mudança geralmente é vários commits. Um recurso mergeado em onze commits produz onze entradas, dez das quais ruído, e esmagá-las para esconder isso perde o histórico de revisão.
chore é uma gaveta, não uma categoria. Atualizações de dependências, mudanças de CI e
renomeações caem todas lá, e algumas importam para usuários enquanto a maioria não.
Então: a convenção te dá tipo, scope e status de breaking de graça, e deixa formulação, agrupamento e seleção completamente abertos. Esses três são o changelog. Quem é realmente responsável por uma entrada de changelog cobre quem deveria cuidar dessa formulação, agrupamento e seleção, já que a convenção em si não tem opinião sobre isso.
Como se gera um changelog a partir de conventional commits?
Em duas camadas, e a segunda tem que ser obrigatória.
Camada um, automática. No merge, derive uma entrada rascunho do commit: tipo mapeado para um
tipo de changelog (feat para Added, fix para Fixed, um marcador breaking para Changed mais uma
flag), scope mantido como metadado em vez de texto, link para a PR. Coloque-a na seção Unreleased
que o Keep a Changelog pede.
Camada dois, humana, e obrigatória. Antes que uma release saia, cada entrada rascunho recebe ou uma reescrita de uma linha no vocabulário do usuário, ou é marcada como interna e removida da visão pública. Este é o passo que as pessoas tentam pular, e pular é o que produz changelogs que se leem como um diff.
O detalhe importante de design é que a camada dois não é opcional no pipeline. Se uma release pode ser cortada com rascunhos não editados, ela será, na semana em que todo mundo está ocupado. Quais etapas pertencem à máquina e quais à pessoa é todo o assunto de automação de changelog.
Cortar o lançamento também é o momento em que uma tag do git, um lançamento e essa entrada de changelog se encaixam ou começam a sair de sincronia; tags do git, lançamentos e o seu changelog cobre como manter os três sincronizados.
Três armadilhas
Squash merges comem os footers. Se sua plataforma esmaga com o título da PR como mensagem, o
footer BREAKING CHANGE: de um commit dentro daquela branch desaparece, e sua ferramenta para de
ver a mudança que quebra algo silenciosamente. Verifique o que seu template de squash realmente
mantém.
Commits de revert produzem entradas fantasmas. Um fix que é revertido no dia seguinte gera
uma entrada para algo que nunca foi lançado, a menos que a derivação reconcilie reverts. A maioria
das ferramentas não faz isso.
O incremento de versão e o changelog saem de sincronia. Se a versão é calculada a partir de commits e o changelog é escrito manualmente depois, eles divergem em cerca de duas releases. Calcule ambos na mesma passagem ou aceite que um dos dois está errado.
Se você quer a parte mecânica sem um pipeline
Nosso gerador de changelog faz a etapa de derivação no navegador: cole commits, receba entradas agrupadas e tipadas. É deliberadamente determinístico e inteiramente do lado do cliente, então os commits que você cola nunca saem da sua máquina, o que importa quando as mensagens vêm de um repositório privado. Faz a metade de coleta honestamente e não tenta a camada dois, porque a camada dois é um julgamento e uma ferramenta que a finge produz exatamente o changelog contra o qual este artigo argumenta.
Para a versão de pipeline, ferramentas de changelog cobre o que existe.
O resumo
Conventional commits respondem “que tipo de mudança é esta” de forma confiável e barata. Não respondem “o que devemos dizer às pessoas”, e nenhuma quantidade de ferramentas em cima da mensagem de commit vai fazer isso, porque a informação nunca esteve na mensagem de commit. Reserve um orçamento para a reescrita.
FAQ
Conventional commits geram um changelog automaticamente? Eles geram um rascunho automaticamente: entradas tipadas, com scope, linkadas. A formulação para uma cliente, o agrupamento e a decisão do que deixar de fora ainda precisam de uma pessoa, e um pipeline que pula essa etapa publica mensagens de commit.
Quais tipos de conventional commit aparecem em um changelog?
feat e fix sempre, como Added e Fixed. perf geralmente, como Changed. chore, docs,
refactor, test, build e ci são internos por padrão e só aparecem se uma pessoa promover
um.
Como conventional commits marcam uma mudança que quebra algo?
Um ! depois do tipo ou scope (feat(api)!: ...), ou um footer BREAKING CHANGE: no corpo do
commit. Ambos se perdem se um squash merge mantiver só o título da PR.
Você precisa de conventional commits para automatizar um changelog? Não. Labels de PR, templates de PR e links de issue carregam os mesmos metadados para equipes que fazem merge por pull request. Conventional commits são a opção mais barata quando a unidade de mudança é o commit.
As afirmações técnicas deste artigo não foram revisadas de forma independente. Se algo estiver errado, avise a gente e vamos corrigir.