Categories
Engenharia de software Testes

Programadores de Schrödinger

Donald Knuth deu uma entrevista para o InformIt esses dias que deu o que falar!

Como o Paulo Silveira comentou no GUJ, Knuth tem algumas opiniões controversas e polêmicas sobre testes unitários, eXtreme Programming, Open Source e programação concorrente. O Phillip Calçado também comentou o assunto, referindo-se a opinião do Keith Braithwaite, que tem ótimos pontos.

Sobre testes unitários em particular, Knuth fala o seguinte:

[…] the idea of immediate compilation and “unit tests” appeals to me only rarely, when I’m feeling my way in a totally unknown environment and need feedback about what works and what doesn’t. Otherwise, lots of time is wasted on activities that I simply never need to perform or even think about. Nothing needs to be “mocked up.”

Basicamente ele parece não acreditar em testes unitários.

O problema é que hoje em dia, além da complexidade da programação em sí, é necessário lidar com a complexidade do domínio do software, com o fato de que as empresas devem responder rapidamente às mudanças (e o software deve acompanhar), o fato de que não existem “desenvolvedores solo” mas sim equipes de desenvolvimento e por aí vai. Por isso é essencial que os softwares sejam bem testados e que esses testes sejam executados constantemente para garantir o bom funcionamento do sistema ao longo dos incrementos que serão feitos durante seu ciclo de vida. Mesmo com todas essas precauções, como bem disse o Phillip, se nos dias de hoje alguém ou alguma empresa pagasse $2.56 por cada bug encontrado nos seus programas (como faz o Knuth), provavelmente já estaria falida. Já imaginou então se não houvessem os testes como que seria?

Convenhamos, o caso do Knuth é um caso a parte. Ele está na ativa desde os anos 60 quando nossos pais ainda eram adolescentes, e possivelmente por isso tem práticas e opiniões que não necessariamente se aplicam às situações de hoje. É claro que ele merece todo o respeito por tudo que ele fez e faz pela computação, mas não acho que a opinião dele sobre práticas ágeis em especial possa ser levada em consideração ao pé da letra. E o que mais me preocupa nisso tudo é que, por ele ser um ícone da computação, as pessoas tomem tudo que ele falou como verdade absoluta e comecem a achar que testes unitários não servem para nada, quando na verdade eles são essenciais para qualquer programador profissional!

Para mim, os programadores que não testam poderiam ser chamados de Programadores de Schrödinger.

Falo isso por causa da teoria do Gato de Shrödinger. Resumindo a história, o físico Erwin Schrödinger uma vez sugeriu que se puséssemos um gato numa caixa fechada, onde a vida do gato dependesse do estado de uma partícula sub-atômica, haveria uma superposição de estados que faria com que, do ponto de vista da mecânica quântica, o gato estivesse vivo e morto ao mesmo tempo até que a caixa fosse aberta. Seria impossível determinar o seu estado até abrir a caixa! Na minha imaginação eu fico pensando no momento que a caixa é aberta e aparece um gato vivo (ou morto) o que aconteceu com o outro gato… Provavelmente ele está em algum outro universo paralelo onde todas as coisas são ao contrário (e as pessoas acham guarda-chuvas ao invés de perdê-los). Enfim, não se sabe de forma alguma o que pode acontecer.

Finalizando toda essa viagem, os Programadores de Schrödinger simplesmente não sabem o que vai acontecer quando o seu sistema entrar em produção. Pode ser que nenhum bug apareça e que eles fiquem ricos e comprem milhares de jatos particulares. Ou não.

Na dúvida, eu escrevo testes. 🙂

22 replies on “Programadores de Schrödinger”

De fato é uma boa analogia 🙂 Vou abusar um pouco das palavras no contexto da física quântica: poderíamos dizer que os programadores que escrevem testes são os que acreditam no Princípio da Incerteza de Heisenberg aplicado ao domínio de desenvolvimento de software 🙂

Você trabalha em cima das probabilidades, então programadores profissionais escrevem testes para lidar melhor com o princípio da incerteza, reduzindo bugs 😉

Fantástico. Isso me lembrou uma outra situação que você mencionou outra vez sobre o que aconteceria se passassemos manteiga em um gato, onde você sugeriu que era essa a tecnologia que fazia com que o trem bala flutuasse.

Ah…só um addendo: não precisa nem ir muito longe: na URSS, o guarda-chuva acha você!

Hahahahaha.

Sobre a história do gato e da torrada com manteiga, é assim:

When a cat is dropped, it always lands on its feet, and when toast is dropped, it always lands buttered side down. Therefore, if a slice of toast is strapped to a cat’s back, buttered side up, and the animal is then dropped, the two opposing forces will cause it to hover, spinning inches above the ground. If enough toast-laden felines were used, they could form the basis of a high-speed monorail system. (http://www.begent.org/toast.htm)

O problema com testes unitários é que tem muita gente que essa é a única (ou melhor) maneira de se testar software. Knuth não disse que não testa. Não fazer TDD não significa que você não testa. Existem mais de uma maneira de se obter o mesmo resultado.

Guilherme, acho que ter pensamento critico sobre o que dizem – mesmo quando se trata de alguém como o Knuth – é o que realmente importa. Então, ok. Ele não escreve testes. Isso se aplica ao meu trabalho? Eu desenvolvo software nas mesmas condições do Knuth?

Bom, eu não desenvolvo, então, não pensar em testes unitário para mim e para minha equipe é um erro.

Haha! Muito bom 🙂 [fiquei rindo durante muito tempo]

Mas, levando a sério o princípio da incerteza para software, implica em dizer que nunca poderemos saber todo o estado do sistema – e, inclusive, a própria medição já afetaria o seu estado :p

E, [som de suspense] será que o seu software realmente existe quando ele está rodando em um datacenter abandonado e desconectado de tudo? Na verdade, um bug só existe quando um humano observa o log do xunit e força as ondas de probabilidade colapsarem (de forma desfavorável às vezes :p).

Marcos,

O meu ponto é que TDD e testes unitários não são a única forma de se testar software. Acho fundamental testar claro. Agora eu não sou muito fã da idéia de escrever primeiro os testes, depois código e escrever apenas código suficiente para passar nos testes. Para linha de produção e monkey coders eu acho ótimo, mas para mim não serve.

Eu geralmente abuso do REPL (quando estou programando em alguma linguagem que permite isso). Uso ele para fazer pequenos testes e experimentações enquanto desenvolvo, e o que acho mais importante coloco em rotinas de teste (que não são testes unitários, são coisas um pouco maiores).

Gosto muito da idéia de contrato e de testes gerados automaticamente tomando como base esses contratos, o que funciona muito bem com linguagens funcionais. Veja o QuickCheck como um exemplo disso, ou o sistema de contratos do PLT Scheme.

Outra coisa que eu acho útil, e que uso no dia a dia, são scripts de testes funcionais, que geralmente é possível escrever numa linguagem mais fácil. Faço scripts em Ruby e Python para testar meus códigos C++.

Quanto ao desenvolvimento colaborativo eu acho muito importante fazer code review, mas parece que as pessoas costumam depositar toda sua fé testes unitários mesmo, achando que TDD é a bala de prata.

Em suma, eu raramente faço testes unitários, não sou adepto de TDD e testo sim os meus programas.

Entendo o Lucindo. Realmente, para determinados tipos de linguagens, pode fazer sentido testar de outra forma que não seja com testes unitários. Em Java, por exemplo, faz total sentido usar testes unitários (com TDD ou não), mock objects e etc.

A questão é que é interessante testar a menor quantidade de componentes de cada vez para isolar os problemas, e que a execução desses testes seja automatizada.

E por último, TDD não eh um técnica para “code monkeys”. Nesse ponto, Lucindo, vc está totalmente equivocado. O que eu vejo é justamente o oposto: os programadores mais experientes é que usam mais e melhor a técnica, lembrando que o objetivo maior do TDD não é o teste em sí mas influenciar positivamente no design das classes.

[ ]s, gc

De qualquer forma, não podemos comparar o que faz um Donald Knuth, com um programador normal. Ele pode tudo! Ele é o Knuth!! 🙂 O dia que chegarmos a ser um Knuth talvez possamos deixar de usar testes também, mas até lá é melhor usar testes. Nunca vou esquecer como fiquei perplexo quando folheei o The Art of Computer Programming, 15 anos atrás. Preciso confessar que eu não consigo ler tudo aquilo e ainda entender cada passo. Me falta o talento matemático para isso.

Alguém já disse isso mas por outro lado o Knuth não trabalha – que eu saiba – em ambientes enterprise, prazos apertados, escopos absurdos, clientes babacas. Ele é um acadêmico, estuda seus algoritmos com precisão matemática, ele literalmente pode fazer um Big Up Front Design de tudo e construir pedaço a pedaço com precisão cirúrgica. São coisas que não temos o luxo de fazer.

Olá Guilherme.

Talvez o Lucindo tenha falado isso porque diversos defensores do TDD comentam que um programador júnior pode se “tornar” um experto ao realizar testes unitários, melhorando a qualidade de seu software.
Eu uso práticas do TDD dependendo do projeto, mas como o Lucindo falou, não achou que seja a única maneira de testar.

Sobre o Knuth, ele é um caso à parte. Possui uma abstração muito alta, mente científica, QI alto, e uma visão matemática (Talvez seja um gênio). Pelos caminhos matemáticos, sabemos que é mais fácil chegar ao provável do que ao improvável, como na física. Uma pessoa que pensa em algorítimo como abstrações matemáticas, onde os corolários são pontuais e factíveis, deve pensar em testes implícitos no próprio algorítimo. 🙂 Para quem leu algum dos livros “The Art of Computing Programming”, deve ter percebido isso.

[]’s,

Jônatas

GC,

Estava justamente discutindo TDD ontem com o Paulo no MSN. Quando leio algo como as 3 regras de TDD do uncle Bob eu não consigo pensar que não seja algo para disciplinar monkey coders.

Quanto a influenciar o design, forçando um desacoplamento baixo, eu tenho minhas dúvidas. Pode ajudar? Talvez. É uma solução? Talvez. A única? De maneira alguma. Um bom design tem a ver com quem desenha e não com a técnica que usa. O que eu vejo é que para projetos pequenos, CRUDs e afins funciona muito bem, o que força ainda mais o meu ponto de linha de produção.

Agora, projetos grandes e complexos o que se acaba gerando é um código que é completamento oposto do spaghetti e igualmente difícil de se entender, onde é impossível não se perder na rio de camadas e classes que não fazem nada a não ser delegar, onde o código de verdade fica todo escondido.

Mas o ponto de tudo isso é: TDD/Unit tests não é única maneira de se testar o que se faz.

Discordo dessa teoria do spaghetti aí. Se você pegar um projeto open-source grande com uma grande base de testes e vai ver que a diferença é notável para projetos grandes e sem testes. Esse spaghetti ao contrário que você fala não depende de usar ou não TDD, mas sim do talento e organização dos programadores. E note que eu não estou falando de um projeto de CRUDs, mas de um projeto para qualquer coisa.

Resumindo, não podemos misturar as coisas. O efeito Spaghetti pode acontecer em qualquer situação independente do propósito do sistema, técnicas e linguagens utilizadas.

Condordo com o seu ponto de que teste unitário não é a única forma válida de testar (já tinha concordado antes), mas para certas linguagens e situações não tem como fugir…

E por último, é perfeitamente possível desenvolver sistemas sem uma linha de código de testes, com baixíssimo acoplamento e sem usar TDD. Só que vai ser bem mais difícil 😀

“There´s no silver bullet.” Eu não defendo que TDD ou testes unitários são silver bullets, mas na maioria dos casos é uma ótima prática.

[ ]s, gc

Concordo em parte, nem necessariamente com o Knuth. Apesar de eu advogar o uso de unittests, XP, as coisas estão indo longe demais.

Por exemplo, muito tempo acaba sendo perdido em ambientes em que exige-se do desenvolvedor rodar toda uma suite de testes antes de qualquer committ do código, em casos em que é óbvio e cristalino que não pode haver nada. Hoje mesmo alguém me comentou que rodou testes depois de corrigir documentação.

TDD é uma prática essencial para encontrar erros em linguagens mais dinâmicas como Python e Ruby para um momento mais próximo do desenvolvimento, como aquelas linguagens cheias de restrições, feitas para as “senzalas” de software, como Java.

@Pedro Werneck

1) Se a sua suite de testes demora muito pra rodar, o problema está em quem desenvolveu a suite e não nos testes em sí. Uma das premissas da técnica é que os testes rodem MUITO rápido justamente para que possam ser executados muitas vezes sem onerar o desenvolvimento.

2) Se algum desenvolvedor do seu time rodou a suite de testes após alterar a documentação, vocês estão com um sério problema de falta de capacitação profissional. Novamente o problema não está nos testes, mas sim no desenvolvedor. Além do mais, rodar testes após os commits é trabalho do Cruise Control.

3) Python, Ruby e Java requerem unit tests, com ou sem TDD. Não sei o que você quis dizer com “senzala do software” e nem com essa última frase que ficou muito confusa, mas acho que você está precisando se atualizar.

[ ]s, gc

A Questão do TDD é que ele te dah pelo menos mais uma forma de pensar no problema, algo que sem o mesmo talvez saísse uma solução “engessada” na primeira vez que a fizermos ( e pelo menos 99% das vezes acontece isso mesmo ). Então quando mentalizamos um design e montamos um teste e depois a implementação, temos um feedback rápido e a possibilidade de pensar de novo na solução.

Essa questão de “monkey programmer”, “pedreiro de software” e tudo mais, eu gosto muito de comparar com esse artigo do Martin Fowler : http://martinfowler.com/bliki/PreferDesignSkills.html

Particularmente eu acho q ele diz tudo sobre o tema nesse artigo.

ps: tô até agora rindo com a imagem do pobre coitado do gato flutuando e girando infinitamente com o pão nas costas 😀

[]’s
Marinho

Eu observo muitas vezes os profissionais de nossa área discutirem TDD, unit tests, etc… Mas vejo poucas iniciativas em projetos e também casos de sucesso. Tenho enxergado que existe uma lacuna muito grande até chegarmos a adoção / convencimento do uso de TDD em projetos corporativos de grandes empresas.

Não é fácil montar uma equipe relativamente grande e consciente da necessidade de se criar testes automatizados, pelo menos atualmente. Os projetos brasileiros, (e o trabalhador brasileiro, de modo quase geral) possuem uma necessidade de ‘sobreviver’ maior do que de inovar, isso por que nossa situação econômica não nos permite inventar muito as coisas (todos nós não estamos discutindo termos criados no estrangeiro?).

Todos sabemos que testes automatizados são vantajosos, mas por que ATÉ HOJE são adotados por apenas 3% dos projetos (sendo que muitas das pessoas que o utilizam no Brasil participam deste blogroll e similares)?

Acho que existe um lado oculto que geralmente a equipe que está envolvida num projeto reconhece, porém não combate. Por exemplo, no caso da carência de testes, esta está relacionada à idéia de que testes são caros, chatos e consomem tempo, porém todos reconhecem que a carência deles ocasiona retrabalho. O lado oculto neste caso é que o próprio retrabalho geralmente custa mais caro e consome mais tempo que a construção de testes automatizados.

A pergunta é: por que todos aceitam o retrabalho? Ora, não vivemos na república perfeita de Platão … As pessoas possuem problemas e sentimentos que não são ‘ajustáveis’ em processos. Vejam bem: o cara trabalha numa grande empresa, onde a equipe alocada no projeto varia de 2 em 2 meses; muitas vezes corrigir os trabalhos dos outros é querer ser ‘Madre Tereza de Calcutá’ num mundo competitivo; muitas vezes estamos presos a processos burocráticos existentes; ele pensa: vou perder meu tempo querendo seguir o que os acadêmicos dizem?

Estou participando de um projeto a uns 2 anos para uma grande petrolífera brasileira. Nossos objetivos: definir uma metodologia de desenvolvimento utilizando práticas de Agile Development e também estando em conformidade com os processos burocráticos do cliente.

Primeiro passo: montar uma equipe de 20 desenvolvedores qualificados.
Segundo passo: introduzir as práticas de testes automatizados.
Terceiro passo: introduzir um ambiente de integração contínua auto-ajustável.

Passo final: entregar o sistema pronto depois de 6 meses de desenvolvimento.

Muito bem, esbarramos em diversos problemas. Primeiro, somente eu e mais um é que tinhamos prática em programar com testes automatizados. Segundo, todos nós éramos quase desconhecidos, até todos se conhecerem e entenderem as qualidades e defeitos demorou um certo tempo. Terceiro, queríamos um ambiente de integração contínua sem ter ao menos uma equipe que soubesse desta importância… Foram momentos difíceis…

Mais dificuldades:

Não adiantava querer explicar para o desenvolvedor que TDD era o máximo, enquanto que o projeto estava pegando fogo nos bastidores.

Não adiantava querer adotar o Maven 2.0 com o Continuum, quando na verdade tínhamos etapas antecedentes a resolver.

Não adiantava querer criar testes unitários, sendo que a equipe tinha dificuldade de criar mocks para alimentar os cenários…

Aonde nós chegamos:

Criamos um ambiente automatizado de testes funcionais, baseado no DBUnit, e focando nas regras de negócio do sistema. Testávamos apenas os métodos públicos das chamadas dos eventos. Configuramos um banco de dados Oracle Express local para testes. Não criamos um ambiente de integração contínua naquele momento…

Nenhum teste criado por nós utilizou TDD, todos foram criados logo após o desenvolvimento. Sinceramente não vi nenhuma desvantagem nesta abordagem.

Se você perguntar pra mim se sentimos falta dos testes unitários, direi que foi muito pouca. Nossos testes funcionais automatizados nos supriu bastante. Não concordo com o Knuth mas também não penso que ‘TDD + unit test’ DEVE ser adotado por que é assim que manda o figurino.

Somente hoje é que estamos pensando em configurar um ambiente de integração contínua…

Leave a Reply to Guilherme Chapiewski Cancel reply

Your email address will not be published. Required fields are marked *