Por que comentários “Why Not” são necessários
(buttondown.com)- Comentários podem usar linguagem humana, mais expressiva que identificadores, por isso são adequados para registrar alternativas que não entraram no código e opções abandonadas
- “comment the why, not the what” é uma abordagem que busca colocar o máximo de informação possível nos identificadores, mas recentemente tem surgido uma tendência de mover até os motivos para nomes longos de funções ou de testes
- No build do epub de
Logic for Programmers, foi usada uma implementação lenta que substitui sequencialmente 16 símbolos matemáticos em cada string, mas atualmente existem apenas 25 strings matemáticas, então isso é rápido o suficiente - Esse tipo de comentário indica, no futuro, onde mexer quando as strings matemáticas crescerem para centenas e virarem um gargalo no build, além de registrar que o código lento foi um trade-off consciente
- Como nomes de função e testes se ligam ao que o código realmente faz, fica difícil autodocumentar informação negativa — alternativas não escolhidas e coisas que o código não faz
Informações que comentários guardam com mais facilidade que o código
- Código é uma linguagem de máquina estruturada, enquanto comentários são escritos em linguagem humana mais expressiva
- “comment the why, not the what” pode ser interpretado como a ideia de colocar o máximo possível de informação nos identificadores
- Identificadores se parecem com uma forma limitada de linguagem humana embutida no código; eles não conseguem carregar todo “what”, mas em muitos casos conseguem carregar bastante
- Recentemente, vem crescendo a visão de que até o “why” pode ir para nomes longos de funções ou nomes de casos de teste, em vez de comentários
- Codebases autodocumentadas em geral ampliam a documentação por meio de mais identificadores
- Como exceção, há casos em que se diz que trocar comentários por logging torna o código mais autodocumentado
O que comentários “Why Not” tratam
- A informação mais difícil de expressar em código é a informação negativa
- Informação negativa chama atenção para o que não existe no sistema e para por que certas alternativas não foram escolhidas
- “why not” não explica o que o código faz; ele registra quais escolhas o código não faz e por quê
Caso de substituição de símbolos matemáticos no epub
- No build do epub de
Logic for Programmers, por motivos técnicos, notações matemáticas como\forallnão eram convertidas em símbolos como∀ - Para resolver isso, foi escrito um script que troca diretamente os tokens dentro das strings matemáticas por seus símbolos Unicode correspondentes
- A implementação mais fácil é chamar
string = string.replace(old, new)para cada um dos 16 símbolos matemáticos necessários - Esse método é ineficiente porque percorre cada string 16 vezes, mas uma abordagem que faz as 16 substituições em uma única passada é mais complexa
- A ideia central do comentário deixado foi a seguinte
- cada string é percorrida 16 vezes
- o livro atualmente tem apenas 25 strings matemáticas e a maioria tem menos de 5 caracteres
- por isso, ainda é rápido o suficiente
- Esse comentário explica não só “por que usar código lento”, mas também “por que não usar código rápido”
Uma placa para o leitor do futuro
- Mesmo que o código lento não cause problema agora, ele pode virar problema depois
- Se versões futuras de
Logic for Programmerspassarem a ter centenas de strings matemáticas, essa etapa do build pode virar o gargalo do processo inteiro - Deixar essa placa agora permite saber imediatamente, no futuro, qual parte precisa ser corrigida
- Mesmo que o código continue funcionando sem problemas, o comentário preserva o fato de que o autor conhecia o trade-off
- Quando alguém reabrir
epub_math_fixer.pydois anos depois, haverá menos necessidade de investigar se o código lento veio de inexperiência, falta de tempo ou erro - Um comentário negativo registra que a implementação lenta era conhecida, que alternativas foram avaliadas e que se decidiu não otimizar
Por que é difícil substituir isso por nomes de função e testes
- Um nome como
RunFewerTimesSlowerAndSimplerAlgorithmAfterConsideringTradeOffsé longo demais e ainda não explica o trade-off de forma suficiente - Se o código for otimizado depois, talvez seja preciso mudar esse nome em vários lugares
- O problema maior é que esse tipo de nome não informa o que a função realmente faz, enfraquecendo a autodocumentação em vez de fortalecê-la
- Identificadores como nomes de função e variável só conseguem carregar uma única cláusula de informação
- É difícil colocar ao mesmo tempo, em um único identificador, “o que a função faz” e “qual trade-off ela assume”
Informação negativa que também é difícil deixar em testes
- Seria possível criar um teste que faz grep nos blocos matemáticos do livro e falha se a contagem passar de 80
- Mas esse teste não testa diretamente o próprio
EpubMathFixer - Não há, dentro da função, um ponto ao qual esse teste possa se prender
- A autodocumentação acompanha o código escrito e explica o que o código faz
- Como a informação negativa trata do que o código não faz, ela não combina, em sua base, com a lógica de autodocumentação
Uma pergunta mais ampla
- Comentários “why not” podem ser vistos como um caso de contrafactualidade
- Fica a questão de até que ponto elementos abstratos da comunicação humana podem, em geral, ser autodocumentados
- Ainda permanece em aberto se informações como metáfora, incerteza e argumentos éticos também podem ser autodocumentadas
1 comentários
Opiniões no Hacker News
Lembro de uma piada que acho que vi no Twitter alguns anos atrás: era algo como “engenheiros júnior escrevem comentários explicando o que o código faz, engenheiros plenos escrevem comentários explicando por que o código faz aquilo, e engenheiros sênior escrevem comentários explicando por que o código não foi escrito de outro jeito”
Por outro lado, quando o significado podia ficar claro só com nomes de funções e variáveis, já escrevi funções bem grandes sem comentário nenhum
Por isso, menos comentários acaba vencendo, mas, ao ver uma base de código sem comentários, só olhando de perto dá para saber se aquilo é uma obra-prima belamente lapidada ou um código instável feito por iniciantes
Mesmo que pareça uma gambiarra completa, às vezes isso é de fato o melhor que dá para conseguir
Deixo em comentário tudo que acho que será útil para mim quando eu voltar a olhar o código um ano depois. Normalmente é o porquê e o por que não, e, quando o código é complexo, também escrevo um breve “o quê” para enxergar melhor o fluxo
O que não é útil são comentários obrigatórios. APIs públicas devem ser bem documentadas, mas algumas organizações exigem comentário em todas as funções, até nas privadas, e às vezes o propósito é tão óbvio que o comentário só repete o nome da função. Isso não só desperdiça tempo como também nos deixa insensíveis a comentários e ensina o hábito de ignorá-los
Também não gosto de comentários inúteis adicionados por ferramentas. Colocar
//forou//tryem todo loop ou bloco do tipo é especialmente ruimSeria melhor eliminar comentários obrigatórios e gerados e, em temas escuros, transformar comentários em cores neon claras para que se destaquem. Se há um comentário, isso deveria significar que ele é importante
Para manter sistemas grandes e antigos em um ambiente de negócios com prazos apertados, às vezes é preciso fazer coisas estranhas, e nesses casos é necessário explicar por quê
Um grande motivo para ser contra comentários é que eles também passam a fazer parte do código e precisam de manutenção. Mas a maioria das pessoas só lê comentários quando fica travada, e os editores também os deixam em cinza apagado, tornando-os psicologicamente invisíveis. Por isso, comentários envelhecem com facilidade
Fico curioso se o contexto escrito para o “eu do futuro” também é útil em uma base de código compartilhada tocada por muitos desenvolvedores, ou se é uma abordagem que funciona melhor quando apenas poucas pessoas mexem no código
Então acrescentei “=> yes!” ao comentário existente e fiquei grato ao meu eu do passado por ter documentado aquela dúvida. No trabalho, especialmente ao corrigir bugs, costumo deixar uma ou duas linhas de comentário sobre mudanças não óbvias, junto com o número do ticket
O formato de comentário de que mais gosto entre os que já deixei no código é este template: “DEAR MAINTAINER: este código está assim por [motivo]. Se, ao tentar ‘consertá-lo’, você perceber que foi um erro terrível, incremente o contador
total_hours_wasted_here = ncomo aviso para a próxima pessoa”Não sou o autor original, mas já usei com gratidão uma ou duas vezes, e era divertido ver commits de uma linha só incrementando o contador
Um engenheiro mais sênior assumiu a base de código e “consertou” tudo transformando em laços; tentou me dar uma bronca por e-mail sobre por que recursão era ruim, mas o código dele não atendia a todos os requisitos reais e, no fim, ele basicamente recriou as coisas que eu tinha feito com recursão
Mais tarde ele até se desculpou por algumas das falas, mas talvez toda a situação pudesse ter sido evitada se eu tivesse colocado um comentário desses no topo
Concordo que o título é ambíguo, e foi por isso que acabei lendo o texto. Pessoalmente, prefiro usar poucos comentários no geral, mas os comentários explicativos mencionados no texto claramente têm valor. É um bom lembrete para explicar por que algo foi feito daquele jeito e por que não de outro
Isso se aplica especialmente ao próprio código que você terá de manter daqui a 5, 10 ou 15 anos. Recentemente, ao revisar código novo de um colega, pensei “por que ele fez assim?”, mas 10 linhas acima havia exatamente o mesmo motivo que eu tinha deixado 8 anos antes. O colega estava seguindo uma regra central de manutenção: fazer parecer com o código existente
É apenas um caso especial de um princípio mais amplo: comentar aquilo que é surpreendente ao ler o código
Quem, ao escrever código, fica se perguntando na cabeça “vou conseguir entender este código depois?” e sempre responde instintivamente “sim” é arrogante e frequentemente está errado. Se a resposta for “não tenho certeza”, a próxima pergunta naturalmente é “por quê?”, e essa resposta é justamente o que deve ir no comentário
Às vezes a resposta é “porque quem lê pode se perguntar por que não foi escrito de outro jeito”, que é o caso especial tratado neste texto. Mas, às vezes, é “porque não está claro como funciona ou por que está correto”, e aí é preciso outro tipo de comentário
Isso costuma acontecer principalmente ao cortar strings ou ao explorar, como etapa intermediária, uma estrutura de dados incomum
Só com identificadores dá para ir muito longe, mas não até o fim. Pessoalmente, gosto de exigir comentários de documentação, como jsdoc/xmldoc, para métodos públicos, variáveis, campos e parâmetros
Dar bons nomes aos métodos também é importante, mas tentar escrever brevemente o que eles fazem deixa tudo mais claro e, em especial, revela defeitos óbvios. Muitas vezes, no momento em que se escreve a primeira frase, surge um nome melhor; e, quando a explicação começa a incluir “e”, isso é sinal de que o método está fazendo coisas demais e pode ser dividido de forma mais lógica
É fácil achar que propriedades são tão claras que não precisam de documentação, mas algo como
/** The API key */ string ApiKey;deixa coisa demais de fora. Não dá para saber de onde vem essa chave, se é só para uso interno ou se é trocada com sistemas externos, se é obrigatória, se pode ser null ou vazia, se há tamanho máximo, o que acontece com valores inválidos, ou se há mais código ou documentação para lerPara o autor original, são informações que levam 1 ou 2 minutos para escrever, mas uma pessoa nova pode levar horas para descobri-las ao modificar ou usar aquilo, ou ao ser chamada anos depois para corrigir um bug
Em revisões de código, quando consigo prever o que um revisor excessivamente minucioso vai dizer, costumo escrever comentários como “não fiz X por causa de Y”. O objetivo é reduzir idas e vindas incômodas na discussão
Comentários do tipo “percorremos cada string 16 vezes, mas o livro tem só 25 strings de fórmulas até agora, e a maioria tem menos de 5 caracteres, então é rápido o suficiente” também têm outra variação
É colocar um log de debug que dispare quando a entrada crescer muito além das restrições de projeto originais. Ele transmite quase a mesma mensagem ao futuro desenvolvedor, mas permite descobrir mais cedo e reduzir ainda mais o tempo de diagnóstico e depuração
Em um sistema ideal de logging e observabilidade, mediríamos o tempo de todos os componentes do app e deixaríamos informações de debug com frequência, mas quem de fato usa um sistema perfeito desses? É mais importante fazer o esforço específico de adicionar logs nas partes em que a performance pode piorar depois ou onde não houve tempo para otimizar
Em retrospecto, é uma ideia óbvia, mas até agora eu costumava deixar comentários nos pontos que precisariam ser revistos depois e torcer para lembrar desses comentários quando as coisas piorassem
Não importa o que digam, uso muitos comentários e comentários de documentação por todo o código. Mas faço a abordagem inversa: primeiro escrevo, em comentários, uma lista aproximada das etapas da aplicação e, durante o desenvolvimento, vou quebrando as etapas grandes em etapas menores, apagando ou mantendo os comentários originais e refinando-os até virar um algoritmo quase completo
Como normalmente programo de fora para dentro, também escrevo o código enquanto vou quebrando os comentários. Às vezes programo tudo de uma vez e depois comento a ponto de a maioria achar cansativo. Coloco descrições em todas as funções e variáveis, e até na função
deg_to_radponho"""Converts degrees to radians.""". Armazenamento é baratoSei que a maioria não gosta disso, mas tudo bem. Se não quiser ver, dá para remover com um script ou tirar na revisão de código. Ainda assim, acho muito mais agradável ler meu código antigo do que código alheio sem comentários. Em Python, código boilerplate simples como uma API Flask em geral é autodocumentado, mas às vezes é justamente esse boilerplate, por mudar bastante, que recebe comentários importantes. Na indústria, é mais comum reescrever por inteiro a parte de algoritmos
Vou continuar gostando de comentários e comentários de documentação
Gosto de fazer a conceitualização inteira na cabeça, e no início sinto que experimentar várias opções de projeto escrevendo de fato é lento. Então, quando uma escolha de design fica definida, ela precisa ser documentada. Afinal, outras pessoas ainda não têm acesso à minha cabeça
Espero que os detalhes fiquem mais claros quando os outros também concluírem essa conceitualização. Mas, se por algum motivo essa conceitualização não acontecer, o código pode ficar mais difícil de ler, então faço ajustes adicionais para manter pelo menos uma legibilidade básica
Atualizar comentários muitas vezes dá tanto trabalho quanto corrigir o código. Então, na prática, comentários geralmente são algo esperando para virar mentira. No fim, comentário e código se desalinham, e isso pode ser pior do que código sem comentários
Prefiro testes automatizados que mostrem a intenção. Em geral, testes não conseguem mentir, porque, se mentissem, não teriam sido mesclados. Se houver um bom conjunto de testes mostrando como o código deveria ser usado, quero vê-lo, porque ele é explicativo e quase garantidamente verdadeiro
Ajuda demais quando preciso voltar a olhar 5 semanas depois
Sigo a ideia de que “comentários são um pedido de desculpas ao meu eu do futuro”
Se o código é estranho ou lento, ou se é uma parte que, ao explicar para alguém, eu diria “está meio tosco”, normalmente deixo um comentário. Especialmente se já tentei mudar aquilo antes, documento os casos que não funcionaram, o que foi corrigido etc.
Ao abordar os comentários com esse critério, os comentários desnecessários desaparecem naturalmente, e em geral você acaba documentando o porquê só quando realmente precisa. Teste por cerca de um mês na sua própria base de código e vai entender a sensação