Seções iMasters
Agile + Carreira + Desenvolvimento + Tendências

Código bem comentado x Código limpo

Recentemente o Uilton publicou um post (A falsa impressão de um bom código) que gerou considerável polêmica. Como ele abordou a assunto de forma bem ligeira, gostaria de ir um pouco além, pois acredito que esse assunto suscita questões interessantes relacionadas a código-limpo, linguagem ubíqua, código como documentação, etc.

Primeiramente, entendo que o objetivo de adicionarmos comentários ao código-fonte seja esclarecer questões relevantes aos eventuais leitores do código, quando o próprio código per si não consegue ser suficientemente claro. Logo, uma primeira pergunta importante é: quem irá ler esse código? Devo me preocupar com o fato de eventualmente o leitor não conhecer bem a linguagem de programação utilizada? Devo assumir que o leitor conhece o domínio? Existem outros documentos que devem ser lidos antes de navegar pelo código? Ele conhece os frameworks utilizados? Ele conhece a arquitetura adotada? Enfim, um contexto precisaria ser melhor definido para então discutirmos qual seria a melhor abordagem em termos de código limpo e de uso de comentários. Logo, pra simplificar, vou discutir aspectos mais gerais sobre o assunto, ciente que cada caso é um caso.

Teorema 1: Existem trechos de código que são suficientemente claros, e não exigem qualquer comentário.

Óbvio. Caso contrário, teríamos necessariamente um comentário para cada instrução no código. No exemplo abaixo, o comentário me parece totalmente descartável.

    //Obtém a idade atual da pessoa
    public int Pessoa.ObterIdadeAtual()

Agora, introduzindo uma pequena variação, podemos complicar um pouquinho. Digamos que eu queira calcular a idade que a pessoa teria em uma determinada data passada como parâmetro:

    //Obtém a idade que a pessoa teria na data referência fornecida.
    public int Pessoa.ObterIdade (DateTime pDataReferencia)

Esse comentário parece ser útil, pois esclarece como será utilizada a “data referência”. Mas a necessidade (o valor) desse comentário está indicando que o nome do método não é suficientemente claro. Não estou dizendo que sempre conseguiremos dar um nome suficientemente claro. Mas, nesse caso, me parece que um nome mais claro poderia ser dado. Por exemplo:

    //Obtém a idade que a pessoa teria na data referência fornecida.
    public int Pessoa.ObterIdadeNaDataReferencia(DateTime pDataReferencia)

Dependendo da situação, pode ser que seja melhor inclusive mantermos o comentário. Por exemplo, se for uma biblioteca documentada via um “JavaDOC” da vida. Contudo, o comentário é mais um item para receber manutenção, e se ele não agregar valor, por que deveríamos mantê-lo?

Outro cenário comum é o de grandes funções, organizadas em blocos de linhas iniciando com um comentário. Uma função não deveria ser muito grande, em termos de números de linhas, por mais que o negócio seja complexo (ainda mais quando for complexo, hehe). Por outro lado, muitas indireções no código também podem dificultar a legibilidade. Ou seja, os dois extremos podem ser muito ruins. Mas esse tipo de situação (vários blocos começando com um comentário) frequentemente ficam muito mais legíveis se refatorados, substituindo cada bloco por um método com o nome equivalente ao comentário. Nesse caso, talvez não seja mais necessário o comentário. Talvez?

O ponto aqui (e acredito ser o ponto do Uilton) é que um comentário PODE SER um tipo de “caminho mais fácil”, em vez de nos preocuparmos realmente em construir código limpo. O problema é que muitos desenvolvedores acreditam que seu código seja “limpo”, sem realmente parar e olhar criticamente o seu trabalho. Há muitos egos “out there”. Enfim, ler sobre refactoring, clean-code, object-oriented approach, etc pode ser muito útil.

Algumas referências (catei rapidinho aqui no Google):

Mas vamos ao outro lado da moeda.

Teorema 2: Existem códigos que não serão suficientemente claros, por mais que os refatoremos.

Por mais que pra alguns essa afirmativa pareça óbvia, e seja trivialmente aceita, ela não é tão facilmente provada. Provar que existem códigos suficientemente claros é fácil. É só mostrarmos ao menos um. Contudo, provar que um código não pode ser refatorado ao ponto de se tornar claro é um desafio, pois fora a questão subjetiva de quando um código é ou não suficientemente claro, eu teria de explorar todas as implementações (refatorações) possíveis, para provar que não existe nem uma que seja suficientemente clara. E me parece que o universo de possibilidades seja infinito.

Deixando a pretensa formalidade matemática de lado, apresento os seguintes argumentos a favor do Teorema 2:

a) Entendimento do Domínio: o leitor do código frequentemente não tem o mesmo entendimento do domínio de quem o escreveu, quando escreveu. Alias, nós mesmos podemos não lembrar mais todos os detalhes do negócio. Por exemplo, desenvolvemos um sistema de previdência complementar que possui a funcionalidade de “calcular o valor atuarialmente equivalente do benefício”. Veja, eu não conseguiria explicar esse conceito com poucas palavras, quanto mais no nome de um método. Na verdade, o ideal, nesse caso, talvez seja o comentário dar uma ideia geral do conceito e remeter o leitor para um glossário do sistema ou outro documento de análise que contenha uma descrição mais detalhada do conceito e das regras de negócio envolvidas.

b) Linguagem Ubíqua: sistemas que não possuem uma análise de domínio, ou uma ontologia, e que não utilizam linguagem ubíqua, frequentemente nomearão variáveis, métodos e classes utilizando terminologia imprecisa ou ambígua, dificultando a leitura e exigindo esclarecimentos via comentários que poderiam ser evitados se o código fosse mais legível.

c) Nível de Abstração da Linguagem de Programação: além dos comentários que explicam o negócio, alguns programas resolvem escrever comentários explicando o que o código faz, especialmente quando a linguagem de programação é de baixo nível. Um exemplo extremo seria um código em Assembly, hehe. Mas temos coisas desse tipo em linguagens de mais alto nível como C/C++. Um desenvolvedor que não conheça bem C/C++ deve ter alguma dificuldade para entender o que são esses valores abaixo.

Além disso, é  fato que há uma tendência de linguagens mais antigas terem menos mecanismos de abstração, misturando instruções cerimoniais a comandos que refletem as regras de negócio em si, tornando o código mais prolixo e menos legível, exigindo assim mais comentários. Em linguagens modernas, além da orientação a objetos, temos recursos como AOP (aspecto-oriented programming), lambda calculos e características de linguagens dinâmicas, tudo na mesma plataforma. Isso nos permite escrever código muito mais limpo (focado no negócio).

d) Complexidade Algorítmica: já desenvolvi sistemas de informação muito complexos, com muitas regras de negócio, porém com algoritmos relativamente simples, se comparados a outros problemas computacionais (por exemplo, problemas de otimização combinatória da classe NP, algoritmos concorrentes, etc). Quando lidamos com algoritmos mais complexos, há um outro nível de problemas de comunicação. Às vezes, para entendermos um algoritmo de poucas dezenas de linhas, precisamos ler um paper inteiro várias vezes, com toda uma explicação da estratégia do algoritmo. Acho difícil refatorar um código desses de modo a tornar o código tão autoexplicativo ao ponto de o código ser mais claro que o paper. Mas essa classe de algoritmos são casos bem específicos, muito menos frequentes em sistemas de informação.

Enfim, pessoal, há muitas variáveis. Talvez pra alguém que não trabalhe com coisas mais “cutting edge” em termos de plataforma, arquitetura, padrões de projeto, e coisas do gênero, esse papo de “clean-code” pode parecer coisa de outro planeta, e não do “mundo real”. Beleza, eu entendo. Já programei em plataformas “antigas” como Assembly, BASIC, Lisp, C/C++ (pra caramba), T-SQL (idem), Object Pascal (idem), etc, e sei que parte dos truques para se conseguir “clean-code” são bem difíceis de serem aplicados nelas. E com certeza há muito sistema desenvolvido e em desenvolvimento nessas (e outras) plataformas. Mas é importante entender que existe um novo mundo de técnicas, linguagens, arquiteturas, frameworks, que nos permitem fazer coisas bem interessantes em termos de código limpo, com muita qualidade e produtividade.

Pra fechar, o que acham de um desafio de refactoring/clean-code? Segue uma implementação em C do algoritmo que computa um “suffix array” de um dado texto (sarray.c). Apesar de pequeno, esse não é um algoritmo trivial de se compreender. Por isso foi escolhido, hehe. A ideia é ver se alguém consegue apresentar (em qualquer linguagem) uma implementação significativamente mais legível do mesmo. Quem se habilita?

Crédito da imagem: http://butunclebob.com/ArticleS.UncleBob.GreenWristBand

Mensagem do anunciante:

Torne-se um Parceiro de Software Intel®. Filie-se ao Intel® Developer Zone. Intel®Developer Zone

Comente também

5 Comentários

Fabrício, parabéns pelo post, muito bem elaborado. Na minha opnião, mais uma vez ficou provado que nada é tão bom para que seja único, devemos sempre procurar um equilibrio entre qualquer padrão, regra, etc.

Concordo, principalmente quando fala da regra de negócio específica, acho interessante comentamos (em certos momentos) para que tenhamos uma idéia maior de “porque” estamos fazendo aquilo, e não somente “o quê” estamos fazendo.

Diria que você conseguiu expor sua idéia de maneira mais claro do que o Uilton (consegui entender a idéia dele também).
Concordo com basicamente tudo o que você disse. Trabalho como arquiteto de software no desenvolvimento de grandes frameworks. Uma das situações que me impedem muitas vezes de fazer um código limpo é a performance que posso obter em um código “sujo”, nesses casos não há documentação que chega no mundo. Imagine transformar por exemplo o código da jQuery em algo “clean”.
Muita das vezes o tempo que possuímos para escrever ou até mesmo arrumar determinado código é muito curto, saindo dai as aberrações que ninguém entende.

Trak Trak Trugui

Como disse o amigo do primeiro comentário, o exagero nos comentários dificulta mais do que ajuda em certos casos, assim como em uma classe muito complexa a ausência de comentários explicativos muitas vezes dificulta o ententimento.

Muito bom o artigo!! De todo caso, considerando as várias variáveis é algo a pensar muito sobre.

Excelente artigo.
Concordo em 100%.
Parabéns!

Camilo Lopes

fabricio, parabens pelo post. Um dia desses peguei um codigo que só o comentario tinha umas 20 linhas senao mais, quase um livro. eu disse, esse programador nao programou, ele escreveu um livro no formato de comentario, eram tantos que desanimava. Tive meu primeiro contato com a tecnica de refactoring em 2008 e de lá pra ka vi o quanto escrevia codigos que talvez só eu entendesse mais na frente, e realmente sentimos o efeitos de refactoring, qdo vamos dar manutenção é ai que tem o pulo do gato de fato. há um post meu aqui de 2009 sobre o assunto, mostrando alguns code.
http://imasters.com.br/artigo/11096/java/refatoracao-em-codigo-java-na-pratica
abracos,

Qual a sua opinião?