tldr: As páginas a seguir são bem concisas em explicar a utilização do padrão que vou explicar a seguir. Referência para convencional commits, e documentação do Karma.

Provavelmente você já presenciou ou presenciará esse cenário durante sua carreira:

Você acaba de chegar no escritório e verificar suas tasks, escolhe um número ímpar e decide por ‘corrigir erro no perfil do usuário. Prioridade: média+’. Abre seu editor e vai até o trecho do perfil, sem documentação e sem possibilidade de teste, o blame levanta a mão e acusa sem dó que o último commit foi feito há 1 mês às 01:37 da manhã de uma sexta-feira com a mensagem ‘Small fix’. Você pára por um segundo, e suspira — ‘Hum, o-o-kay, deveria estar cansado…’ Você, então, verifica os commits antecedentes, bem como suas mensagens, e descobre uma variação entre ‘fix error’ a ‘fix tons of shit’ em diversas horas do dia. Nesse momento, você começa a inspirar, conseguindo puxar mais de dois litros de ar e sem soltar nada, mas segue firme. Decide, então, abrir commit por commit para verificar as alterações durante o tempo, fica 45 minutos - no mínimo - olhando para um mesmo trecho de código e fazendo alusões históricas em xingamentos internos. As pessoas ao seu lado não te reconhecem mais, você muito menos e assim decide por amaldiçoar o mundo por ter escolhido ímpar.

Apesar de bastante hiperbólica, isso acontece com uma certa frequência no meio de desenvolvedores. É bastante claro também que podemos solucionar os problemas aqui citados com diversas iniciativas, mas para fazer jus ao título e à história (obviamente) vou sugerir começarmos pela padronização de commits utilizando commits semânticos.

— Mas que diabos são commits semânticos?
Commit semântico ou conventional commit, em sua especificação formal, é uma das formas que a gente pode fazer padronização de commits dentro do nosso projeto utilizando de regras bem simples, que apesar de introduzirem um pouco de overhead, ele é ainda menor quando utilizando ferramentas como commitzen e git blame no seu editor. Essa padronização vai contribuir para que reduzamos o tempo gasto em compreender como e por que algo foi feito apenas olhando pelo histórico do git como ilustrado na historinha acima.

— E como ela se parece?
Você vai conseguir identificar um commit semântico de primeira porque ele é bastante particular e ele tem o seguinte formato:

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

— Sim, e na prática?
Bom, ele tem essa aparência:

fix(containers/profile): adjust argument of  getThumbnailImage function

getThumbnailImage used to receive argument of type XPTO.
Now receives the correct argument of type FOO.


Solves issue #132

— Tá, tudo bem, mas afinal: quais as convenções que vamos utilizar?
Vamos utilizar apenas quatro regras e elas são as seguintes:

  • Uso de commits atômicos;
  • Cada linha da mensagem do seu commit terá no máximo 100 caracteres;
  • A compreensão do conteúdo da tarefa deve ser quase que automática para qualquer contribuidor e de fácil compreensão para novos contribuidores;
  • Os tipos, escopos, descrições, conteúdo do corpo e rodapé devem ser devidamente declarados.

Não tão claro? Então vamos às explicações…

Nossas tarefas agora serão divididas em átomos, unidades bem simples que implementam uma única funcionalidade (uma micro-tarefa) ou corrigem um erro específico. E nossos commits são uma instância dessas tarefas, uma representação simplificada do que aquela tarefa representa.

Agora, vamos conhecer as definições dessa padronização. Voltando ao esqueleto, temos o seguinte:

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

Os Tipos se resumem em feat, fix, refactor, style, chore, doc e test

  • feat são quaisquer adições ao código. Enquanto elas podem alterar parte do código já existente, o foco dela é a implementação de features novas ao ecossistema.
  • fix refere-se às correções de bugs. Caso seu time trabalhe com issues ou com Jira, é possível com smart commits associar seu commit a uma issue e alterar seu estado com keywords como resolve, fix, solves. Em geral, essas marcações devem vir na descrição ou no footer.
  • refactor refere-se a quaisquer mudanças que atinjam o código, porém não alterem sua funcionalidade. Alterou o formato de como é processamento em determinada parte da sua tela, mas manteve a mesma funcionalidade? Declare como refactor.
  • Alterações referentes a formatações de código, semicolons, trailing spaces e lint em geral são em style.
  • Em uma das equipes com que trabalho, decidimos por criar novos dois tipos e ignorar o tipo chore porque ele não passava a ideia que precisávamos e passamos a utilizar outros dois: env e project. Vale notar que esses dois tipos fogem das especificações do conventional commit. Em project, todo o escopo de versões e pacotes ficam inseridos nele, por exemplo:
project: bump version to 0.0.7
project(package): bump react-navigation version to beta.19
project(package): add native-base@2.2.1

Já o env, utilizamos para as funções restantes do antigo chore, como atualizações de arquivos de CI, docker, build files, tasks runners ou configurações, por exemplo:

env(docker): add a node script to  DockerFile
  • Com doc, temos conteúdo relativo à documentação.
  • Commits com tipo test estão relacionados às modificações e adições aos testes.

Escopos podem ser quaisquer partes do projeto; é importante que eles sejam compreendidos de uma maneira quase automática para alguém que não conhece o projeto. Em geral, a utilização do escopo é bem genérica, especificando apenas o primeiro contexto (login, middleware, profile). No entanto, prefiro ser mais específico e definir um segundo escopo (containers/login, por exemplo). Supondo que você tenha feito uma refatoração nas rotas relativas as settings do seu projeto, uma possibilidade de commit seria:

feat(routes/settings): adjust settings to be called in any screen.

As descrições devem ser suficientemente claras, utilizando seu espaço até o máximo permitido da linha. Caso você veja que a explicação não foi suficiente, sinta-se à vontade para adicionar conteúdo ao corpo.

O corpo, por sua vez, vai conter descrições mais precisas do que está contido naquele commit, mostrando as razões ou consequências geradas por esse código, assim como instruções futuras.

O rodapé restringe-se às alterações de estado via smart commit, como resoluções de estado de issues, e.g. ‘resolves issue #312’.

E se existir uma mudança drástica que quebrará funcionalidades, você DEVE (sim, em caixa alta) marcar no body com ‘BREAKING CHANGE’ (sim, em caixa alta) e explicar as razões que levaram a essa marcação.

Pronto!

Isso é suficiente para você para utilizar os commits semânticos. E o melhor de tudo? Você pode começar isso sozinho na sua equipe, e ir aos poucos incentivando o uso dessa padronização.

— Mas pera! Vem cá, quem usa?
Os repositórios do angular, yarn, e karma são exemplos de equipes que adicionaram em seu código de conduta a utilização de commits semânticos.

— Tem mais alguma vantagem?
Possibilita visualização de maneira rápida em blames dentro do código, assim como acesso a lista de logs de um trecho em particular de código de maneira legível e compreensível.

Se sua equipe trabalha em formato de sprints ou talvez utiliza issues como Jira, é de grande valia conseguir gerar um changelog limpo, formatado com listagem de correções de bugs, breaking changes e features novas tanto para o cliente quanto para a equipe de projeto e desenvolvimento.

Vale lembrar que BREAKING CHANGE, feat e fix se correlacionam com o versionamento semântico, respectivamente com MAJOR, MINOR e PATCH, facilitando a utilização de semantic releases de pacotes que sua equipe mantém.

Então, que tal dar uma relida e absorver um pouquinho mais disso e agregar mais informação nas suas ações como desenvolvedor? E conforme a grande Clarice disse: ‘vale a pena, e mesmo que doa, dói apenas no início’.

No próximo artigo, mostrarei ferramentas que vão negar qualquer overhead que você ache essa padronização pode gerar, com a utilização git blame e commitzen. Assim como ignorar commits que não entram no padrão com git hooks utilizando husky.