Muitas pessoas afirmam que "os comentários devem explicar 'por que', mas não 'como'". Outros dizem que "o código deve ser auto-documentado" e os comentários devem ser escassos. Robert C. Martin afirma que (reformulado com minhas próprias palavras) frequentemente "os comentários são desculpas por códigos mal escritos".
Minha pergunta é a seguinte:
O que há de errado em explicar um algoritmo complexo ou um pedaço longo e complicado de código com um comentário descritivo?
Dessa forma, em vez de outros desenvolvedores (inclusive você) terem que ler toda a linha algoritmo por linha para descobrir o que ele faz, eles podem simplesmente ler o comentário descritivo amigável que você escreveu em inglês simples.
O inglês é "projetado" para ser facilmente compreendido pelos seres humanos. Java, Ruby ou Perl, no entanto, foram projetados para equilibrar legibilidade humana e legibilidade por computador, comprometendo assim a legibilidade humana do texto. Um humano pode entender um pedaço de inglês muito mais rapidamente do que ele / ela pode entender um pedaço de código com o mesmo significado (desde que a operação não seja trivial).
Então, depois de escrever um pedaço complexo de código, escrito em uma linguagem de programação parcialmente legível por humanos, por que não adicionar um comentário descritivo e conciso que explique a operação do código em inglês amigável e compreensível?
Alguns dirão que "o código não deve ser difícil de entender", "torne as funções pequenas", "use nomes descritivos", "não escreva código espaguete".
Mas todos sabemos que isso não é suficiente. Essas são meras diretrizes - importantes e úteis -, mas não mudam o fato de que alguns algoritmos são complexos. E, portanto, é difícil de entender ao lê-los linha por linha.
É realmente ruim explicar um algoritmo complexo com algumas linhas de comentários sobre sua operação geral? O que há de errado em explicar código complicado com um comentário?
fonte
Respostas:
Em termos leigos:
Bottom line:
Explicar-se é bom, não é melhor fazê-lo.
fonte
Há várias razões diferentes para o código ser complicado ou confuso. Os motivos mais comuns são melhor resolvidos refatorando o código para torná-lo menos confuso, não adicionando comentários de qualquer tipo.
No entanto, há casos em que um comentário bem escolhido é a melhor escolha.
Se é o próprio algoritmo que é complicado e confuso, não apenas a sua implementação - o tipo que é escrito em periódicos de matemática e é conhecido como Algoritmo de Mbogo - você coloca um comentário no início da implementação, lendo algo como "Este é o algoritmo do Mbogo para atualizar widgets, originalmente descrito aqui: [URL do artigo]. Esta implementação contém aprimoramentos de Alice e Carol [URL de outro artigo]". Não tente entrar em mais detalhes do que isso; se alguém precisar de mais detalhes, provavelmente precisará ler o artigo inteiro.
Se você pegou algo que pode ser escrito como uma ou duas linhas em alguma notação especializada e a expandiu para uma grande quantidade de códigos imperativos, colocar essas uma ou duas linhas de notação especializada em um comentário acima da função é uma boa maneira de diga ao leitor o que ele deve fazer. Isso é uma exceção ao argumento "mas e se o comentário ficar fora de sincronia com o código", porque a notação especializada é provavelmente muito mais fácil de encontrar bugs do que o código. (É o contrário, se você escreveu uma especificação em inglês.) Um bom exemplo é o seguinte: https://dxr.mozilla.org/mozilla-central/source/layout/style/nsCSSScanner.cpp#1057 ...
Se o código for direto no geral, mas contiver uma ou duas coisas que parecem excessivamente complicadas, desnecessárias ou simplesmente erradas, mas precisam ser assim por motivos, então você coloca um comentário imediatamente acima da parte suspeita, na qual você declara os motivos . Aqui está um exemplo simples, onde a única coisa que precisa ser explicada é por que uma constante tem um certo valor.
fonte
4
deve serCHAR_BIT / 2
;-)CHAR_BITS
16 e sizeof (size_t) 2, mas o valor máximo de size_t era, por exemplo, 2 ^ 20 [size_t contendo 12 bits de preenchimento]?reallocarray
e OpenBSD geralmente não acreditam na restauração de possibilidades que não acontecem em sua ABI.int
. Como é, dadouint32_t x,y,z;
, o significado de(x-y) > z
depende do tamanho deint
. Além disso, uma linguagem projetada para escrever código robusto deve permitir que os programadores façam a distinção entre um tipo em que os cálculos devem exceder o intervalo do tipo e devem ser encapsulados silenciosamente, versus aquele em que os cálculos que excedem o intervalo do tipo devem interceptar versus um em que os cálculos não é esperado que exceda o intervalo do tipo, mas ... #Não se trata de certo ou errado, mas de 'melhores práticas', conforme definido no artigo da Wikipedia :
Portanto, a melhor prática é tentar melhorar o código primeiro e usar o inglês, se isso não for possível.
Não é uma lei, mas é muito mais comum encontrar código comentado que exija refatoração do que código refatorado que exija comentários, a melhor prática reflete isso.
fonte
//This code seriously needs a refactor
Chegará o dia em que seu código bonito, perfeitamente criado, bem estruturado e legível não funcionará. Ou não funcionará bem o suficiente. Ou um caso especial surgirá onde não funcione e precise ser ajustado.
Nesse ponto, você precisará fazer algo que mude as coisas para que funcione corretamente. Especialmente nos casos em que há problemas de desempenho, mas também em cenários em que uma das bibliotecas, APIs, serviços da web, gemas ou sistemas operacionais com os quais você trabalha não se comporta conforme o esperado, você pode acabar fazendo sugestões que não são necessariamente deselegante, mas são contra-intuitivos ou não óbvios.
Se você não tiver comentários para explicar por que escolheu essa abordagem, há uma chance muito boa de que alguém no futuro (e que alguém possa até ser você) veja o código, veja como ele pode ser "corrigido" para algo mais legível e elegante e, inadvertidamente, desfaz sua correção, porque ela não parece uma correção.
Se todo mundo sempre escrevesse um código perfeito, seria óbvio que o código que parece imperfeito está contornando alguma intervenção complicada do mundo real, mas não é assim que as coisas funcionam. A maioria dos programadores costuma escrever códigos confusos ou um pouco confusos; portanto, quando o encontramos, é uma tendência natural resolvê-lo. Juro que meu eu passado é um idiota de verdade sempre que leio códigos antigos que escrevi.
Portanto, não penso nos comentários como um pedido de desculpas pelo código incorreto, mas talvez como uma explicação do motivo pelo qual você não fez a coisa óbvia. Ter
// The standard approach doesn't work against the 64 bit version of the Frobosticate Library
permitirá que futuros desenvolvedores, inclusive seu futuro, prestem atenção a essa parte do código e testem nessa biblioteca. Claro, você também pode colocar os comentários em seus commits de controle de origem, mas as pessoas só olharão para eles depois que algo der errado. Eles lerão os comentários do código à medida que mudam o código.As pessoas que nos dizem que devemos sempre escrever código teoricamente perfeito nem sempre são pessoas com muita experiência em programação em ambientes do mundo real. Às vezes, você precisa escrever o código que executa com um certo nível; às vezes, você precisa interoperar com sistemas imperfeitos. Isso não significa que você não possa fazer isso de maneira elegante e bem escrita, mas soluções não óbvias precisam de explicação.
Quando escrevo códigos para projetos de hobby que sei que ninguém mais lerá, ainda comento partes que acho confusas - por exemplo, qualquer geometria 3D envolve matemática com a qual não estou totalmente em casa - porque sei quando volto em seis meses, esquecerei totalmente como fazer isso. Isso não é um pedido de desculpas por código incorreto, é um reconhecimento de uma limitação pessoal. Tudo o que eu faria, sem comentar, é criar mais trabalho para mim no futuro. Não quero que meu futuro precise reaprender algo desnecessariamente se puder evitá-lo agora. Que valor possível isso teria?
fonte
A necessidade de comentários é inversamente proporcional ao nível de abstração do código.
Por exemplo, a linguagem Assembly é, para fins mais práticos, ininteligível sem comentários. Aqui está um trecho de um pequeno programa que calcula e imprime termos da série Fibonacci :
Mesmo com comentários, pode ser bastante complicado grocá-lo.
Exemplo moderno: Regexes geralmente são construções de abstração muito baixas (letras minúsculas, número 0, 1, 2, novas linhas etc.). Eles provavelmente precisam de comentários na forma de amostras (Bob Martin, IIRC, reconhece isso). Aqui está uma regex que (eu acho) deve corresponder aos URLs HTTP (S) e FTP:
À medida que os idiomas avançam na hierarquia de abstração, o programador pode usar abstrações evocativas (nome da variável, nomes de funções, nomes de classes, nomes de módulos, interfaces, retornos de chamada etc.) para fornecer documentação interna. Negligenciar tirar proveito disso e usar comentários para ocultar o papel é preguiçoso, um desserviço e desrespeito ao mantenedor.
Estou pensando em receitas numéricas em C traduzido principalmente na íntegra a receitas numéricas em C ++ , que eu inferir começou como receitas numéricas (em FORTAN), com todas as variáveis
a
,aa
,b
,c
,cc
, etc mantida através de cada versão. Os algoritmos podem estar corretos, mas não tiraram vantagem das abstrações fornecidas pelas linguagens. E eles me fodem. Amostra de um artigo do Dr. Dobbs - Fast Fourier Transform :Como um caso especial de abstração, todo idioma possui idiomas / trechos de código canônico para determinadas tarefas comuns (excluindo uma lista vinculada dinâmica em C) e, independentemente de sua aparência, eles não devem ser documentados. Os programadores devem aprender esses idiomas, pois eles não fazem parte oficialmente do idioma.
Portanto, o que deve ser levado em consideração: o código não-idiomático criado a partir de blocos de baixo nível que não podem ser evitados precisa de comentários. E isso é necessário muito menos do que acontece.
fonte
dec dword [term] ; decrement loop counter
. Por outro lado, o que está faltando no seu exemplo de linguagem assembly é um comentário antes de cada "parágrafo de código", explicando o que o próximo bloco de código faz. Nesse caso, o comentário normalmente seria equivalente a uma única linha no pseudocódigo, como;clear the screen
, seguido pelas 7 linhas que realmente são necessárias para limpar a tela.^(((ht|f)tps?)\:\/\/)?(www\.)*[a-zA-Z0-9\-\.]+\.(com|edu|gov|mil|net|org|biz|info|name|museum|us|ca|uk)(\:[0-9]+)*(\/($|[a-zA-Z0-9\.\,\;\?\'\\\+&%\$#\=~_\-]+))*$
? Esteja ciente dos endereços numéricos."The need for comments is inversely proportional to the abstraction level of the code."
praticamente tudo ali.Não acredito que haja algo errado com os comentários no código. A ideia de que os comentários são de alguma forma ruins na minha opinião se deve a alguns programadores que levam as coisas longe demais. Há muita variação nesse setor, principalmente em relação a visões extremas. Em algum lugar ao longo do caminho, o código comentado tornou-se equivalente ao código incorreto e não sei por que.
Os comentários têm problemas - você precisa mantê-los atualizados à medida que atualiza o código a que se referem, o que acontece com pouca frequência. Um wiki ou algo assim é um recurso mais apropriado para documentação completa sobre seu código. Seu código deve ser legível sem exigir comentários. O controle de versão ou as notas de revisão devem ser onde você descreve as alterações de código feitas.
Nenhuma das opções acima invalida o uso de comentários, no entanto. Não vivemos em um mundo ideal; portanto, quando qualquer uma das opções acima falhar por qualquer motivo, prefiro ter alguns comentários para recuar.
fonte
Eu acho que você está lendo um pouco demais o que ele está dizendo. Existem duas partes distintas na sua reclamação:
(1) é inevitável. Não acho que Martin discorde de você. Se você estiver escrevendo algo como a raiz quadrada inversa rápida , precisará de alguns comentários, mesmo que seja apenas "hacking de nível de bit de ponto flutuante maligno". Com exceção de algo simples, como uma DFS ou uma pesquisa binária, é improvável que a pessoa que está lendo seu código tenha experiência com esse algoritmo e, portanto, acho que deveria haver pelo menos uma menção nos comentários sobre o que é.
A maioria dos códigos não é (1), no entanto. Raramente você escreverá um software que não passa de implementações mutex feitas à mão, operações de álgebra linear obscuras com pouco suporte à biblioteca e novos algoritmos conhecidos apenas pelo grupo de pesquisa da sua empresa. A maioria dos códigos consiste em chamadas de biblioteca / estrutura / API, IO, clichê e testes de unidade.
Esse é o tipo de código que Martin está falando. E ele aborda sua pergunta com a citação de Kernighan e Plaugher no topo do capítulo:
Se você possui seções longas e complicadas em seu código, não conseguiu mantê-lo limpo . A melhor solução para esse problema não é escrever um comentário com um parágrafo no topo do arquivo para ajudar os futuros desenvolvedores a confundi-lo; a melhor solução é reescrevê-lo.
E é exatamente isso que Martin diz:
Este é o seu (2). Martin concorda que um código longo e complicado precisa de comentários - mas ele coloca a culpa por esse código nos ombros do programador que o escreveu, não uma idéia nebulosa de que "todos sabemos que isso não é suficiente". Ele argumenta que:
fonte
Nada como tal. Documentar seu trabalho é uma boa prática.
Dito isto, você tem uma falsa dicotomia aqui: escrever código limpo vs. escrever código documentado - os dois não estão em oposição.
O que você deve focar é simplificar e abstrair o código complexo em um código mais simples, em vez de pensar que "o código complexo é bom desde que seja comentado".
Idealmente, seu código deve ser simples e documentado.
Verdadeiro. É por isso que todos os seus algoritmos de API pública devem ser explicados na documentação.
Idealmente, depois de escrever um pedaço de código complexo, você deve (não uma lista exaustiva):
Nenhuma dessas etapas é trivial de executar (ou seja, cada uma pode levar algumas horas) e as recompensas por executá-las não são imediatas. Como tal, essas etapas são (quase) sempre comprometidas (por desenvolvedores cortando cantos, gerentes cortando cantos, prazos, restrições de mercado / outras condições do mundo real, falta de experiência etc.).
Você nunca deve confiar na leitura da implementação para descobrir o que uma API faz. Ao fazer isso, você está implementando o código do cliente com base na implementação (em vez da interface) e isso significa que o acoplamento do seu módulo já está pronto, você está potencialmente introduzindo dependências não documentadas com cada nova linha de código que escreve e está já adicionando dívida técnica.
Não - isso é bom. Adicionar algumas linhas de comentários não é suficiente.
O fato de você não ter código complicado, se isso puder ser evitado.
Para evitar código complicado, formalize suas interfaces, gaste ~ 8 vezes mais em design de API do que em implementação (Stepanov sugeriu gastar pelo menos 10x na interface, em comparação com a implementação) e desenvolva um projeto com o conhecimento de que você está criando um projeto, não apenas escrevendo algum algoritmo.
Um projeto envolve documentação da API, documentação funcional, medições de código / qualidade, gerenciamento de projetos e assim por diante. Nenhum desses processos é uma etapa rápida e única a ser executada (todos eles levam tempo, exigem reflexão e planejamento, e todos exigem que você volte periodicamente a eles e os revise / complete com detalhes).
fonte
Eu consideraria isso um pequeno abuso de "comentários". Se o programador quiser ler algo em vez de todo o algoritmo, é para isso que serve a documentação da função. OK, a documentação da função pode realmente aparecer nos comentários na fonte (talvez para extração por ferramentas de documentos), mas embora sintaticamente seja um comentário no que diz respeito ao seu compilador, você deve considerá-los como itens separados com propósitos separados. Não acho que "os comentários devam ser escassos" significa necessariamente "a documentação deve ser escassa" ou mesmo "avisos de direitos autorais devem ser escassos"!
Os comentários na função são para alguém ler , bem como o código. Portanto, se você tem algumas linhas difíceis de entender no código e não pode facilitar a compreensão, um comentário é útil para o leitor usar como espaço reservado para essas linhas. Isso pode ser muito útil enquanto o leitor está apenas tentando obter a essência geral, mas existem alguns problemas:
Existem exceções, mas a maioria dos leitores precisará entender o próprio código. Os comentários devem ser escritos para ajudá-lo, não para substituí-lo, e é por isso que você geralmente recomenda que os comentários digam "por que você está fazendo isso". Um leitor que conhece a motivação para as próximas linhas de código tem mais chances de ver o que faz e como.
fonte
Muitas vezes temos que fazer coisas complicadas. Certamente é certo documentá-los para uma compreensão futura. Às vezes, o local certo para esta documentação está no código, onde a documentação pode ser mantida atualizada com o código. Mas definitivamente vale a pena considerar a documentação separada. Também pode ser mais fácil apresentar a outras pessoas, incluir diagramas, figuras coloridas etc. Então o comentário é apenas:
ou mesmo apenas
Certamente as pessoas estão felizes com as funções nomeadas
MatchStringKnuthMorrisPratt
ouencryptAES
oupartitionBSP
. Nomes mais obscuros merecem explicação em um comentário. Você também pode adicionar dados bibliográficos e um link para um artigo do qual você implementou um algoritmo.Se um algoritmo é complexo, novo e não é óbvio, definitivamente vale a pena um documento, mesmo que seja apenas para circulação interna da empresa. Verifique o documento no controle de origem, se estiver preocupado com a perda.
Há outra categoria de código que não é tão algorítmica quanto burocrática. Você precisa configurar parâmetros para outro sistema ou interoperar com os erros de outra pessoa:
fonte
doPRD239Algorithm
que me diz nada sobre a função sem ter que procurar o algoritmo, a razãoMatchStringKnuthMorrisPratt
e oencryptAES
trabalho é que eles começam com uma descrição do que fazem e, em seguida, seguem com uma descrição da metodologia.Eu esqueço onde eu lê-lo, mas não é uma linha nítida e clara entre o que deve aparecer no seu código e que deve aparecer como um comentário.
Eu acredito que você deve comentar sua intenção, não seu algoritmo . Ou seja, comente o que você pretendia fazer, não o que você faz .
Por exemplo:
Aqui não há nenhuma tentativa de indicar o que cada etapa executa, tudo o que afirma é o que deve fazer.
PS: Encontrei a fonte à qual me referia - Horror em codificação: o código diz como, os comentários dizem o porquê
fonte
Future
indica que umget()
seguido por uma verificação paranull
detecta se aFuture
já foi executada - documentando corretamente a intenção e não o processo .Realmente? Desde quando?
Código bem projetado com bons nomes é mais que suficiente na grande maioria dos casos. Os argumentos contra o uso de comentários são bem conhecidos e documentados (como você se refere).
Mas estas são diretrizes (como qualquer outra coisa). No raro caso (na minha experiência, cerca de uma vez a cada 2 anos) em que as coisas seriam piores quando refatoradas para funções legíveis menores (devido a necessidades de desempenho ou coesão), vá em frente - faça um longo comentário explicando o que realmente é a coisa fazendo (e por que você está violando as melhores práticas).
fonte
O principal objetivo do código é comandar um computador para fazer algo; portanto, um bom comentário nunca substitui um bom código, porque os comentários não podem ser executados.
Dito isto, os comentários na fonte são uma forma de documentação para outros programadores (inclusive você). Se os comentários são sobre questões mais abstratas do que o que o código está fazendo a cada etapa, você está se saindo melhor que a média. Esse nível de abstração varia de acordo com a ferramenta que você está usando. Os comentários que acompanham as rotinas da linguagem assembly geralmente têm um nível mais baixo de "abstração" do que, por exemplo, este APL
A←0⋄A⊣{2⊤⍵:1+3×⍵⋄⍵÷2}⍣{⍺=A+←1}⎕
. Eu acho que provavelmente mereceria um comentário sobre o problema que ele pretende resolver, hmmm?fonte
Se o código é trivial, ele não precisa de um comentário explicativo. Se o código não for trivial, o comentário explicativo provavelmente também não será trivial.
Agora, o problema com a linguagem natural não trivial é que muitos de nós não somos muito bons em ler ou escrever. Tenho certeza de que suas habilidades de comunicação escrita são excelentes, mas, mesmo assim, alguém com uma compreensão menor da linguagem escrita pode entender mal suas palavras.
Se você se esforçar muito para escrever uma linguagem natural que não possa ser mal interpretada, você acaba com um documento legal (e como todos sabemos, essas são mais detalhadas e difíceis de entender que o código).
Código deve ser a descrição mais concisa de sua lógica e não deve haver muito debate sobre o significado de seu código, porque seu compilador e plataforma têm a palavra final.
Pessoalmente, eu não diria que você nunca deve escrever um comentário. Somente você deve considerar por que seu código precisa de um comentário e como você pode corrigi-lo. Este parece ser um tema comum nas respostas aqui.
fonte
Um ponto ainda não mencionado é que, às vezes, comentar com precisão o que um pedaço de código faz pode ser útil nos casos em que uma linguagem usa uma sintaxe específica para vários propósitos. Por exemplo, supondo que todas as variáveis sejam do tipo
float
, considere:O efeito de converter explicitamente um
float
parafloat
é forçar o resultado a ser arredondado para uma precisão única; o comentário poderia, portanto, ser visto como simplesmente dizendo o que o código faz. Por outro lado, compare esse código com:Aqui, o objetivo do elenco é impedir que o compilador grite da maneira mais eficiente de calcular com precisão (f2 / 10) [é mais preciso do que multiplicar por 0,1f, e na maioria das máquinas é mais rápido que dividir por 10,0f].
Sem o comentário, alguém que estivesse revisando o código anterior poderia pensar que o elenco foi adicionado com uma crença equivocada de que seria necessário impedir o compilador de gritar e que não era necessário. De fato, o elenco serve para o propósito de fazer exatamente o que a especificação da linguagem diz que faz: forçar o resultado do cálculo a ser arredondado para precisão única, mesmo em máquinas onde o arredondamento seria mais caro do que manter o resultado em maior precisão. Dado que um elenco
float
pode ter vários significados e propósitos diferentes, ter um comentário especificando qual significado é pretendido em um cenário específico pode ajudar a deixar claro que o significado real está alinhado com a intenção.fonte
someFloatProperty
a representação mais precisaf2/10
possível; o objetivo principal do segundo elenco é, portanto, simplesmente compilar o código . No primeiro exemplo, no entanto, o elenco claramente não é necessário para seu objetivo normal (alterando um tipo de tempo de compilação para outro), pois os operandos já o sãofloat
. O comentário serve para esclarecer que o elenco é necessário para uma finalidade secundária (arredondamento).(float)
elenco no segundo exemplo. A questão é sobre a constante literal0.1
. Você explicou (no próximo parágrafo do texto) por que escreveríamos0.1
: "é mais preciso do que multiplicar por 0,1f". Estou sugerindo que essas são as palavras que devem constar no comentário.double
para constantes ou cálculos intermediários cujo valor pode não ser representável, poisfloat
[em idiomas que exigem lançamentos explícitos irritantes de duplo para flutuar, preguiça pode ser o uso defloat
constantes, não para velocidade, mas para minimizar o aborrecimento].Comentários que explicam o que o código faz são uma forma de duplicação. Se você alterar o código e esquecer de atualizar os comentários, isso pode causar confusão. Não estou dizendo para não usá-los, apenas use-os criteriosamente. Eu assino a máxima do tio Bob: "Apenas comente o que o código não pode dizer".
fonte