Engenharia

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 deQuem preenche a lacuna
feat / fix / choreAdded / Fixed / internoUm mapeamento, automático
(scope)Um agrupamento que o leitor reconheceUma pessoa, uma vez por scope
! ou BREAKING CHANGE:Quem quebra, até quando, e o que fazerUma pessoa, toda vez
A descrição, escrita para uma revisoraO resultado, escrito para uma clienteUma pessoa, cada entrada
Um commitUma mudança, que pode ser vários commitsRegras 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.

Relacionado na changeloop: Gerador de changelog, Comparativo de ferramentas de changelog

changeloop
O time por trás de um changelog que fecha o loop. Os usuários pedem algo, sua equipe entrega, quem pediu fica sabendo.