12 janeiro, 2017 3 Comentários AUTOR: elemarjr CATEGORIAS: Sem categoria

Minhas considerações sobre "The Dark Path"

Tempo de leitura: 4 minutos

Uncle Bob é um daqueles autores que provocam reações. Você pode gostar do que ele escreve ou não. Entretanto, dificilmente conseguirá ficar alheio a suas posições.

Este post contem minhas considerações com relação a The Dark Path. Recomendo que você invista algum tempo lendo o que ele escreveu - vale a pena, pode ir, eu espero! Depois, volte aqui e veremos se concordamos...

O que eu entendi?

Uncle Bob parece não gostar do fato de algumas linguagens de programação "forçarem" o programador a codificar de uma determinada maneira. Mais especificamente, ele reclama de linguagens como Swift e Kotlin por exigirem, em tempo de compilação, que:

  • qualquer código que use uma função que possa lançar exceptions faça tratamento adequado.
  • toda classe que possa ser herdada seja declarada como tal.
  • que seja feita uma verificação ao acessar o valor de uma expressão que possa resultar em null.

Segundo Uncle Bob, estas exigências não caberiam a linguagem. Mas, sim, ao desenvolvedor. Ele afirma que estas linguagens, por exemplo, obrigam o programador a fazer análise em demasia para determinar se uma classe poderá ser herdada ou não. Para Uncle Bob, o caminho correto seria garantir correção através de testes e não por features da linguagem.

Ele encerra o post dele com:

Why did the nuclear plant at Chernobyl catch fire, melt down, destroy a small city, and leave a large area uninhabitable? They overrode all the safeties. So don’t depend on safeties to prevent catastrophes. Instead, you’d better get used to writing lots and lots of tests, no matter what language you are using!

Afinal, para que serve uma boa linguagem?

Na minha opinião, uma boa linguagem de programação deve permitir que eu escreva um código expressivo. Ou seja, eu quero que o meu código reflita, da forma mais clara possível, as minhas intenções enquanto eu escrevo o código.

Em dado momento Uncle Bob escreve:

These languages are like the little Dutch boy sticking his fingers in the dike. Every time there’s a new kind of bug, we add a language feature to prevent that kind of bug. And so these languages accumulate more and more fingers in holes in dikes. The problem is, eventually you run out of fingers and toes.

But before you run out of fingers and toes, you have created languages that contain dozens of keywords, hundreds of constraints, a tortuous syntax, and a reference manual that reads like a law book. Indeed, to become an expert in these languages, you must become a language lawyer (a term that was invented during the C++ era.)

Pois bem, na minha opinião ele está sendo um bocado tendencioso aqui. Cabe aos designers da linguagem decidir adicionar keywords que facilitem o código e isso, evidentemente, pode tornar a linguagem mais complexa. Mas, se o trabalho for bem feito, isso poderá levar o código que essa linguagem aceita a outro nível.

Se a linguagem consegue impor, de forma elegante, uma disciplina para tratamento de exceptions, por exemplo, então, por que não se beneficiar disso? Se minha intenção é permitir que exceptions sejam lançadas, por que meu código não deve deixar isso claro?

Algumas palavras sobre C++

C++ é citada, de forma irônica, por Uncle Bob como uma linguagem grande (e complexa).

C++ é uma linguagem bastante antiga. Ela vem passando por revisões constantes e tem conseguido acomodar demandas de diferentes períodos da computação. Com o passar dos anos a linguagem foi ficando "grande" - não nego isso. Entretanto, ela continua firme e forte como alternativa para o desenvolvimento de aplicações que exigem portabilidade e suporte a alta demanda computacional.

Modernamente, você pode programar em C++ conhecendo uma porção bem reduzida da especificação.

C++ é uma linguagem que permite ao desenvolvedor ser absurdamente expressivo quanto a suas intenções. Você pode escrever, por exemplo, um método afirmando que ele não alterará o estado da classe. Isso é útil para você e também para o programador que dará continuidade ao seu projeto.

C++, em uma de suas extensões, permite que você escreva código para rodar na GPU. Usando outra extensão, há suporte para suportar ambiente gerenciado de execução. Isso é feito através de keywords.

Afinal, para que serve o compilador?

O compilador é responsável por converter nosso código fonte em código executável. Um bom compilador irá gerar o código mais eficiente possível em tempo razoável.

Modernamente, um bom compilador também deve ser claro quando há algum erro de programação. Ele deve produzir bons insights sobre o que há de errado e o que deve ser feito para corrigir o problema.

Muitos erros podem ser descobertos em tempo de compilação. Muitos outros só podem ser descobertos em tempo de execução em função de características permissivas das linguagens. Linguagens mais "restritivas" permitem que eu tenha mais ajuda do compilador.

Se um sistema que estou escrevendo pode falhar porque eu esqueci de tratar um null e o compilador tem condições de impedir que isso aconteça, por que isso é uma má ideia?

Afinal, para que servem os testes?

Uncle Bob me ensinou a amar a prática de escrever testes. Em dado momento, ele me levou a acreditar, inclusive, que não é profissional quem não os escreve.

Minha opinião é que os testes devem servir, principalmente, para eu poder expressar os objetivos de negócio relacionados ao código que estou escrevendo. Eles são excelentes mecanismos para que eu possa tornar mais claras as motivações para minhas intenções (e não as intenções propriamente ditas).

Hoje, penso que se estou escrevendo testes para garantir que o meu código está tratando null adequadamente, estou fazendo algo errado. Tenho que garantir que isso seja verificado em uma etapa antes - na etapa de compilação.

Quanto menos código eu preciso escrever, melhor. Logo, se as linguagens me permitem, de forma elegante, escrever menos testes (e menos código), isso é uma coisa boa.

BDUF?

Uncle Bob reclama do fato de você ter de marcar uma classe como open indicando que ela poderá ser herdada. Ele acusa as linguagens de exigirem pensamento exagerado no ato da escrita do código e, provavelmente, necessidade de alterações no futuro. Temos opiniões diferentes!

Quando penso em arquitetura evolutiva e na possibilidade de postergar decisões ao último momento responsável, não vejo que temos um cenário incompleto. Penso que temos uma cena definida mas que precisaria ser fácil de modificar no futuro.

Se não tenho intenções de que uma classe possa ser modificada, devo deixar isso claro no código.

Finalizando...

Gosto e respeito muito Uncle Bob. Mas, acho que, dessa vez, não atingi maturidade suficiente para acompanhar a visão dele.

Ele defende testes. Eu também! A diferença é que ele defende lots and lots of tests, no matter what language you are using. Eu defendo testes em quantidade necessária - nada além disso.

O melhor teste que existe é aquele que não precisa ser escrito.

3 Comentários

  1. Guilherme Oenning 2 meses ago says:

    A minha leitura do post é que ele tem uma quedinha maior por linguagens dinâmicas, apesar de ter falado que gosta de ambos. Ficou um tempo programando em Ruby/Closure e agora que fez algo em Swift/Kotlin, sentiu falta da flexibilidade e versatilidade de uma linguagem dinâmica. Se deparou com uma linguagem mais strict (pelo visto ainda mais do que o Java) e assim saiu o post.

    Tenho escrito bastante código em TypeScript e estou simplesmente adorando. É uma combinação perfeita entre a linguagem estática e a dinâmica, em especial o nullable types que tenho usado MUITO, fica bem explicito quando um método pode retornar null ou não, e quando isto ocorre, você é obrigado a tratar isto, assim como o Swift. Não gosta disto? Ótimo, desabilita esta feature nas opções do compilador. Não quer "tipar" uma parte do seu código? Sem problemas, é só usar o *any* e você tem o dinamismo do JavaScript em suas mãos mais uma vez. É basicamente uma linguagem que permite se adaptar ao gosto do cliente.

    Responder
    • Carlos 2 semanas ago says:

      Acredito que a sua leitura está correta, Guilherme. Pois inclusive alguns meses antes desse post, o Uncle Bob escreveu um sobre a guerra entre tipagem estática e dinâmica, na qual ele categoricamente afirmou: "Se você tem cobertura de testes de 100%, tipagem estática é desnecessária".
      Eu não tenho experiência com Swift/Kotlin, mas conforme eu ia lendo o post, eu não conseguia deixar de pensar: "Cara, como eu adoraria que C# tivesse algumas dessas restrições!"

      Responder
  2. Carlos 2 semanas ago says:

    Eu em geral gosto muito das ideias do Uncle Bob. "Clean Code" foi um livro fundamental para mim, mudou minha forma de programar e pensar a respeito de código. No entanto, às vezes ele é muito radical em certas visões, como por exemplo insistir que quem não usa TDD não é profissional.
    Seu post basicamente expressa tudo o que eu penso sobre esse assunto, e acho que poderia ser resumido com essa frase: 'Linguagens mais "restritivas" permitem que eu tenha mais ajuda do compilador.'
    Imagine a quantidade de erros que seriam evitados se C# tivesse tipos de referência não-nuláveis, por exemplo! Fiquei sabendo que isso quase entrou nas features do C# 7, mas acabou ficando pra próxima, infelizmente.
    Excelente post!

    Responder