Comente o porquê, não o quê
Comentários podem ajudar você ou seus colegas a entenderem decisões cruciais por trás do design do código de um projeto. Porém, para que isso aconteça, é importante saber diferenciar quando e como comentar, de forma que os seus comentários não se transformem em informação desatualizada ou simplesmente redundante. Tipos de comentários Ao ler diferentes comentários em diferentes projetos, é possível notar que alguns "tipos" de comentários podem ser formados, com base no que eles informam e em como são feitos. Comentários redundantes Alguns comentários podem ser classificado como redundantes, ou triviais. Normalmente esses comentários repetem o que já é possível inferir pelos nomes das funções chamadas e nomes de variáveis. // carrega configuração cfg, err := config.Load() if err != nil { return err } Esse tipo, apesar de desnecessário, tende a não causar um impacto negativo de grande magnitude. Isso, pois ele custa tanto ao autor do código, quanto ao leitor, pouco trabalho e esforço cognitivo. Dito esse, ainda se trata de comentários desnecessários e que não agregam valor. Comentários explicativos Esse tipo de comentário tende a variar em questão de complexidade. Em certos casos, ele pode explicar ações que desenvolvedores já acostumados com a linguagem em uso entendem facilmente, mas desenvolvedores mais novatos podem ainda não entender. // Retorna o resultado da promise que resolver primeiro const data = await Promise.race([mainServerPromise, secondaryServerPromise]); Esse caso pode trazer algum valor, simplificando a leitura para alguns. Apesar disso, ele ainda polui o projeto de informação que eventualmente pode ser considerada redundante. No exemplo acima, um desenvolvedor que ainda não conheça Promise.race precisa pesquisar e aprender apenas uma vez para que o comentário não agregue mais valor. Além disso, é preciso definir em quantos casos esse comentário será usado. Se a função Promise.race for usada muitas vezes em um módulo, onde você coloca esse comentários e quantas vezes? Assim, esse comentário também se torna indesejado, apesar de também causar um baixo esforço cognitivo. Outra caso em que é possível ver comentários explicativos é como explicação de implementação de algoritmos, ou alguma lógica um pouco mais estruturada. Isso é mais comum em casos em que um CRUD clássico, normalmente relacionado apenas a implementações de regras simples, e integrações, precisa implementar alguma lógica mais estruturada. // exemplo simplista desse caso. func binarySearch(arr []int, target int) int { left, right := 0, len(arr)-1 for left
Comentários podem ajudar você ou seus colegas a entenderem decisões cruciais por trás do design do código de um projeto. Porém, para que isso aconteça, é importante saber diferenciar quando e como comentar, de forma que os seus comentários não se transformem em informação desatualizada ou simplesmente redundante.
Tipos de comentários
Ao ler diferentes comentários em diferentes projetos, é possível notar que alguns "tipos" de comentários podem ser formados, com base no que eles informam e em como são feitos.
Comentários redundantes
Alguns comentários podem ser classificado como redundantes, ou triviais. Normalmente esses comentários repetem o que já é possível inferir pelos nomes das funções chamadas e nomes de variáveis.
// carrega configuração
cfg, err := config.Load()
if err != nil {
return err
}
Esse tipo, apesar de desnecessário, tende a não causar um impacto negativo de grande magnitude. Isso, pois ele custa tanto ao autor do código, quanto ao leitor, pouco trabalho e esforço cognitivo. Dito esse, ainda se trata de comentários desnecessários e que não agregam valor.
Comentários explicativos
Esse tipo de comentário tende a variar em questão de complexidade. Em certos casos, ele pode explicar ações que desenvolvedores já acostumados com a linguagem em uso entendem facilmente, mas desenvolvedores mais novatos podem ainda não entender.
// Retorna o resultado da promise que resolver primeiro
const data = await Promise.race([mainServerPromise, secondaryServerPromise]);
Esse caso pode trazer algum valor, simplificando a leitura para alguns. Apesar disso, ele ainda polui o projeto de informação que eventualmente pode ser considerada redundante. No exemplo acima, um desenvolvedor que ainda não conheça Promise.race
precisa pesquisar e aprender apenas uma vez para que o comentário não agregue mais valor. Além disso, é preciso definir em quantos casos esse comentário será usado. Se a função Promise.race
for usada muitas vezes em um módulo, onde você coloca esse comentários e quantas vezes?
Assim, esse comentário também se torna indesejado, apesar de também causar um baixo esforço cognitivo.
Outra caso em que é possível ver comentários explicativos é como explicação de implementação de algoritmos, ou alguma lógica um pouco mais estruturada. Isso é mais comum em casos em que um CRUD clássico, normalmente relacionado apenas a implementações de regras simples, e integrações, precisa implementar alguma lógica mais estruturada.
// exemplo simplista desse caso.
func binarySearch(arr []int, target int) int {
left, right := 0, len(arr)-1
for left <= right {
mid := left + (right-left)/2 // Calcula o índice do meio
if arr[mid] == target {
return mid
}
// Se target é maior, ignora a metade esquerda
if arr[mid] < target {
left = mid + 1
} else {
// Se target é menor, ignora a metade direita
right = mid - 1
}
}
// target não encontrado
return -1
}
Esse caso tende a agregar mais valor. Além disso, ele serve pra ajudar o leitor a se situar durante a leitura. Por mais que ele seja capaz de identificar a lógica e as etapas envolvidas no algoritmo sem o auxílio de comentários, eles podem ajudar a distinguir visualmente as etapas dele com mais facilidade.
Comentários como documentação de API
Um tipo que tm se popularizado mais, com linguagens com Go e Rust, mas também presentes em linguagens mais antigas como JavaScript (JSDoc) e Java (JavaDoc), é o comentário de documentação de API.
Cada linguagem adota seu padrão de como esses comentários devem ser estruturados, mas eles possuem imenso valor, especialmente quando acoplados com algum gerador automático de documentação. O pkg.go.dev (Go) e crates.io (Rust) são exemplos fantásticos disso. Desenvolvedores de bibliotecas podem contar apenas com as funcionalidades da linguagem para ter uma documentação profunda de suas bibliotecas, sem precisar de desenvolver sites externos (mas alguns ainda optam por isso para terem mais controles sobre o fluxo de sua documentação).
Uma vantagem ainda maior dessa forma de documentação é ela estar próxima ao código. Atualizar a implementação e a documentação torna-se possível com um único commit.
// Exemplo da biblioteca padrão em Go
// Sort sorts a slice of any ordered type in ascending order.
// When sorting floating-point numbers, NaNs are ordered before other values.
func Sort[S ~[]E, E cmp.Ordered](x S) {
n := len(x)
pdqsortOrdered(x, 0, n, bits.Len(uint(n)))
}
Esse tipo de comentário está sujeito à redundância, especialmente com funções mais triviais em pacotes. Apesar disso, ainda é extremamente útil pois a ausência de ressalvas no comentário tranquiliza o leitor, enquanto a ausência de comentários pode não informar muito. Além disso, mesmo com informações redundantes, a presença deles padroniza a documentação da API.
Comentários de design
Dentre os mencionados, esse é o único tipo de comentário que é capaz de adicionar informação que não está contida no próprio código. Essa classe de comentários informa ao leitor a razão de uma implementação, podendo conter contextos técnicos e de negócio, deixando o leitor a par do porquê certa ação foi tomada. Esse tipo de contexto normalmente não se encontra no resto do código e, além de justificar a implementação, ajuda o leitor a entender melhor sobre as diretrizes e objetivos do código.
// Código do pacote http, na biblioteca padrão do Go
// fonte: https://cs.opensource.google/go/go/+/refs/tags/go1.23.5:src/net/http/client.go;l=717
// ...
if err != nil {
// Special case for Go 1 compatibility: return both the response
// and an error if the CheckRedirect function failed.
// See https://golang.org/issue/3795
// The resp.Body has already been closed.
ue := uerr(err)
ue.(*url.Error).URL = loc
return resp, ue
}
//...
O exemplo acima descreve que erro é retornado junto à a resposta caso a função CheckRedirect
falhe por motivos de compatibilidade, evitando quebrar códigos antigos que dependam desse comportamento. Além disso, inclui um link de uma issue relacionada, onde o leitor pode buscar ainda mais contexto por trás da escolha.
Esse tipo de comentário agrega muito valor, deixando o leitor a par das escolhas e até possíveis débitos técnicos. Ele adiciona um contexto valioso que, originalmente, apenas o autor tinha. Dessa forma, leitores futuros conseguem traçar uma forma de histórico, podendo entender problemas aos quais ele deve se atentar ao modificar o código, e tendo um norte para suas edições.
Comentários desatualizados
Tendo isso em vista, vale observar que todos esses tipos de comentários são passíveis de se tornarem desatualizados. Para qualquer tipo, isso pode gerar um trecho de código contra-produtivo, que desinforma o leitor. Dito isso, é algo que acontece mais facilmente com comentários que explicam o código, ao invés de comentários que explicam o porquê (comentários de design). Isso se dá pela própria natureza dos comentários. Aqueles que estão 100% atrelados ao código e apenas reforçam ideias que o próprio código já expressa estão fadados à necessidade de sempre mudarem com o código. Já os comentários de design, mesmo quando desatualizados (a implementação que eles justificavam foi alterada), ainda proveem contexto histórico, por mais que desnecessário.
Dessa forma, observa-se necessário dar atenção aos comentários como parte do processo de commits, e PRs. Sempre que algum trecho de código é alterado, é importante que seja dada atenção aos blocos de comentários relacionados a esse trecho. Isso, claro, é mais fácil quando os comentários são imediatos ao código a que se referem. Dê uma atenção aos comentários durante o seu próximo processo de code review :).
Conclusão
Como regra geral, é sempre interessante comentar o porquê do seu código, dando ao leitor contexto que você tinha durante a autoria do código. Evitar redundância em comentários deixa o seu código limpo e menos confuso, além de manter um fluxo mais legível.
Comentários, assim como programação em si, não seguem regras fixas. Há casos diferentes e sempre contexto a ser considerado, mas algumas diretrizes podem ser adotas para guiar a inserção de comentários.
E não caia no papo de que código bom não precisa de comentários.