Conventional Commits Pattern

O que é, para que serve e como utilizamos dentro da LinkApi

Victor Ribeiro
LinkApi Solutions
Published in
8 min readOct 19, 2020

--

Antes de tudo, sabemos como é intrínseco para nós Devs o uso do git durante o desenvolvimento. Não só em projetos pessoais, de código aberto ou empresariais, mas em todo projeto que seja fomentado por várias pessoas ou comunidades.

Dado isso, é importante utilizarmos o git commit apropriadamente. Dispor de uma linguagem coerente e padronizada ajuda a todos os envolvidos no projeto a entenderem as mudanças ocorridas e quais contextos foram afetados.

Exemplo de péssimo padrão de mensagens de commit (xkcd)

Na imagem acima, percebemos o quão prejudicial — a curto prazo — pode ser commits mal comentados, uma vez que não conseguimos entender a natureza da mudança ocorrida e o contexto que se aplica. Entretanto, a longo prazo, o efeito é ainda mais danoso, dado que a manutenibilidade do software é prejudicada devido a incoerência no escopo dessas mudanças, e como afetaram o projeto no passado.

Dev ao ver commit de 3 semanas que está dando problema e escrito “some changes”

Os commits são pontos na linha do tempo de um projeto. Quando documentados propriamente nos mostram quem alterou, quando, em qual contexto e qual tipo de alteração foi feita. Diante disso, vamos conhecer o Conventional Commits Pattern.

Conventional Commits: Uma especificação para dar um significado legível às mensagens de commit para humanos e máquinas. Disponível em: <link>

O que é ?

O Conventional Commits é uma convenção simples de mensagens de commit, que segue um conjunto de regras e que ajuda os projetos a terem um histórico de commit explícito e bem estruturado.

Há vários benefícios (além dos já citados anteriormente) em utilizar esse tipo de convenção, como por exemplo, poder automatizar a criação de CHNGELOGs, facilitar a entrada de novos Devs no projeto, assim como poder gerar relatórios e conseguir entender onde está se concentrando as horas do projeto (em refatoração de código, criação de features, mudança de estilos, ambiente de desenvolvimento, entre outros).

Há também quem possa estar como Michael Scott pensando algo como: “Adotando essa prática, vamos perder tempo seguindo o padrão e desestimular o tempo de mudanças rápidas no projeto”

A verdade é que adotando essa convenção nós desestimulamos a mudança desordenada, e assim, como um código bem estruturado e organizado, nós perdemos algum tempo pontualmente, mas temos um enorme ganho de tempo a longo prazo.

Como utilizar

As regras são muito simples, como demonstrado abaixo temos um tipo de commit (type), o escopo/contexto do commit (scope) e o assunto/mensagem do commit (subject), mas adiante irei detalhar cada um.

!type(?scope): !subject
<?body>
<?footer>

Dessa maneira, ! indica os atributos obrigatórios e ? indica os atributos não obrigatórios. Nesse artigo não iremos falar sobre o body e nem o footer do commit. Mas tratam-se de especificações simples, que vocês podem ver mais aqui.

Subject: Imperativo ao invés de pretérito (modo passado)

O primeiro exemplo com o subject no pretérito e o segundo com o subject no imperativo

Sei que pode parecer estranho ao primeiro momento escrever a mensagem no imperativo, pois a mudança implementada foi uma ação passada, mas escrevendo subjects utilizando o imperativo nós estamos dizendo à nossa equipe o que fará o commit se aplicado. No artigo de Chris Beams ele diz um pouco mais sobre a escolha do imperativo e traz uma grande notação, ao qual, todo subject deve se enquadrar:

“If applied, this commit will <message>”

Em português: “Se aplicado, esse commit irá <mensagem>”. Se pensarmos no exemplo representado acima, o resultado seria:

If applied, this commit will change the markup”, o que faz muito mais sentido do que: “If applied, this commit will changed the markup”

Type: Quais são os tipos de commit

O type é responsável por nos dizer qual o tipo de alteração ou iteração está sendo feita, das regras da convenção, temos os seguintes tipos:

  • test: indica qualquer tipo de criação ou alteração de códigos de teste. Exemplo: Criação de testes unitários.
  • feat: indica o desenvolvimento de uma nova feature ao projeto. Exemplo: Acréscimo de um serviço, funcionalidade, endpoint, etc.
  • refactor: usado quando houver uma refatoração de código que não tenha qualquer tipo de impacto na lógica/regras de negócio do sistema. Exemplo: Mudanças de código após um code review
  • style: empregado quando há mudanças de formatação e estilo do código que não alteram o sistema de nenhuma forma.
    Exemplo: Mudar o style-guide, mudar de convenção lint, arrumar indentações, remover espaços em brancos, remover comentários, etc..
  • fix: utilizado quando há correção de erros que estão gerando bugs no sistema.
    Exemplo: Aplicar tratativa para uma função que não está tendo o comportamento esperado e retornando erro.
  • chore: indica mudanças no projeto que não afetem o sistema ou arquivos de testes. São mudanças de desenvolvimento.
    Exemplo: Mudar regras do eslint, adicionar prettier, adicionar mais extensões de arquivos ao .gitignore
  • docs: usado quando há mudanças na documentação do projeto.
    Exemplo: adicionar informações na documentação da API, mudar o README, etc.
  • build: utilizada para indicar mudanças que afetam o processo de build do projeto ou dependências externas.
    Exemplo: Gulp, adicionar/remover dependências do npm, etc.
  • perf: indica uma alteração que melhorou a performance do sistema.
    Exemplo: alterar ForEach por while, melhorar a query ao banco, etc.
  • ci: utilizada para mudanças nos arquivos de configuração de CI.
    Exemplo: Circle, Travis, BrowserStack, etc.
  • revert: indica a reverão de um commit anterior.
Exemplos dos tipos de commit citados anteriormente

Assim, conseguimos de forma simples e direta ver qual tipo de mudança está ocorrendo, melhorando bastante a visibilidade e alinhamento com a equipe.

Observações:

  • Só pode ser utilizado um type por commit;
  • O type é obrigatório;
  • Caso esteja indeciso sobre qual type usar, provavelmente trata-se de uma grande mudança e é possível separar esse commit em dois ou mais commits;
  • A diferença entre build e chore pode ser um tanto quanto sutil e pode gerar confusão, por isso devemos ficar atentos quanto ao tipo correto. No caso do Node.js por exemplo, podemos pensar que quando há uma adição/alteração de certa dependência de desenvolvimento presente em devDependencies, utilizamos o chore. Já para alterações/adições de dependências comuns aos projeto, e que haja impacto direto e real sobre o sistema, utilizamos o build.

Scope: contextualizando o commit

Nesse ponto — e seguindo as convenções passadas — conseguimos entender o tipo de alteração que foi realizada no commit (commit type) e entender com clareza o que o commit irá trazer se aplicado (commit subject).

Entretanto, até aonde essa mudança pode afetar ?

Em repositórios enormes, como monorepos, ou projetos com várias features e mudanças paralelas, não fica bastante claro até onde a mudança que irá chegar pode alterar. Para isso, podemos utilizar o escopo(scope) do commit.

Exemplo de commit utilizando o scope

Mesmo o scope não sendo obrigatório, ele pode ser utilizado para contextualizar o commit e trazer menos responsabilidade para a subject, uma vez que dispondo do tipo de commit e o contexto que foi aplicado, a mensagem deve ser o mais breve e concisa possível. Lembrando que o scope deve ser inserido no commit entre parênteses.

Além disso, no caso do scope é possível adicionarmos múltiplos valores, como por exemplo: Caso houvesse uma refatoração de código em um repositório com versões mobile, web e desktop. A qual afeta o contexto mobile e web, poderíamos escrever o commit da seguinte maneira:

Dessa maneira indicamos que é um commit de refatoração que afetara os contextos mobile e web da aplicação

Observação: Os escopos devem ser separados com / , \ ou ,.

Como usamos na LinkApi

Primeiramente, é importante dizer que o Conventional Commit e suas especificações devem ser seguidas e respeitadas. Entretanto, não é um modelo ao qual se adequa a todo tipo de projeto e empresa. Na LinkApi, por exemplo, ao desenvolver integrações não utilizamos CI. Além disso, alguns projetos por serem bastante conhecidos já possuem documentações e não é necessário reproduzi-las. Portanto os tipos de commit: ci ou doc não são utilizados.

Por outro lado, em nosso cotidiano haverão diversas regras de negócios implicadas à integração, e que não necessariamente gerariam uma feature nova, como por exemplo: um campo que deva vir em maiúsculo, ou um valor com 3 casas decimais, alguma regra de embalagem, tracking e etc.

Em nosso modelo de desenvolvimento, esse tipo de alteração encontra-se como uma mudança de regra de negócio, e para isso, nos criamos e utilizamos o tipo de commitbusiness.

Outra regra, que utilizamos “dentro de casa” é a de definir o escopo do commit, de acordo com a sprint que trabalhamos, assim conseguimos entender o contexto que aquela mudança ocorreu com mais facilidade.

Lembrando que quando ocorre uma mudança que afetam funcionalidades de mais de uma sprint, nós adicionamos isso ao contexto também, como representado abaixo:

Por fim, podemos dizer que o Conventional Commit pode adquirir propriedades daquele projeto, equipe ou empresa, desde que não fuja do conjunto de regras definidos pela convenção, esteja bem alinhados entre os interessados/participantes do projeto e seja bem documentado.

Bônus

Sim, você não leu errado: BÔNUS !!!

De certo, a curva de adoção para essa convenção pode ser demorada e até por vezes exaustiva. Para isso, vou deixar aqui recomendado uma ferramenta bastante útil para nos ajudar a adotar mais essa boa prática ao desenvolvimento de projetos:

CommitLint

Demonstração do commitlint (presente no repositório)

O commitLint fica responsável por checar se a mensagen de commit está seguindo o padrão da convenção e bloquear o envio de mudanças que não seguem o conventional commit pattern. Portanto, essa ferramenta “força” sua equipe a seguir o padrão estabelecido, lembrando que é possível fazer diversas customizações, como adicionar tipos, obrigar a inserir o escopo, utilizar somente snake_case/kebab-case/camelCase, entre outras diversas customizações de acordo com as necessidades do time.

Vocês podem conhecer um pouco mais da ferramenta, assim como suas configurações através desse vídeo, do repositório do commitlint, desse artigo, entre outros.

Conclusão

Obrigado a todos que leram até aqui, a intenção desse artigo foi de mostrar — para aqueles que não conheciam — uma boa forma de trabalhar em equipe, gerenciar o projeto e por fim de poder envolver-se melhor nos repositórios da comunidade (já que grande parte segue esse padrão).

Além, é claro, de poder mostrar um pouco mais sobre como trabalhamos na LinkApi.

Valeu pessoal, may the force be with you.

--

--

Victor Ribeiro
LinkApi Solutions

Dev at LinkApi and passionate about technology, Information Systems at USP