O dedo do meio de Chesterton
(arp242.net)- Chesterton’s fence é um conselho para não mudar levianamente um código cujo motivo você desconhece, mas código sem motivo registrado e histórico de commits sem explicação empurram o mesmo peso para o próximo desenvolvedor
- O corpo dos commits dos últimos 13 anos nesse repositório soma apenas 295 linhas; removendo os corpos relacionados a dependabot, revert e typo, cai para 167 linhas
- Os títulos dos commits, mesmo em mudanças grandes, não dão contexto, como em “fix page A”, e quase não há documentação separada nem comentários no código, o que torna difícil rastrear o motivo das mudanças
- No código restam refatorações inacabadas, vestígios de funcionalidades removidas, recursos adicionados mas não conectados nem usados, e funcionalidades que aparentemente ninguém usa
- Mensagens de commit e documentação deveriam ao menos registrar “o que mudou, por que mudou e por que essa é uma boa solução”; quando não se deixa nada, o custo para quem vem depois aumenta
O peso deixado por código sem motivo
- Chesterton’s fence é a metáfora de que, se você não entende por que algo está daquele jeito, não deve sair mudando sem cuidado
- Em programação, também pode acontecer de alguém “corrigir” um código que parecia estranho e só depois descobrir que havia uma razão para aquela estranheza
- Neste caso, aparece o problema do lado oposto
- Há muito código e muitas decisões estranhas, mas não existe registro que permita saber por que ficaram assim
- Os desenvolvedores anteriores já saíram todos, então nem há a quem perguntar
- O histórico de commits do repositório praticamente não oferece contexto útil
- O corpo dos commits dos últimos 13 anos totaliza 295 linhas
- Excluindo manualmente os corpos de commits de dependabot, “revert commit” e “fix typo”, restam 167 linhas
- Dá algo como uma linha por mês
- Os títulos dos commits também costumam ser insuficientes para explicar mudanças grandes, como “fix page A”
- Não há documentação separada e quase não existem comentários no código
- Uma situação assim se aproxima mais do dedo do meio de Chesterton: “fizemos um monte de coisas estranhas, mas não vamos dizer por quê”
O mínimo de registro que um desenvolvedor deve deixar
- A base de código contém vários tipos de código incompleto ou residual
- Refatorações que nunca terminaram
- Vestígios de funcionalidades removidas
- Funcionalidades adicionadas, mas não conectadas nem usadas
- Funcionalidades que aparentemente ninguém usa
- No geral, também parece haver um forte problema de Chesterton’s gap
- Algo como “ainda não existe uma cerca, então vamos construir uma”, sem perguntar se a cerca realmente é necessária
- Escrever bem é difícil, mas deixar uma explicação aceitável não é
- Um registro de mudanças deveria responder basicamente a três perguntas
- O que está sendo mudado
- Por que está sendo mudado
- Por que esta solução é boa
- Em alguns casos, “Implement new feature X” pode bastar, mas na maioria das vezes há algo a dizer sobre por que ou como a funcionalidade foi adicionada daquele jeito
- Em correções de bugs, refatorações e mudanças substanciais, normalmente dá para registrar ao menos um ou dois parágrafos explicando a mudança e seu motivo
- Esse tipo de registro não é opcional; faz parte do trabalho de desenvolvimento de software
- Não precisa ser elegante
- Não precisa ser um inglês perfeito
- Não precisa ser um ensaio grandioso
- Mesmo que falte alguma coisa, ainda é muito melhor do que não deixar nada
- Quando não se deixa nada, o problema é empurrado para todas as pessoas que vierem depois
1 comentários
Comentários do Lobste.rs
Às vezes, não é claro no momento o que vai se tornar importante depois. Ajuda muito quando todo o processo que levou até um commit fica registrado publicamente, mas como todos os envolvidos já têm muito contexto, algumas coisas acabam ficando de fora por serem consideradas “óbvias demais”.
Quando a discussão não é registrada, fica muito mais difícil escavar o processo de tomada de decisão como uma arqueologia digital. No fim, às vezes é preciso lidar com um estado em que não se sabe por que aquela cerca está ali, e avaliar com base no contexto atual e no conhecimento do sistema que as pessoas de agora têm.
Ajuda muito quando há alguém que permaneceu no projeto por bastante tempo e adquiriu compreensão e intuição sobre o sistema como um todo. Organizações que veem desenvolvedores como engrenagens substituíveis acabam sem ninguém ficando tempo suficiente, repetindo os mesmos erros e reinventando a roda.
Quando a próxima pessoa olhar o código, a chance de ela entender por que aquilo está daquele jeito deixa de ser zero.
Nunca entendi desenvolvedores que colocam só “fix” ou “WIP commit” na mensagem de commit. Provavelmente nunca fizeram uma arqueologia de código séria, ou nem chegaram a pensar que isso fosse algo possível.
Eu sempre tento errar pelo lado de informação demais. Assim, meu eu do futuro, meu sucessor ou o pobre coitado chamado quando der pane pelo menos têm alguma chance de descobrir por que algo acabou quebrando.
Nesses casos, fica difícil acompanhar mentalmente o que mudou após cada checkpoint ou escrever uma explicação significativa, e o commit passa a ser tratado menos como um meio de dividir o trabalho em unidades bem definidas e mais como o botão de “salvar” de um videogame.
Por outro lado, se você gerar um grande commit como resultado de uma grande refatoração de API, provavelmente qualquer coisa menos que uma explicação no nível de um documento de design será insuficiente, e se você não vai fazer isso, o valor de uma mensagem longa também fica ambíguo. Ainda assim, algumas pessoas tratam isso como se fosse um distintivo de honra e começam a colocar nos anúncios de release frases como “correções de bugs e melhorias de funcionalidades”, o que é claramente a conclusão errada.
Muitos desenvolvedores frequentemente esquecem que a “outra pessoa” que vai precisar ler e entender o contexto de uma mudança pode ser eles mesmos no futuro. Todo mundo já passou pela situação de coçar a cabeça olhando para um bloco de código, rodar
git blamee ver o próprio nome encarando de volta.Boas mensagens de commit já me pouparam incontáveis vezes de gastar horas, às vezes dias, cavando em issues, e-mails e logs de chat atrás do porquê. Isso aconteceu até em casos em que eu supostamente deveria conseguir responder de imediato.
É preciso ser gentil com seu eu do futuro. Vale a pena despejar no log do git tudo o que você sabia, pensava e foi discutido. Nunca é claro o que ainda continuará óbvio daqui a 5 anos.
git blamepelo menos não acontece muito quando houve um motivo para aquilo. Coisas aleatórias, como o comportamento estranho de outra pessoa, são difíceis de explicar de forma útil se você não consegue ver o processo dela, mas se foi algo que um dia fez sentido para mim, ao reler eu normalmente consigo entender de novo.Meu jeito de aprender parece ser mais como camadas de lava se acumulando. Desde o ensino médio, em vez de mudar muito, fui só acrescentando o que sei e as formas como consigo usar esse conhecimento.
Recentemente alguém afirmou que, se uma mensagem de commit passa de uma frase, em geral é perda de tempo; eu queria rebater com força, mas fui pior do que esperava em provar o contrário
Uma das questões é que tipo de informação deve entrar na mensagem de commit e que tipo deve ir para comentários inline, ADRs ou outros documentos mais longos
Ainda tento escrever boas mensagens de commit, mas no trabalho isso já parece sem esperança, e nem nos meus projetos pessoais de brincadeira eu consigo manter consistência
Mas o código final deve ser compreensível sem precisar ler a mensagem de commit. Se houver algo no código novo que precise de justificativa, isso deve entrar em comentário
Em outras palavras, a mensagem de commit explica por que fizemos essa mudança agora, enquanto os comentários explicam por que o código, no estado final da mudança, está daquele jeito
Mudanças maiores, especialmente novas funcionalidades, deveriam ter algum documento de design em algum lugar. Se precisar de revisão, pode ser um documento real dentro do repositório, ou pode estar no rastreador de issues
A mensagem de commit deve apontar para esse documento e talvez também precise explicar detalhes que surgiram quando o design foi traduzido para o código. Se possível, também é bom incluir um breve resumo do documento de design. Isso é especialmente importante quando há vários possíveis revisores: sinaliza quem deveria se interessar mais e ajuda a detectar casos em que ficou de fora alguém que deveria ter dado feedback na fase de design
git logoujj log, mas quase sempre por meio da exibição de comentários por linhaA linha de título costuma ser muito útil, de forma geral, para decidir se vale a pena investigar mais. Quando o corpo traz informação sobre por que a mudança foi feita, isso ajuda quando o motivo não é intuitivo
Por exemplo, mesmo que entre bastante código, muitas vezes só “admin: add impersonation” já basta. Mas se for “auth: shorten JWT timeouts”, eu gostaria de ver uma ou duas frases explicando por que os timeouts precisavam ser reduzidos
Mensagens de commit realmente longas me parecem, na prática, pouco úteis. Em grande parte pelos mesmos motivos apontados no texto. Esse formato parece vir de fluxos de trabalho em que a mensagem de commit também é a descrição do PR, como fluxos baseados em e-mail ou o Gerrit. Nesses casos não chega a ser prejudicial, mas também é difícil dizer que agrega valor necessariamente
Estou numa situação parecida. No grupo mais amplo do meu trabalho, só eu e mais uma pessoa escrevemos mensagens de commit detalhadas
Mesmo que você saiba por que uma cerca foi construída, pode não saber por que ela está ali agora. Mesmo sendo a própria pessoa que construiu a cerca de Chesterton, talvez você não saiba se pode derrubá-la
A árvore de interdependências pretendida quando o sistema foi criado é apenas um subconjunto da árvore de interdependências real em algum momento posterior. Então, mesmo que um desenvolvedor perfeito explique completamente por que a cerca foi construída, a utilidade disso é limitada
O que essa pessoa sabe é por que ela foi construída, não a resposta para “o que quebra se eu remover esta cerca?”. Basta ver https://xkcd.com/1172/. Entre os casos de uso engraçados, alguns podem parecer irrelevantes, mas sempre existem usos legítimos que nem o desenvolvedor original conseguiria prever
Saber o que o desenvolvedor original pensava, ou o que ele fumou, é legal, mas essa informação fica em algum ponto entre incompleta e irrelevante
Como exemplo inventado, imagine que a cerca de Chesterton tenha sido erguida originalmente para manter as crianças longe de uma poça d’água, numa época em que havia fazendas dos dois lados. Agora existe uma rodovia, e essa cerca pode ter se tornado por acaso a única barreira que impede colisões entre carros e veados, evitando mortes em massa de animais e pessoas
Ninguém sabe disso. A combinação entre rodovia e ausência da cerca nunca foi testada, e na verdade nunca existiu; além disso, quem construiu a rodovia e o departamento de recursos naturais não sabem por que há tão poucos atropelamentos de animais naquela estrada. Alguns anos depois, todas as fazendas podem virar conjuntos habitacionais e deixar de ser uma rota importante de migração animal; nesse ponto, a cerca pode se tornar inútil — ou não
Se isso lhe parece forçado ou algo que não se aplica ao seu caso, eu tenho inveja. Pela minha experiência, tirando empresas com algum porte e que não sejam muito antigas, no geral é assim que as coisas funcionam
A verdade é que tudo o que fazemos faz parte de um ecossistema, depende de coisas com as quais nunca concordamos explicitamente em interagir, e também é algo de que outras coisas passam a depender. Dá para reduzir a superfície da API e evitar que todos os detalhes de implementação virem problema do vizinho, mas o acoplamento não intencional é quase uma lei do universo tão inevitável quanto o aumento da entropia
Para algumas pessoas, isso soa niilista e derrotista. Dá para dizer que não deveríamos lutar contra a entropia? Mas eu acho que o melhor uso do tempo e o maior retorno sobre o investimento vêm de reconhecer que isso, no fundo, não é algo para combater, e sim para gerenciar
Se você finge que sempre pode conhecer o estado do mundo, está basicamente agendando fracasso e autopunição. É como 100% de uptime: não existe, e para a maioria dos casos é uma meta errada
Se você admite que está gerenciando um processo com certo grau de incerteza heisenberguiana, consegue escolher maneiras de usar melhor o tempo limitado do dia para produzir resultados melhores. Em especial, isso permite fazer um equilíbrio inteligente entre resposta preventiva e resposta reativa, além de entender que não dá para reduzir a resposta reativa a zero, e que às vezes não faz sentido gastar um ano de prevenção para evitar um dia de reação
Então, quanta documentação deve ir em um commit? Quantos documentos de design ou planos de teste deveriam existir? Eu também não sei. Mas, se for para sugerir uma linha de pensamento: toda documentação é escrita para o leitor
Se você altera a base de código, as pessoas do time atual — incluindo quem entrar depois — devem conseguir, por meio de investigação, entender o que a mudança fez e por que foi feita; e também deveria haver alguns alertas sobre armadilhas perigosas ou bugs que sustentam carga
Em geral, isso funciona melhor na forma de ponteiros para contexto adicional que montem o cenário, não como prosa longa. Por exemplo: “Esta etapa exige autenticação como parte da política que requer aprovação multipartes para toda alteração. see: go/multiparty”
Sistemas que buscam perfeição ao lidar com seres humanos são realmente desconfortáveis. Coisas como DRM, trusted computing, remote attestation, Faro Plague e smart contracts
Sistemas que podem ser reiniciados em modo de serviço para serem corrigidos são muito melhores. Porque não dá para prever em que direção o software deve evoluir para realmente ajudar as pessoas no futuro. Melhor torná-lo fácil de modificar do que travá-lo 100%
Quase não escrevemos corpo de commit, mas escrevemos títulos razoavelmente bons. Se esse for o critério de medição, não sei bem do que ele é critério
Em codebases grandes, código claro e cobertura de testes suficiente muitas vezes são bem mais úteis do que documentação
Pode acontecer de uma mudança totalmente válida ser feita, os testes serem atualizados de acordo, mas depois continuar completamente obscuro por que aquela mudança era necessária. Isso é ainda mais verdadeiro quando as linhas modificadas causam comportamento inesperado ou comportamento adicional em produção
Um simples revert pode não ser desejável, e nessas horas ter o histórico completo de por que a mudança foi feita ajuda de verdade
Já vi muitos casos em que a ideia estava certa, mas surgiram consequências inesperadas. Se você conhece a intenção, consegue chegar a uma mudança realmente correta que também resolva o novo problema e preserve o motivo original da alteração
Se você insiste em commits de uma linha só, então pelo menos coloque o número do ticket para que o histórico possa ser lido por lá
Ganhei um dinheiro bem decente durante cinco anos viajando por regiões exóticas para recuperar codebases desse tipo. @arp242, sempre cobre mais caro e durma com https://archive.org/details/working-effectively-with-legacy-code debaixo do travesseiro
Felizmente, os geradores de tranqueira de IA escrevem mensagens de commit gigantes. Muitas vezes elas até têm alguma relação com a mudança real, então pelo menos essa parte estaria resolvida