129

Como saber se o código é de fácil leitura e manutenção? Costumo abstrair grande parte dos meus códigos, tenho costume de usar muito funções lambda no C#. Como essa função para validação de CPF.


public bool ValidacaoCPF(int[] cpf)
{
    int DV1 = 0, DV2 = 0, aux = 10;

    if (!cpf.All(x => x == cpf.First())) //Verifica se todos os números são iguais
        if (cpf[9] == ((DV1 = (cpf.Take(9).Sum(x => (x * aux--)) % 11)) < 2 ? 0 : 11 - DV1)) //Cálculo do primeiro digito verificador
            return cpf[10] == (((aux = 11) * 0) + (DV2 = (cpf.Take(10).Sum(x => (x * aux--)) % 11)) < 2 ? 0 : 11 - DV2); //Cálculo do segundo digito verificador

    return false;
}

Nesse exemplo comentei para facilitar quem for realizar leitura ou alteração no código.

  • A solução seria comentar e/ou utilizar a quantidade mínima possível de funções prontas (que agilizam muito desenvolvimento)?
  • Quais outras técnicas podem ser usadas para melhorar a legibilidade do código?
6
  • 2
    Sua dúvida é se deveria encapsular esses 3 códigos comentados, em funções com um nome relevante e chamar essas funções ao invés de colocar o código in line e comentar o que ele faz (como você fez)?
    – Maniero
    Commented 24/04/2014 às 17:42
  • 1
    @bigown Não minha dúvida como saber que um código que você está escrevendo vai ser de fácil manutenção para o próximo programador, as vezes nós programadores nos empolgamos e fazemos coisas que eram pra ficar gigantes em poucas linhas (mas o próximo que for pegar nosso código, pode gastar um tempo grande para conseguir entender 100% da lógica que usamos). Queria saber até que ponto abstrair é bom e viável do ponto de vista mantenabilidade de código.
    – Laerte
    Commented 24/04/2014 às 17:48
  • 1
    entendi, não sei se é fácil responder isso sem recorrer a opinião, mas é uma boa tentativa. O que posso adiantar é que a maioria dos programadores costumam ficar mais confortáveis com estilo mais imperativo, separando mais os passos. Eu já acho que código grande é que é difícil de acompanhar. Mas sei que sou minoria.
    – Maniero
    Commented 24/04/2014 às 17:53
  • 5
    Lá as respostas ajudam pouco, curiosamente tem resposta melhor c/ zero votos s as mais votadas são anti-respostas. Aqui também não começou muito bem, passando pela tangente, mas espero que ainda venha alguma coisa boa.
    – Maniero
    Commented 24/04/2014 às 18:02
  • 1
    Apenas queria deixar os meus agradecimentos a esta questão. Cumprimentos Commented 1/05/2014 às 12:13

7 Respostas 7

136
+100

Primeiramente para ser objetivo tenho que sugerir algo subjetivo.

Trabalho em equipe

Você diz "facilitar quem for mexer no código depois". Esse "quem" é muito importante. Por mais que você tenha uma forma correta de tratar do assunto tecnicamente um aspecto não pode ser deixado de lado. Politicamente você tem que levar em consideração especificamente quem é esse "quem".

Seguir o que sua equipe espera em muitos casos é mais importante que seguir regras, especialmente as regras cegas. Como você espera que um código seja fácil de manutenção se todos que provavelmente mexerão nele não esperam e possivelmente não entendem o que você escreveu, por mais que ele esteja correto?

Fica pior se você nem sabe quem são essas pessoas, ou seja, quando você está pensando em programadores futuros que substituam você no desenvolvimento do software.

Estilo funcional X estilo imperativo

Olhando para seu código, ele tem um problema para a maioria dos programadores. Eu chutaria que 90% dos programadores que possam vir dar manutenção no seu software não entendem bem o estilo funcional de programação. Mesmo os que entendem o uso dos métodos de extensão de coleções para consultas, não entendem totalmente suas implicações (lazy evaluation e side effects por exemplo). E o que vai acontecer quando esse programador for mexer no seu código? Ele provavelmente abandonará o seu e escrever a própria versão dele.

Você pode argumentar que é obrigação do programador saber usar todos os recursos da linguagem. E eu estaria com você nessa. Eu até já pedi demissão de um emprego porque me obrigavam fazer códigos tão simplistas, para o estagiário mais iniciante em programação. Não fui politicamente correto, apenas fiz uma escolha para a minha vida.

O programador comum entende melhor o estilo imperativo, ele entende como o loop funciona. Não adianta dizer que o seu código está obviamente pegando 9 elementos de uma coleção e aplicando uma fórmula a cada um desses elementos e somando todos os resultados. É óbvio para mim como isto funciona, mas não é pra muita gente. Talvez o programador possa até entender, mas não sabe como mudar isso para alcançar um resultado diferente que a manutenção exige. Percebemos isto acontecendo aqui neste mesmo site o tempo todo.

E imagine em um exemplo mais complexo. O seu exemplo é simples e não é tão difícil de entender. Estou considerando que ele é apenas um exemplo curto acertadamente postado assim, mas o problema vai aparecer de verdade em situações mais complicadas.

Em alguns casos pode ser complicado fazer o estilo funcional corretamente já que nem sempre conhecemos a implementação. Em estilo imperativo uma ineficiência seria mais óbvia.

Eu sei que este exemplo não precisa de performance, mas o programador terá dificuldade de melhorar códigos mais complexos por não entender totalmente o que está acontecendo no código.

O estagiário

Então vou lhe responder que dependendo do cenário possível de quem vai dar manutenção este código é de difícil manutenibilidade porque usa recursos que poucas pessoas estão familiarizados suficientemente.

Eu mesmo não seguiria esta regra do mínimo denominador comum, mas esta é uma decisão minha e estou disposto a arcar com as consequências dela.

Nomes de variáveis

Fazendo uma análise específica no seu código (só nas partes que mais importam), afirmo que suas variáveis usam bons nomes.

Sim, variáveis devem ter nomes significativos. E as suas têm. Porque auxiliar é mais legível que aux ou porque primeiroDigitoVerificador é melhor que dv1? (usei minúsculo simplesmente porque esta é a convenção adotada na linguagem).

Quem disse que usar contadorDasVezesQueAlgumaCoisaOcorreEmUmLaço é melhor que i. Muito longo? Eu fiz o que a "regra" manda! Mas mesmo que use só contador ele não ajuda o entendimento mais que i em um loop.

Claro que quanto mais longe da sua declaração uma variável será usada, mais o nome precisa ser significativo. Mas quando ela só será usada em escopo limitado, o nome não é tão importante. Claro, não vá usar nomes aleatórios (conhecia um programador que usava palavrões em todas as variáveis). Você dosou bem.

Usar um nome com significado pode ser útil para dar semântica a um trecho de código e evitar comentário, mas nem sempre isso é tão importante. Comentários devem ser evitados, mas tem que analisar o contexto, é mais errado seguir uma regra cegamente.

Não é curioso que praticamente todo exemplo de código ensinando lambda usa x como argumento? Exemplos escritos por gênios da programação! Será que eles não conhecem os livros que dizem que as variáveis devem ter nomes significativos? Ou será que eles apenas sabem usar a regra da forma correta e não cegamente?

Se você for analisar bem, no seu exemplo se o argumento fosse chamado de elemento ou item seria mais descritivo, mas todo mundo usa x e ninguém que conheça o mecanismo usado deixa de entender que ali é um elemento da coleção.

A lição aqui é para tomar cuidado com regras estabelecidas. Sejam elas ditas por desconhecidos na internet ou por autores consagrados da computação.

Mesmo os melhores mais populares livros foram escritos em contextos que a maioria das pessoas desconhecem, costumam ser vagos quando algo deve ser usado (e tem que ser mesmo, existem tantos cenários) e não costumam explicar como se chegou àquela conclusão (quando explica, não enfatiza isso, enfatiza a regra simplificada que todo mundo vai guardar). Então um livro que parece muito bom e bem intencionado acaba, por falha dos leitores, levando as pessoas cometerem barbaridades, onde a intenção era oposta. E pior, quase ninguém percebe isso acontecendo. Começam usar o livro como muleta para suas atrocidades.

DRY

Pra mim, um dos princípios mais importantes na codificação é o Don´t Repeat Yoursef. Ele prega que, simplificadamente, códigos não devem ser repetidos.

No seu código há repetição. Um trecho pode ser abstraído em uma função e eliminar a repetição.

Quando você tem repetição de código, fica mais difícil dar manutenção. A alteração em um ponto não garante que o outro ponto seja alterado da mesma forma.

Claro que as chances de erro no exemplo postado não são grandes. Mas elas existem.

Este é semelhante ao problema do comentário. É comum o comentário ser repetição, até quando não parece ser. Quando você muda um código e não muda o comentário para refletir a mudança, potencialmente você pode ter uma dissonância. Lembre-se que isto não é uma regra, não estou dizendo que não deva usar comentário.

Note que abstrair em função o código repetido em questão não tornará o código mais conciso, mas fará que ele tenha a sua representação única. DRY não é para tornar código conciso. Concisão nem sempre ajuda a manutenção, ter um ponto único para alterar um algoritmo ajuda a manutenção. Desde que criar esta abstração não seja mais complicada que deixar o código inline.

Eu até admito que chego exagerar no DRY, mas ignorá-lo sempre me trouxe mais problemas de manutenção.

Também não siga o DRY cegamente.

Dicas

(não são regras)

  • peça para outras pessoas analisarem seu código;
  • experimente entender programas antigos seus;
  • tente ler seu código sem ajuda de editores que "enfeitam" o código;
  • conheça todas as regras de codificação e aprenda como não usá-las (como usar é fácil).

Referências

Alguns dos livros que mais falam sobre o assunto são Clean Code e Code Complete. A versão em português não é tão boa.

Eu relutei um pouco em colocá-los porque são um pouco dogmáticos, tentam vender ideias que não funcionam tão bem assim em todas as situações, vendem ideias que parecem absolutas quando não são, e podem criar uma tendência para quem não é experiente e acha que o livro funciona como um manual. Se você não sabe como usar as dicas que os livros dão, corre o risco de se tornar dogmático também.

O primeiro enxerga uma maneira muito específica de desenvolver, praticamente obriga escolher certas ferramentas (não softwares mas paradigmas, metodologias e técnicas) para tudo. Se você souber pegar só o que é mais importante para aplicar nas suas situações poderá aproveitar bem seu conteúdo.

O segundo é um pouco mais amplo nas considerações mas tenta ser específico demais em alguns casos e não contextualiza bem. É comum dar dicas que funcionam bem em uma linguagem e não em outra, mas ele não trata disto.

Observações finais

Você tem ainda um problema de legibilidade por outros fatores, principalmente por causa do ternário (eu adoro este operador) foi difícil ler o código. Parece que você ficou tão contente que conseguiu fazer tudo em uma linha, que esqueceu da legibilidade. Ou não, afinal está aqui perguntando dela :)

Manter muitas linhas de código podem afetar a manutenção também. Concisão bem pensada, e não artificial, é algo bom também. Só não pode exagerar.

Nem sempre quando uma pessoa não consegue entender seu código significa que ele seja ilegível, pode ser que você esteja lidando com um "paraquedista", cada vez mais comum no nosso meio.

Coloquei algumas convenções de nomenclatura nessa resposta.

E tento definir o que é o código limpo. E também código elegante.

Coloquei no GitHub para referência futura.

11
  • 1
    Obrigado pela resposta, realmente gostei dela. Esse código recebe um vetor de inteiros como você mencionou, mas é apenas PoC que criei, um programador amigo meu me desafiou e fiz essa expressão lambda para atender a essa exigência só que durante esse processo eu me deparei com essa pergunta: Legal um código funcional, me poupou vários loops, mas até onde compensa isso? Se os programadores que forem mexer não entenderem a forma que eu pensei?
    – Laerte
    Commented 29/04/2014 às 12:35
  • 1
    O algoritmo do módulo 11 é bem claro de como funciona, creio que o requisito básico para entender meu código é saber um pouco de expressão lambda, então eu fiquei na dúvida programar para todos ou programar para quem está no mesmo nível que eu (entendimento)? Entendi que isso vai do ambiente onde você trabalha (não teria sentido essa pergunta num ambiente onde todos os programadores tem pleno conhecimento das ferramentas que usam).
    – Laerte
    Commented 29/04/2014 às 12:36
  • 3
    Uma solução também seria todos terem a mesmo nivelamento, pois se for escrever código sempre preocupado com o próximo não entender (pelo nível de abstração usado) acaba limitando o programador, pois fica acumulando conhecimento que não é posto em prática.
    – Laerte
    Commented 29/04/2014 às 12:37
  • 3
    E já que toquei nesse assunto, uma "regra do dedão" que li uma vez e tento sempre usar é só abstrair algo que se repetiu ao menos 3 vezes. Da mesma forma que código repetido me trouxe dor-de-cabeça no passado, também já gastei horas tentando "generalizar" algo que acabei por usar somente uma vez...
    – mgibsonbr
    Commented 29/04/2014 às 14:21
  • 4
    @mgibsonbr Olha quem fala de excelente resposta :) Não digo que não deva haver comentários. Sempre há o perigo das pessoas interpretarem como regra de ouro, claro. É só um motivo p/ se pensar em minimizar seu uso. E sobre o final, isso é verdade, eliminar repetição é uma coisa, generalizar é outra. Nem sempre compensa. Só não gosto de seguir regras. Gosto de conhecê-las (acho que isso é o mais importante sobre regras). Eu posso abstrair algo que não se repete e em casos extremos não fazer isso mesmo que tenha 4, 5 repetições. Mas entendi o que você disse. Ainda vou editar algumas coisas.
    – Maniero
    Commented 29/04/2014 às 14:29
48

1. Concisão

Via de regra, quanto menor o código melhor. Sua decisão de usar funções de nível mais alto é, na minha opinião, acertada: ao mesmo tempo que fica claro o que seus comandos fazem de modo geral (All: verifica se todo elemento da lista satisfaz um predicado; Sum: realiza um somatório), os detalhes específicos são expressos da forma mais simples possível (os lambdas, dizendo qual é o teste a ser aplicado a cada elemento individual, e o que está sendo somado em função dos dígitos individuais).

Este artigo do Paul Graham (em inglês) expressa bem as vantagens da concisão, bem como seu impacto na legibilidade. Por exemplo, seria um loop explícito mais legível que o uso de lambdas? Talvez, mas o que importa não é a legibilidade de uma única linha de código - e sim de um programa como um todo. Traduzindo livremente do artigo:

Se você está acostumado a ler romances e artigos de jornal, sua primeira experiência lendo uma publicação matemática pode ser desanimadora. Você pode levar meia hora para ler uma única página. Ainda assim, tenho certeza que a notação não é o problema, ainda que pareça ser. A publicação é difícil de ler porque as ideias são difíceis. Se você expressasse as mesmas ideias em prosa (como os matemáticos precisavam fazer antes de criarem notações sucintas), elas não seriam nem um pouco mais fáceis de ler, pois a publicação cresceria até o tamanho de um livro.

O mesmo ocorrem em linguagens de programação: à primeira vista, uma expressão regular pode parecer um xingamento de personagem de quadrinhos, mas [a menos que ela seja demasiado grande] ela expressa de forma sucinta algo que demandaria linhas e mais linhas de código para ser realizado de outra forma. Linguagens densas, como J, também parecem difíceis de ler à primeira vista, porém os programas resultantes possuem muitíssimo menos linhas que aqueles expressos numa linguagem tradicional (mesmo as funcionais).

2. Apresentação

Entretanto, uma ressalva: concisão não significa "menos linhas de código" (ex.: "one-liners" gigantescos) nem "variáveis com nome mais curto" - isso só atrapalha a legibilidade, em vez de melhorá-la. Em vez disso, ela se refere ao número de elementos lógicos que compõem seu programa. Citando novamente o artigo linkado:

Creio que uma medida melhor para o tamanho de um programa seria seu número de elementos, onde um elemento é qualquer coisa que seria um nó distinto se você desenhasse uma árvore representando o código-fonte. O nome de uma variável ou função é um elemento; um inteiro ou número em ponto flutuante é um elemento; um segmento de texto literal é um elemento; um elemento de um padrão, ou diretiva de formatação, é um elemento; um novo bloco é um elemento. Há casos de fronteira (-5 é um ou dois elementos?) mas acho que a maioria deles é o mesmo para toda linguagem, de modo que não afetam muito a comparação.

Essa métrica precisa de mais detalhamento, e pode ser sujeita a interpretação no caso de linguagens específicas, mas creio que ela tenta medir a coisa certa, que é o número de partes que um programa tem. Acho que a árvore que você desenharia nesse exercício é a mesma que você teria que fazer em sua cabeça de modo a conceber o programa, então seu tamanho é proporcional à quantidade de esforço que você teria para escrevê-lo ou lê-lo.

Ou seja, ainda que o número de elementos seja o mesmo, a forma de dispô-lo em linhas, o uso de espaços em branco, etc podem afetar muito a legibilidade. Exemplo:

if (!cpf.All(x => x == cpf.First())) //Verifica se todos os números são iguais

    //Cálculo do primeiro digito verificador
    if (cpf[9] == (
        (DV1 = (
            cpf.Take(9).Sum(x => (x * aux--)) % 11
        )) < 2 ? 0 : 11 - DV1
    )) 
    {
        //Cálculo do segundo digito verificador
        aux = 11;
        return cpf[10] == (
          (DV2 = (  cpf.Take(10).Sum(x => (x * aux--)) % 11  )) < 2 ? 
          0 : 
          11 - DV2);
    }

Note que eu não mexi em nada no código do seu programa, exceto pela colocação dos espaços em branco e quebras de linha [e mudei o comentário de lugar, para se adequar]. Fiz isso de duas formas diferentes. Qual das três [a original e as minhas] é mais legível é discutível, pessoalmente considero a original tão fácil quanto as demais de entender (não tive dificuldade de enxergar a lógica ao ler seu código original) porém não tão fácil de depurar (são tantos parênteses que demorei pra descobrir o que fechava o quê).

3. Separação de Responsabilidades

Assim como uma função (ou uma classe) não deve fazer mais de uma coisa ao mesmo tempo, uma linha de código que faz isso é mais difícil de ler que duas que fazem cada uma uma coisa só. Um exemplo que considero bastante positivo é o jQuery, que ao mesmo tempo que permite encadeamento de métodos (method chaining) encoraja o programador a colocar cada invocação em uma linha separada:

$(seletor)
    .hide()
    .children(subseletor)
        .addClass(classe)
        .show()
        .end()
    .css(...)
    .show();

Embora a princípio isso seja um one-liner que mistura várias responsabilidades, a forma de apresentação torna bastante claro o que está sendo feito, e facilita inclusive a alteração - no sentido de adicionar linhas ou remover linhas (em vez de mexer no meio de uma linha). Nem sempre esse estilo de codificação é possível, e na ausência do mesmo eu sugiro transferir responsabilidades múltiplas para diferentes instruções:

if (!cpf.All(x => x == cpf.First())) //Verifica se todos os números são iguais
{
    //Cálculo do primeiro digito verificador
    DV1 = cpf.Take(9).Sum(x => (x * aux--)) % 11;
    if ( cpf[9] == (DV1 < 2 ? 0 : 11 - DV1) ) 
    {
        //Cálculo do segundo digito verificador
        aux = 11;
        DV2 = cpf.Take(10).Sum(x => (x * aux--)) % 11;
        return cpf[10] == (DV2 < 2 ? 0 : 11 - DV2);
    }
}

Note que, ao separar o cálculo de DV1 e DV2 do seu uso, pude também eliminar vários parênteses desnecessários - o que por sua vez permitiu a redução do número de quebras de linha e espaços em branco, aumentando de novo a concisão [no sentido mais geral].

4. Nível de Abstração

É importante para a legibilidade que uma função/subrotina opere sempre no mesmo nível de abstração. Por exemplo, se uma função precisa de todas as linhas de um arquivo, o ideal é criar uma função separada para abrir o arquivo, ler linha por linha e retornar em uma coleção ou iterador/gerador [se sua linguagem já não faz isso]. Caso contrário, teríamos um raciocínio em alto nível "quebrado" pela presença de um trecho de código em baixo nível. Na minha opinião, mais do que a concisão esse é o motivo principal pelo qual achei seu uso de All, Take e Sum acertado - você não "poluiu" o raciocínio de "como calcular os dígitos do CPF" com detalhes de "como fazer a soma de uma lista", etc.

Só é preciso ter cuidado para não criar níveis demais: se uma função chama muitas funções externas, entender o que o programa faz exige parar de ler a função principal para consultar cada função externa, depois voltar aonde se parou na leitura. Só é bom quebrar uma função em funções menores se cada uma delas estiver num nível de abstração mais baixo (ver essa resposta para um exemplo). Nesse ponto minha opinião é contrária à recomendação geral de "evitar funções muito longas" - eu diria que se uma função possui uma única responsabilidade e opera no mesmo nível de abstração, ela pode ser longa o quanto for necessário sem prejudicar a legibilidade.

5. Comentários

Uma recomendação que sempre leio é de não "narrar o programa" nos comentários. Por exemplo, se seu primeiro comentário fosse:

if (!cpf.All(x => x == cpf.First())) //Verifica se todos os números são iguais ao primeiro

ele seria um comentário inútil - basta ler o código para entender que o programa faz isso! Omitindo "ao primeiro" fica um pouco melhor, dando a entender que "todos os dígitos iguais" são um caso "interessante", mas sem esclarecer bem por que. Um pouquinho melhor seria "Se todos os dígitos são iguais, o CPF é inválido". Os demais comentários estão OK [explicam em alto nível o que o trecho de código faz].

No mais, é bom usar comentários para chamar a atenção a casos particulares ou "inusitados". Por exemplo, se você está implementando um algoritmo clássico, e por alguma necessidade particular você precisa variar um detalhe na implementação ou embutir uma operação/verificação apenas nesse caso, um comentário explicando por que aquela linha/trecho existe pode ser interessante - ainda que o restante do algoritmo inteiro não esteja comentado. Como apontado por Pedro, o código deve [idealmente] ser auto-explicativo, somente a intenção por trás de um trecho de código é que deve ser esclarecida (o objetivo do código deve estar na sua documentação, me refiro a explicar como um certo trecho auxilia na realização desse objetivo).

E se a intenção do seu código não é aparente dada sua representação (ex.: você está usando uma linguagem de baixo nível, ou a sua forma de representar os dados é pouco usual) então ajuda bastante ser generoso nos comentários. Para cada instrução de baixo nível, comente em alto nível o que ela faz. Vou dar um exemplo de um código que escrevi há um tempo - implementando uma tabela hash em JavaScript (tratando colisões por encadeamento):

var toSet = this[':' + key.hash];
if ( toSet === undefined ) { // No key found, create one
    this['::' + key.hash] = key;
    this[':' + key.hash] = value;
}
else if ( toSet instanceof CompoundField ) // Multiple keys found, select the right one (or add)
    toSet.field(key, value);
else if ( this['::' + key.hash].match(key) ) // Same key found, update the value
    this[':' + key.hash] = value;
else { // Single key found, but not the right one; split into multiple keys
    this[':' + key.hash] = new CompoundField(this['::' + key.hash], toSet, key, value);
    delete this['::' + key.hash];
}

Há um local na minha documentação onde está bem explicado como essa representação funciona (e sem lê-la, é provável que ninguém entenda 100% o código acima), mas como ela não é nada abstrata - e portanto nem um pouco auto-explicativa - os comentários servem como indicações de "o quê" o código está fazendo. Assim como no seu caso, um comentário do tipo "Cálculo do primeiro digito verificador" é uma explicação em alto nível do que o código faz, e não de como o faz.

1
  • +1 Quem cita o Paul Graham não pode estar errado :P Falando sério, é uma boa resposta, por indicar que o real problema não é a questão imperativo x funcional, e sim a legibilidade.
    – rsenna
    Commented 29/04/2014 às 15:07
29

Existem diversas técnicas para melhorar a legibilidade do código, e em alguns casos isso pode ser até relativo.

Um livro muito bom sobre o assunto é Código Limpo (Clean Code) do Uncle Bob.

Essas duas práticas são simples e ajudam bastante na legibilidade do código:

  • Métodos pequenos e que executam apenas uma tarefa
  • Variáveis com nomes auto explicativos

Sobre comentar o código, alguns programadores consideram má prática, pois o código deveria ser auto explicativo, mas em algumas situações é difícil de escrever um código que não precise de um comentário (muitas vezes por uso de linguagens e API's verbosas)

No seu caso eu transformaria essas condicionais que estão nos IFs em duas variáveis booleanas e o nome delas seria auto explicativo.

Usar muitos comandos aninhados e if's ternários podem tornar a legibilidade muito difícil, nem sempre o código menor é mais legível e elegante.

2
  • 3
    Adoro esse livro. De acordo com ele a qualidade de um código é medida pela quantidade de palavrões que seus colegas dirão durante uma revisão de trabalho. Commented 28/04/2014 às 17:23
  • +1 "Se um código tem um comentário a mais, provavelmente tem um método a menos". Um bloco de código comentado potencialmente pode virar um novo método, com um nome auto-explicativo, tornando o comentário desnecessário.
    – rsenna
    Commented 29/04/2014 às 15:05
20

A pergunta é quais outras técnicas podem ser usadas para melhorar a legibilidade um código?

No meu ponto de vista pode ser uma conjugação de tudo que já foi dito. Todas são válidas. Acho que esses são os principais pontos:

  • Tentar separar os blocos de código por funcionalidades;
  • Se necessário comentar o código, mais vale um comentário aqui e ali do que dois meses a tentar perceber o código;
  • Se esse bloco for demasiado extenso, ou fora do âmbito da função que pretendemos criar, separa-lo numa função auxiliar;
  • Dar nomes sugestivos a funções e a variáveis;
  • Tentar não repetir código (copy/paste), que torna-se muito complicado quando se quer alterar alguma coisa em algum ponto;

Por fim, quem tem a tendência para ser minimalista a codificar, acho que deveriam ter o cuidado de comentar o que estão a fazer. Acho que qualquer uma das formas é boa, desde que se consiga fazer com que o código seja legível, acho que é o mais importante. Mais vale ter cuidados a mais do que a menos.

2
  • 2
    +1 pelo "Tentar não repetir código (copy/paste)", uma vez trabalhei com um programador que preferia criar cópias das minhas funções "pra não correr o risco de me atrapalhar/quebrar o meu código". A intenção podia ser boa, mas na hora de mudar alguma coisa era um inferno: várias cópias da função precisando da mesma alteração, e se esquecesse uma boa sorte pra depurar depois...
    – mgibsonbr
    Commented 29/04/2014 às 8:52
  • Eu tive de pegar num código já feito tudo replicado em todo lado e ainda estou a sofrer com isso, e já lá vão quase dois anos. Sempre que é preciso alterar alguma coisa é um castigo. Eu quando vou tendo tempo, vou limpando as copias.
    – Jorge B.
    Commented 29/04/2014 às 8:58
15

Prefiro utilizar nomes de variáveis e de funções completas mesmo que fique um pouco extenso, mas ajuda na leitura do código, diminuindo a necessidade de comentários pois o código é auto explicativo.

Ex:

int totalAlunosPresentes = TotalizarAlunosPresentesNaSala(IdSala);

Sobre criar funções, separe os blocos do seu código que fazem uma função especifica, fazendo esse conjunto de funções individuais interagirem formando o resultado que você precisa e possibilitando a reutilização destas funções se necessário.

Deixando bem claro que essa é a minha opinião, baseada nas experiencias e leituras já tive.

1
  • 1
    "separe os blocos do seu código que fazem uma função especifica, fazendo esse conjunto de funções individuais interagirem formando o resultado que você precisa" Concordo, desde que as partes estejam num nível de abstração mais baixo. Ler uma função tendo toda hora que parar para ler uma função diferente é trabalhoso, já tive que trabalhar com códigos desse tipo e digo que não é nada fácil. O problema não é "separar tudo", e sim quando umas coisas são separadas e outras não (i.e. o mesmo nível de abstração fica "pulverizado" entre diferentes funções).
    – mgibsonbr
    Commented 29/04/2014 às 8:59
12

Em primeiro lugar, você deve "indentar", a indentação consiste em organizar categorias, códigos e elementos dos códigos fontes de qualquer linguagem. Para isso, você tem que organizar os códigos, linhas e usar comentários para auxiliar você na manutenção do software. Veja um exemplo em C++:

Código em C++ não indentado:

#include <iostream>
int main()
{
int x;
x = 1;
cout << x << "\n";
}

Código indentado:

#include <iostream> // Inclui uma biblioteca para entrada e saída de dados

int main() // Função principal
{
     // Declaração de variável:
     int x = 1; // "x" recebe 1

     // Mensagem:
     cout << "x é igual a " << x << "\n";
}

Qual desses você achou melhor para ler? Com certeza é o ultimo, pois explica para que que serve os comandos e está organizado. Por exemplo, você sabe que o comando cout está dentro da função int main().

11

Segue alguns apontamentos que explicitei de algumas pesquisas e de convivência do dia-a-dia:

  • Reduzir complexidade: Crie uma rotina e automaticamente você está ocultando parte do código em um único lugar. Isso torna seu código menos complexo, pois, a leitura e entendimento são mais fáceis. Sem contar na manutenção.

  • Evitar código duplicado: Imagine o código repetido em várias partes do sistema. Com a utilização das rotinas você evita a duplicação de código e ainda economiza no tamanho do sistema.

  • Simplificar testes booleanos complexos: Desenvolver rotinas que testem as validações necessárias para executar algum propósito ajuda muito no entendimento do código.

  • Melhorar desempenho: Na manutenção é muito mais fácil mudar em um único lugar e resolver o problema do que mudar em vários outros locais e ainda ter a possibilidade de esquecer algum local.

Mesmo operações simples devem ser convertidas em rotinas.

Muita gente acha que por uma operação ser simples (duas ou três linhas de código) não precisa ser refatorada e acabam copiando o bloco de código em varias partes do sistema. O que os programadores devem saber é que uma rotina simples torna o código simples e legível.

Saiba escolher um bom nome para sua rotina

  1. Torne o código simples, legível e pequeno.
  2. Perca tempo e saiba escolher um bom nome para suas rotinas.

fonte da pesquisa: linhadecodigo

Você deve fazer log-in para responder a esta pergunta.

Esta não é a resposta que você está procurando? Pesquise outras perguntas com a tag .