Meu commit Git favorito (2019)
(dhwthompson.com)- O commit de Dan Carley, “Convert template to US-ASCII to fix error”, surgido durante o trabalho no GOV.UK, mostra que até pequenas mudanças de código podem se tornar documentação de longo prazo da base de código por meio de mensagens de commit detalhadas
- Uma boa mensagem registra não apenas o que mudou, mas por que mudou, e este caso documenta de forma concreta o erro
invalid byte sequence in US-ASCIIque ocorria ao executarbundle exec rake, além das condições para reproduzi-lo - Como a string de erro e o processo de investigação ficaram registrados juntos, quem encontrar o mesmo problema depois pode usar
git log --grepou a busca de commits do GitHub para localizar registros anteriores da solução - A mensagem também inclui o uso de ferramentas Unix como
find -exec,file --mimeeiconv, permitindo que revisores e leitores futuros acompanhem o fluxo de resolução do problema - Nem todo commit precisa ser tão longo, mas mensagens que preservam contexto, processo de investigação e até emoção ajudam a construir entendimento compartilhado da base de código e confiança dentro da equipe
Um commit que transformou uma pequena mudança em documentação de longo prazo
- Mensagens de commit no Git, quando bem escritas, podem se tornar uma ferramenta poderosa de documentação que permanece durante toda a vida da base de código
- O commit citado como exemplo é uma mudança da época em que se trabalhava no GOV.UK dentro do Government Digital Service
- O autor é Dan Carley, e o título é “Convert template to US-ASCII to fix error”
- Graças ao modelo de desenvolvimento aberto do GDS, até exemplos internos de organizações como esse podem ser compartilhados publicamente
Contexto importa mais do que comprimento
- Este commit tem uma mensagem longa em comparação com a quantidade de código alterado, mas seu valor principal não está no tamanho em si, e sim no contexto útil
- A mesma mudança poderia ter sido registrada em outro lugar com algo curto como
change whitespaceoufix bug - Dan deixou um registro detalhado para que colegas próximos e leitores futuros entendessem a causa do problema e como ele foi resolvido
Mais importante do que o que mudou é por que mudou
- Uma boa mensagem de commit explica não só o que mudou, mas também por que mudou
- Este commit registra que, depois de adicionar um teste numa branch de feature para combinar com o conteúdo de
/etc/nginx/router_routes.conf, o resultado variava dependendo da forma de execução- Executando com
bundle exec rake specoubundle exec rspec modules/router/spec, tudo funcionava normalmente - Executando com
bundle exec rake, cada blocoshouldfalhava - O erro era
ArgumentError: invalid byte sequence in US-ASCII
- Executando com
- Sem essa explicação, provavelmente seria preciso adivinhar em qual ferramenta ocorreu o erro de parsing e em que condições
- O contexto original do trabalho pode desaparecer facilmente quando as pessoas esquecem, mudam de equipe ou saem da organização
Registro de erro pesquisável
- No começo da mensagem de commit, aparece exatamente a mensagem de erro que motivou a mudança
- Quem encontrar o mesmo erro pode localizar registros anteriores na base de código desta forma
git log --grep "invalid byte sequence"- GitHub commit search
- Pelos resultados da busca, dava para ver que várias pessoas pesquisaram esse erro e também identificar quem encontrou o problema primeiro e quais medidas foram tomadas
Deixar o processo de solução de problemas em forma de história
- A mensagem é estruturada de modo que se possa acompanhar como o problema aparecia, em que ordem foi investigado e como acabou sendo corrigido
- Como exemplo, ela inclui que o erro desaparecia ao remover o matcher
.with_content(//), que não havia caracteres estranhos no arquivo spec e que era possível reproduzir o problema ao fazer require do Puppet no mesmo interpretador - Como a mensagem de commit documenta a própria mudança, e não apenas um arquivo, função ou linha específica de código, ela é um ótimo lugar para registrar a jornada percorrida pela base de código
O efeito colateral de ampliar o conhecimento da equipe
- Dan registrou os comandos executados em cada etapa da investigação, e isso pode ser uma forma leve de espalhar conhecimento dentro da equipe
- Só pela mensagem de commit, o leitor já pode aprender formas de usar ferramentas Unix
- É possível passar o argumento
-execparafinde executar um comando para cada arquivo encontrado - Ao adicionar
\+no fim do comando, em vez de executar uma vez por arquivo, vários nomes de arquivo são passados a um único comandofile file --mimeinforma o tipo MIME do arquivo- Existe uma ferramenta chamada
iconv
- É possível passar o argumento
- Não é apenas quem revisa a mudança que ganha essa informação; quem encontrar esse commit depois também ganha
- Com tempo suficiente e muitos commits, mensagens assim podem se tornar um mecanismo de amplificação de conhecimento da equipe
Registros que criam empatia e confiança
- No fim do commit, há a frase “Now the tests work! One hour of my life I won't get back..”
- Essa frase transmite tanto a frustração de um desenvolvedor que passou uma hora rastreando um bug sutil quanto a satisfação de finalmente tê-lo resolvido
- Mesmo quando hacks temporários ou código de protótipo acabam entrando em produção e criando raízes, mensagens assim ajudam a lembrar que, por trás de cada mudança, havia alguém tomando a melhor decisão possível com as informações que tinha naquele momento
Nem todo commit precisa ser longo
- Este caso é um exemplo extremo, e não é necessário esperar esse mesmo nível de detalhe em todo commit desse porte
- Ainda assim, ele é um bom exemplo de como explicar o contexto por trás de uma mudança, ajudar outras pessoas a aprender e contribuir para um modelo mental compartilhado da base de código da equipe
- Se você se interessa por boas mensagens de commit e por ferramentas para estruturar mudanças, pode ver os seguintes materiais
- Telling stories through your commits by Joel Chippindale
- A branch in time by Tekin Süleyman
1 comentários
Opiniões no Hacker News
Para o bem ou para o mal, falando como cofundador do GitHub e autor de livros sobre Git, como o Pro Git, mensagens de commit do Git são um caminho peculiar para documentar código, mas muito ineficiente
O problema central é que a maioria das ferramentas, como Git e GitHub, geralmente mostra apenas a primeira linha. Mesmo no commit de exemplo, aparece só uma linha genérica como “US-ASCII error”, e o restante do conteúdo elogiado no texto quase ninguém vê nas ferramentas modernas
O Git foi projetado para que a mensagem de commit fosse o corpo de um e-mail lido por todos no projeto, mas hoje em dia ela em geral não cumpre esse papel. A menos que seja discutida como uma série de patches em uma lista de e-mails, quase nada além dos primeiros 50 caracteres do título é lido
Mesmo quando você procura se é
-w -C -C -Cou dois-Cnogit blamepara rastrear mensagens relacionadas, só vê a primeira linha; no fim, precisa encontrar o SHA e rodargit showpara ver a mensagem longa. Uma das minhas maiores reclamações sobre o Git é que, mesmo quando bem usadas, as informações são difíceis demais de reencontrarOlhando o histórico do projeto Git, as mensagens de commit quase sempre são excelentes, mas mesmo rodando
blameem um arquivo, é muito difícil acompanhar o contexto da implementação de uma função. A documentação que Jeff King deixou nos últimos 10 anos é genial, e é terrível que quase ninguém consiga apreciá-laNão sei qual é a solução exata, mas, na maioria das comunidades, escrever uma ótima documentação em mensagens de commit é, tristemente, quase uma perda de tempo. Porque é difícil demais de encontrar
git blame,git logegit showno terminal; acompanhar o histórico de um arquivo é fácil, e bastam alguns segundos comgit log -Gpara encontrar quando algo foi adicionado ou removidoO difícil é quando você encontra o commit e a mensagem é só “bleh” ou “add a thing”. Bastaria o desenvolvedor gastar 60 segundos escrevendo por que fez aquilo
Por outro lado, é uma alegria encontrar uma mensagem de commit que explica em detalhes por que uma mudança foi feita. Uma boa mensagem de commit economiza horas, até dias, de trabalho
O GitHub também contribui para o problema das mensagens de commit ruins. Com sorte, há detalhes na descrição do PR, mas eles não ficam logo ao lado do log de commits; normalmente o PR aponta para um link do Jira, o Jira aponta para uma conversa no Slack, e o Slack aponta para um documento do Google
O setor é realmente ruim em documentação, mas pessoas como Jeff King estão lutando do jeito certo. No fim, vejo isso não como um problema técnico, mas como um problema humano. Escrever documentação não traz recompensa imediata, então parece trabalho extra, e a recompensa só aparece dias, semanas ou meses depois
Por favor, escrevam boas mensagens de commit. Gastar apenas 1 minuto dizendo por que algo foi feito evita que todo commit vire uma maldita charada da cerca de Chesterton. Coloque isso em uma mensagem de commit fácil de encontrar, e seu eu do futuro — e eu — agradeceremos
Além disso, também discordo da afirmação de que é difícil encontrar mensagens de commit. Tenho pouquíssima dificuldade em acompanhar a história de uma linha de código, função ou arquivo; e, mesmo que a mensagem de commit nunca seja relida, ela tem valor no momento em que é escrita. Escrever uma boa mensagem me faz verificar se realmente escrevi o código que pretendia, e também é valioso para quem faz code review
A afirmação de que é difícil encontrar opções do
git blametambém soa como piada. Uma boa IDE deveria anexar informações de blame a cada linha, mostrar o diff com um botão e, a partir desse diff, permitir fazer blame recursivamente em linhas de contexto ou linhas removidas. Ferramentas como o Tig fazem exatamente issoConcordo que o GitHub dificulta ver mensagens de commit
A documentação anexada a commits não é escrita por diversão. É um mecanismo para reduzir o risco de aceitar patches enviados por alguém que talvez não esteja disponível depois, e para expor o raciocínio relacionado de modo que outras pessoas possam trabalhar em cima dele. Se você esconde o raciocínio, vai se acumulando uma propriedade exclusiva sobre o código, o que geralmente não é bom. Mensagens de commit também servem como prova de trabalho e podem se tornar importantes quando há patches demais. Em projetos comerciais, parte dessa importância pode ser menor
Na verdade, não há outro lugar para colocar esse tipo de documentação. Ela não cabe em comentários de código. Mas agora surgiu uma geração de desenvolvedores que acha que Git é sinônimo de GitHub, e que o único propósito do Git é subir alterações para o GitHub
O Git é ruim, mas é muito menos ruim do que as alternativas. Precisamos voltar ao básico e entender o propósito real do controle de versão
Por isso, talvez não ajude muito a comunidade atual, mas ajuda alguém que estiver depurando daqui a alguns anos
Concordo com a intenção geral, mas seria melhor colocar um BLUF mais específico no começo. Por exemplo: “Fix test issues caused by non-breaking space character \xa0”
Dá para entender imediatamente qual é o problema, e quem quiser saber mais ainda tem a liberdade de ler abaixo
Então, para fins de busca no histórico, uma boa mensagem de primeira linha já basta. Acho pouco relevante a historinha de ter ido até Nárnia e voltado para encontrar a causa raiz do bug. Dito isso, não sou contra usar a descrição do PR ou a mensagem estendida do commit para desabafar um pouco
Já senti orgulho ao escrever uma ótima mensagem de commit, mas tenho menos certeza do valor que isso entrega aos outros. Quando alguém encontra uma mensagem de erro estranha, adiciona um novo recurso ou quase em qualquer situação, acho que a maioria não pesquisa mensagens de commit
É meio triste, mas cresce em mim a suspeita de que mensagens de commit bonitas têm algo próximo da vaidade do programador. Quem mais as admira geralmente é o próprio autor, enquanto os outros passam sem perceber
Às vezes há espaço para esse tipo de ornamento estético, mas não tenho certeza de que o valor prático seja grande. Hoje já não me importo muito quando outra pessoa commita “fix whitespace issue”, e acho que isso me tornou um colega melhor
Pode ser diferente em projetos como Git ou Linux, com equipes distribuídas enormes e inúmeros commits. Os projetos com que estou familiarizado têm de 1 a 100 contribuidores, em sua maioria na mesma organização
Quando Google ou Stack Overflow não dão resposta, também procuro no GitHub se há algo parecido em PRs ou mensagens de commit. Incluindo repositórios privados aos quais tenho acesso, isso já me ajudou inúmeras vezes. Boas mensagens de commit claramente têm valor além da vaidade. Se muitos desenvolvedores não as leem, é perda deles; espero que, com experiência, percebam algum dia. Também vale ensinar métodos de busca a desenvolvedores júnior ou enviar o texto original
Desde então, tento escrever mensagens de commit que pelo menos ajudem meu eu do futuro a lembrar por que a alteração foi necessária
git blameantes de mudar uma linha de código, está fazendo erradoVárias vezes, ao tentar corrigir um bug óbvio, olhei o commit anterior pouco antes de commitar e descobri que aquele “bug” tinha sido introduzido intencionalmente, e que a tarefa JIRA vinculada explicava muito bem por que a minha alteração óbvia recriaria um bug de 2 anos atrás
git blameda IDE para entender o que aconteceu. Quando encontro uma boa mensagem de commit, fico gratoA primeira linha de uma mensagem de commit é a mais importante porque permite que o
git loglide com a cerca de Chesterton. Neste caso, acho que o autor errou o alvoO ponto central é colocar na primeira linha o motivo, não o que foi feito. Quem se interessa pelo que mudou pode olhar o código ou o diff
Por exemplo, poderia escrever “nginx .conf files must be in us-ascii”, depois “changed blahblah.erb to remove nonbreaking space character”, e então continuar com o restante, bastante bom, da mensagem de commit
É preciso pensar como em uma notícia. Presuma que o leitor pode parar em qualquer ponto, e escreva em ordem de importância decrescente e nível de detalhe crescente
Notícias também explicam o quê, não o porquê, no título
Neste caso, a primeira linha do original está perfeita. Ao percorrer o
git log, dá para supor que este commit provavelmente não alterou o comportamento funcional dos testes e seguir em frente“nginx .conf files must be in us-ascii” pode ser um bom título de bug ou PR, mas também pode corresponder a vários commits fazendo coisas diferentes, e não informa o que realmente aconteceu. Não dá para saber se os arquivos estão sendo convertidos para US-ASCII, se uma ferramenta de conversão está sendo criada, se a documentação está sendo atualizada, se testes estão sendo criados ou se é uma combinação disso. Começar pelo quê, e não pelo porquê, reduz essa confusão
Tanto faz se for “The files must be in us-ascii” ou “Changed the files to us-ascii”. Ambas deixam claro que os arquivos foram alterados para US-ASCII
A desvantagem desse estilo de documentação é que uma mensagem de commit, uma vez escrita, na prática não pode ser alterada
Claro, dá para fazer isso com
rebase, mas você realmente vai mudar algo escrito há um ano e já mesclado namain, causando dor nas branches de feature de todo mundo?Comparando com documentos salvos em arquivos
.md, Wiki ou Confluence, eu posso melhorar algo escrito por um colega, e outro colega também pode melhorar algo que eu escreviNeste caso, o bug pode ter sido corrigido e nunca mais aparecer. Mas há a tentação de explicar o design de um componente ao fazer commit dele, e hoje eu evito isso. O que acontece se esse componente mudar depois por alterações nos requisitos de negócio ou algo assim? A documentação do commit vai acabar explicando só a diferença? Então um novo integrante da equipe precisaria ler várias mensagens de commit e mesclá-las mentalmente para entender o sistema
Comparar isso com arquivos
.md, Wiki ou Confluence é como comparar uma bicicleta com um gansoAs considerações e trade-offs de quando um componente foi criado, ou até o fato de que eles não existiram, frequentemente são úteis para entender defeitos originais, evoluções posteriores e defeitos surgidos por mudanças nos casos de uso
Para um novo integrante entender o sistema, basta manter uma documentação “atual” separada. Esse documento não precisa cobrir até os trade-offs de implementação ou o fato de que um rascunho foi escrito sob pressão de prazo
Ao fazer squash de commits, o registro fica melhor como narrativa da adição de uma funcionalidade; o merge preserva o log imutável das mudanças reais no código; o rebase faz um pouco dos dois
Não há motivo para não poder ter os dois. Somos pessoas que programam computadores, então podemos criar o que quisermos
Se eu reescrevesse o Git, dividiria um commit em duas partes. Deixaria o histórico de alterações imutável, como no Git, em uma estrutura tipo Merkle DAG, e guardaria as mensagens de commit em um repositório de dados associado separado, formando um log razoável e editável que explicasse o fluxo de trabalho. Bastaria agrupar commits por tags de funcionalidade, corrigir erros de digitação e alterar mensagens como quiser, preservando o log de diferenças por baixo, ou seja, “o que realmente mudou no código”
git notes. Mas, se a mensagem for tão longa assim, parece pouco provável que alguém percebaHá uma parte da qual discordo. É o trecho “não espero esse nível de detalhe em todo commit, especialmente em commits desse tamanho”
Pela minha experiência, coisas pequenas e aparentemente inofensivas muitas vezes exigem explicações relativamente mais longas
Ontem escrevi três parágrafos explicando por que adicionei
--limit=999agh pr list. É porque isso confunde. Dentro do argumento--jqjá existe umlimit(, e, supondo que haja PRs infinitos, quanto maior o valor, menor de fato fica o resultado finalTambém escrevi um comentário no código, e provavelmente passei mais tempo pensando e lapidando do que escrevendo. Quero que este seja lembrado como exemplo da próxima vez que alguém insinuar que esse trabalho é só sair cuspindo código
Algo como “This was a non-ascii whitespace character that caused
ArgumentError: invalid byte sequence in US-ASCIIwhen runningbundle exec rake” seria suficiente. Tem palavras-chave para buscar problemas parecidos no futuro, contém a causa raiz e não é longo a ponto de as pessoas provavelmente passarem batidoNão acho que seja um ótimo commit do Git
Para tanto texto, a primeira linha “Convert template to US-ASCII to fix error” poderia ser melhor. Bastaria acrescentar algumas palavras dizendo qual caractere de espaço causou o erro e qual era o erro. Com essa explicação e o diff, o contexto necessário já seria suficiente
Sinceramente, o resto é quase inútil. Não chega a prejudicar, mas também não agrega muito valor. O autor documentou a jornada de rastrear esse bug, mas quem se importa?
O texto também traz um link para resultados de busca mostrando vários commits de pessoas que aprenderam com essa correção
Essa mensagem de commit é um repositório de conhecimento
Para ver ótimas mensagens de commit, basta olhar o histórico Git do kernel Linux. Lá isso é o padrão
A primeira linha sempre menciona o subsistema afetado pela mudança e, em seguida, traz um resumo de uma linha no imperativo. Depois, responde com o máximo de detalhes possível a três perguntas
Um exemplo poderia ser escrito como “o código atual faz X. Ao executar o caso de teste T, observou-se o comportamento inesperado U. Isso acontece pelo motivo R. Corrigimos fazendo F”
Recentemente, um colaborador me disse que minha abordagem para mensagens do Git — ou melhor, meus requisitos — é “peculiar”. Parece que minha experiência com o kernel Linux fica evidente, mas todas as minhas mensagens de commit têm esse formato
Se o assunto é sobre código existente, o comentário deve ficar junto do código. Conteúdo relacionado ao processo, por exemplo código removido ou código que não funcionava, deve ficar no commit
O mais importante é que a mensagem de commit pode referenciar uma issue por conveniência, mas precisa necessariamente reproduzir os detalhes essenciais. O GitHub é temporário, mas as mensagens do Git não são
Mensagens de commit cheias de contexto estão aqui[1]
Isso acontece com tanta frequência que um mantenedor escreveu este texto[2]
[1] https://github.com/git/git/commit/d70f554cdf38b0b05cfaa8e8eb...
[2] https://lore.kernel.org/git/xmqqedevo8ps.fsf@gitster.g/