Código conciso: claro e breve

poucos anos, com a onda de novas linguagens, surgiram discussões defendendo código com a intenção de ser “conciso”.

Ser conciso é expressar muito, com poucas palavras; ser claro e sucinto; curto, mas com todas as informações necessárias; breve e claro. Note como todas as definições de concisão implicam em ser curto, mas também na clareza da informação. Dois fatores fundamentais para a busca do código de qualidade

Tomemos como exemplo o ato de salvar um usuário no único banco de dados acessado por uma aplicação:

user.saveInTheDatabase(database)

Ele é claro, e chega a ser até mesmo redundante uma vez que “tipifica” o nome do método ao repetir a palavra database. Já o código a seguir mantem a clareza relativa a responsabilidade citada, além de ser sucinto:

user.save

Em uma outra situação, o mesmo código acima pode ser implementado executando outras tarefas, como inserindo outros objetos relativos a um usuário:

def save
  super
  maquinas_a_instalar.each do |instancia|
    cria_maquina_no_banco(instancia)
  end

  # ou qualquer outro tipo de efeito colateral
end

Agora o código anterior, continua sucinto mas já não é mais tão claro:

user.save

Ele não diz que faz o que realmente fará. A busca pelo código curto introduziu a possibilidade de engano daquele que vai ler, pior ainda, o nome do método e a maneira de implementá-lo levam ao erro: ele não é conciso, ele é somente breve.

Por isso alguns usos excessivos de técnicas avançadas, como AOP ou mesmo reflection, precisam de muito cuidado. Remotabilidade transparente, EJBs, JPA, ActiveResource e quaisquer outras bibliotecas que trabalham um serviço de maneira a ocultá-lo podem gerar problemas caso o usuário não tenha um certo conhecimento do efeito de suas chamadas. É o caso do excesso de invocações remotas ou de queries sqls, que os frameworks já citados podem trazer sem um conhecimento mais profundo..

Concisão não significa texto curto. Um dos grandes desafios de um autor é ser conciso, para não cansar seus leitores, mantendo a clareza de seu texto. Algumas linguagens ou até mesmo bibliotecas podem acelerar ou desacelerar o processo de falar algo com poucas palavras, o que não significa necessariamente o código mais fácil de entender e, consequentemente, de manter a longo prazo.

Escrever pouco – a métrica de linhas de código – traz o menor código, mas não necessariamente o mais claro ou melhor.
No exemplo a seguir, o cliente está sendo somente salvo ou algo mais está sendo feito e a “pseudo-concisão” está nos enganando?

cliente.save()

13 Comentários

  1. Fernando 16/06/2011 at 11:35 #

    “No exemplo a seguir, o cliente está sendo salvo em um banco ou algo mais está sendo feito e a “pseudo-concisão” está nos enganando?”

    Isso realmente importa? Não deveríamos estar programando para a abstração? Deveriamos ter métodos saveInDatabase, saveInTextFile,saveInCloud?

  2. Reinaldo 16/06/2011 at 11:52 #

    Esse assunto é bastante interessante !

    No caso desse exemplo específico, ainda temos a “desculpa” de que é um requisito de infraestrutura que estamos atendendo(instâncias do banco, em outros casos replicação, etc…), e geralmente usamos apenas argumentos de negócio para determinar se o método é conciso ou não.

    Mas existem N casos onde colocamos código de negócio em AOP ou outros recursos (before_filter, triggers do hibernate) o que confunde bastante o ato de persistir algo, eu sinceramente nunca achei uma maneira legal de deixar isso claro.

    Alterar o nome do método toda vez que um novo recurso é utilizado, geralmente fica no esquecimento.

    Documentar com testes, geralmente “mockamos” essa parte de AOP pra não atrapalhar a “unidade do teste” e ai fica “sem especificação”, e testes de integração por sua natureza de escopo maior, não testamos todo o código que já é testado unitariamente.

    Sinceramente espero uma resposta bacana aqui nos comentários 😀

  3. Paulo Silveira 16/06/2011 at 11:56 #

    @FernandoAnonimo Creio que o que Guilherme quis dizer nao foi em relacao ao saveBanco, saveCloud, saveNoTXT. Isto é, nao “como” ele faz, e sim “o que” ele faz, que é a discussao interface X implementacao. Seu nome de metodo+documentacao deve deixar claro o que o metodo faz, mas nao como ele faz. Efeitos colaterais, na minha opiniao, fazem sim parte de “”o que” ele faz, e deveriam ser claros para o programador. Obviamente ha uma linha tenue aqui.

    @Reinaldo, tambem concordo com voce nessa parte do AOP, onde alguns chegam ate a colocar logica de negocios… e fica um codigo praticamente invisivel e de dificil tracking, como uma trigger pode ser no caso de tambem conter negocio.

  4. Guilherme Silveira 16/06/2011 at 12:12 #

    Oi @Fernando, você está certo sim. E está de acordo com o que quis dizer. Atualizei a frase para não ficar ambígua. O cliente está sendo salvo, como o método diz, ou muito mais do que isso está acontecendo? (não o como, mas o quê).

  5. Rogério Yokomizo 16/06/2011 at 22:11 #

    Se existe código dentro do método que faz algo além de sua intenção, então acho que existe um problema na definição da intenção desse método, não?

    “O cliente está sendo salvo, como o método diz, ou muito mais do que isso está acontecendo?”

    O que é salvar?

    Acho que essa confusão existe pois “salvar” está presente em diversos níveis de abstração. Se estamos em um nível muito alto de abstração, então acho que o salvar vai fazer mesmo mais do que persistir os dados.

  6. Bruno Tavares 16/06/2011 at 22:21 #

    Muito bom, só que a questão citada continua em aberto. Seria bacana uma sugestão do autor. Deveria ser alterado o nome do método? Talvez complementar o significado com documentação? Abs

  7. Marcio Lucca 17/06/2011 at 10:02 #

    Acho que para conseguir ser realmente conciso é preciso se livrar dos efeitos colaterais. Aí entra o paradigma funcional que vem ganhando mais notoriedade ultimamente.

  8. Guilherme Silveira 17/06/2011 at 11:39 #

    @Bruno com certeza mudar o nome. Uma prática que costumo fazer com o pessoal é dar um nome bem infantil para um método novo que estou criando (desde fazOQueTemQueSerFeito até chocolate) e depois quando tenho o código funcionando passo a entender o que ele realmente faz e não o que eu esperava que ele faria. Ai renomeio para algo que faça sentido.

    No caso que salva o usuário e ao mesmo tempo salva algumas máquinas novas, o nome do método poderia ser algo mais explicito como um simples (e bobo): “gravaComMaquinas”. Esse é um dos motivos para termos cuidado com cascade update.

    Já o ultimo exemplo, onde questiono o que aquele método faz, o nome novo depende justamente do que ele faz. O ponto importante é sempre deixar claro o que ele faz (lembrando o que o Fernando disse, sem precisar deixar explícito “como” ele faz).

    Abraço!

  9. Guilherme Silveira 17/06/2011 at 11:57 #

    @Marcio acho que no funcional ainda podemos executar mais do que estamos falando, mesmo sem efeito colateral. Por exemplo, quando uma função retorna uma estrutura onde duas entradas são novas mas seu nome não deixa isso claro.

    Isso no exemplo de concisão relativo a valores, claro. Acho que ainda existe a questão da concisão em relação a invocações: encadeamentos longos e funções (ou métodos) grandes também dificultam a compreensão, independente de famílida de linguagem de programação. Não citei esse exemplo aqui, mas é o tradicional a.b.c.d em orientação a objetos ou d(c)(b)(a) em funcional.

    Abraço

  10. Márcio 17/06/2011 at 13:50 #

    Num código de negócio, ocultar aspectos e explicitar a regra de negócio, observando o que diz o Reinaldo sobre não ser desejável implementar negócio via AOP.
    Aspectos, em geral, são recorrentes dentro de uma arquitetura e, portanto, fáceis de serem inferidos.
    Concordo plenamente que o código conciso deve dizer O QUE faz e vou além dizendo que ele deve, quando necessário, buscar dizer também PORQUE o faz e, em não raras vezes, REAFIRMAR porque o faz – quem nunca se deparou com uma regra de negócio estranhíssima? Está aí um bom uso de comentários.

  11. Roberto Nogueira 18/06/2011 at 18:11 #

    Clareza do código só pode ser atingido com confiança no que está se fazendo.
    Os problemas vão além da arquitetura, pois quando a regra de negócio é complicada ou mal feita o código tende a seguir o mesmo caminho. Então o programador tem que fazer o trabalho que o cliente não fez: Separation of Concerns. Onde um problema complicado é quebrado em problemas menores que são mais fáceis de entender.
    Por isso acredito esse artigo vem muito de encontro ao conceito Confident Code. Onde um programador não deve ficar a maior parte do tempo escrevendo código defensivo poluído de ” ifs”, enfim se garantindo de qualquer problema que possa aparecer por não ter confiança no cenário que está trabalhando.
    Continua valendo aquela velha frase ‘dividir para conquistar’.

  12. Ricardo MB 18/09/2014 at 12:41 #

    Uma das coisas que aprendi estudando Clean Code, foi que uma função, um método deve fazer uma coisa, e somente uma coisa. “Uma coisa” é algo bastante difícil de determinar, e acho que isso ajuda a criar os problemas como os comentados.
    Uma coisa que me ajuda bastante para nomear métodos e funções é saber qual o escopo onde isso vai ser usado.
    Eu tento seguir a regra que o próprio Clean Code sugere, e tem me ajudado bastante. Para quem quiser saber mais, além do livro tem vário videos interessantes sobre o assunto, e nesse caso em especial, sugiro dedicar um tempo e ver https://cleancoders.com/episode/clean-code-episode-2/show, que fala especificamente sobre nomeclaturas.

Deixe uma resposta