sexta-feira, 27 de novembro de 2009

Pare de escrever testes inúteis

É bom testar seu código. Ninguém discute isso. Montar um conjunto de testes automatizados pode ajudar em muito a qualidade do seu sistema. O problema é que muitas vezes medimos a qualidade da nossa base de testes unitários pelo percentual de linhas de código que é exercitada pelos testes. Chegar a 100% de cobertura é o alvo sagrado quase inatingível. Infelizmente, essa batalha é a batalha errada por dois motivos.

Primeiro e menos grave, ter uma cobertura de testes alta dá uma falsa sensação de segurança. Se temos 80% de cobertura, então 80% do código não tem erros, certo? Não exatamente. O que estamos medindo é cobertura de comandos, que é a forma mais fraca de cobertura. Eis um exemplo simples:

if ((a > 0) || (b < 0)) {
    c = 0;
} else {
    c = 1;
}

Bastam dois testes, um com a==0 e outro com a==1 para cobrirmos todas as linhas. No entanto, se houver um erro na segunda cláusula (se tivesse de ser b > 0, por exemplo), nossos testes não detectarão o erro. Um testador experiente sabe que tem de testar as duas cláusulas, mas um inexperiente pode ser ludibriado pela medida de 100% de cobertura desse código e pensar que não há nada mais a fazer.

Segundo e mais grave, há testes que simplesmente não fazem sentido. Já vi inúmeros testes em que o testador está simplesmente repetindo todo o comportamento da classe sendo testada. Quando se usa frameworks como EasyMock ou Mockito, fica ainda mais fácil cometer esse erro. Esses frameworks nos permitem verificar se determinados métodos estão sendo chamados com certos parâmetros. Se só o que o método faz é chamar os métodos x, y e z, o que acaba acontecendo é que o teste unitário simplesmente verifica se x, y e z são chamados nessa ordem. Qual é o erro que pode ser encontrado por esse teste unitário? Nenhum. Só o que acontece é que quem modificar o método vai ter de modificar seu clone do mal nos testes unitários.

Imagine uma classe de modelo cujo objetivo é receber estruturas de dados internas e convertê-las em chamadas ao banco de dados. Como testar essa classe? Fazendo um mock do banco, chamando a classe com alguns valores concretos, e observando que o banco está sendo chamado com os valores corretos. Qual o valor de se fazer esse teste? Quase nenhum. O único valor é que você está efetivamente duplicando a funcionalidade que você fez na classe original e, se as duas implementações forem diferentes, você vai ter de olhar pra ver qual das duas está errada. É uma versão do fenômeno da segunda vez.

Quando faz sentido fazer testes de unidade, então? Pra mim, só quando a classe tem lógica interna, ou seja, toma decisões ou faz alguma computação real que pode ser observada externamente. O ideal é poder observar as mudanças no estado no sistema, e não o seu fluxo de controle.  Traduzindo:

int metodo(int a) {
  int b = w(a);
  z(b);
  if (b > 0) {
    return x(b);
  } else {
    return y(b);
  }
}

Como deve ser um teste para esse método? Deve ser um teste caixa-preta. O teste deve testar os valores retornados são compatíveis com diferentes valores de entrada de a. Nada mais. Para isso precisamos entender o que w(a)x(b) e y(b) retornam. E z(b)?  Como sempre é executado, não faz sentido testá-lo. E devemos verificar se x(b) ou y(b) foram chamados? Na minha opinião, não. Devemos é saber se os valores de retorno esperados são equivalentes aos recebidos.

O bom teste deve desafiar o código sendo testado. Deve criar várias situações distintas e observar se o código reage de acordo com o desejado. Quanto mais forte o desafio, mais útil o teste. Desafios fracos criam testes fracos.

E daí, onde quero chegar com tudo isso? daí que criar e manter testes tem um custo, e esse custo não é trivial. Vamos então à lei de Torsten sobre testes unitários:
Todo teste unitário deve trazer valor a longo prazo maior que seu custo de criação e manutenção.
Aqui, o valor do teste é proporcional ao desafio que ele faz ao código testado.

Resumindo, é OK não chegar nem perto de 100% de cobertura. Teste apenas o que faz sentido, lembre-se que nem todo teste é útil. E quando fizer sentido, teste direito, não se esqueça de testar nenhum caso importante só porque já cobriu todos os comandos.

quinta-feira, 26 de novembro de 2009

Ajude a definir seus direitos na internet, não deixe para o Frederico Pandolfo.

Participei na terça-feira de um painel sobre computação nas nuvens em um evento chamado internet do futuro, patrocinado pela Abranet e pelo ICANN. O painel foi legal, com presença da Microsoft, UOL, Locaweb e Google, mas mais legal ainda foi o painel que veio antes dele, sobre regulação da internet no Brasil.

Lá eu fiquei sabendo de uma iniciativa super interessante: o Marco Civil da Internet. O marco civil é uma iniciativa do governo em definir direitos e responsabilidades do usuário de internet no Brasil. É o contrário da Lei Azeredo, que queria começar definindo quais são os crimes antes de definir quais são os direitos.

O mais legal é que está sendo feito de forma realmente democrática. A proposta de projeto está publicada no site e qualquer pessoa pode inserir seus comentários em cada seção do projeto. Há um debate muito interessante acontecendo, mas acho que pouca gente ainda sabe disso. Durante o painel, um representante do Ministério da Justiça explicou o esforço sendo realizado e me pareceu que o governo está realmente atento ao debate e levando a sério. O resultado do debate será transformado em anteprojeto de lei e levado para votação.

Quem é Frederico Pandolfo? Ninguém sabe, mas ele é responsável por quase um quarto de todos os comentários no site. Se você não quer que o Frederico Pandolfo decida quais serão seus direitos legais, vá agora mesmo ler a proposta e deixe lá seus comentários.

sexta-feira, 20 de novembro de 2009

Faça outra coisa

Acordou hoje e aquela tarefa grande parece enjoada? Está há várias horas travado em um bug e não progride?

Use seu tempo para aprender outra coisa, escrever uma ferramenta que auxilie marginalmente sua equipe, desenvolver um projeto pessoal, escrever em um blog, atrapalhar todo mundo e conversar sobre a direção do seu projeto. Programação é uma tarefa que precisa ser executada com perfeição. Se você está sem paciência com o que tem em mãos agora, é muito provável que seu tempo seja melhor aproveitado em outra tarefa.

Prazos existem, mas gastar X horas fazendo um trabalho que depois precisará de Y horas para ser refeito é pelo menos tão ruim quanto postergar esse trabalho por Y horas e depois gastar alguma fração de X motivado.

terça-feira, 17 de novembro de 2009

Um dia depois do outro

Estou viajando e sem tempo de postar. Obrigado Ademir por ter mantido o Micos vivo na semana passada.

Viajar é bom mas é sempre difícil quando a gente volta, leva um tempo pra conseguir retomar as atividades. Aliás, nem é preciso viajar. Muitas vezes começar de manhã cedo já é complicado. Eu às vezes enrolo o quanto posso, lendo meu Google Reader, respondendo e-mail, tudo menos pegar no código.

Uma coisa que me ajuda muito a pegar firme de manhã é não terminar no dia anterior. Essa dica me deram quando eu estava escrevendo - pare um parágrafo no meio, sabendo como quer continuar, mas não continue. No dia seguinte, pegue aquele parágrafo pra terminar e fica fácil continuar escrevendo. A inércia está em começar algo novo.

O mesmo se aplica à programação. Eu agora nunca vou pra casa deixando o programa arrumadinho. Sempre fica algum erro de sintaxe, algum bug não corrigido, algum método sem terminar. No dia seguinte eu sei exatamente o que fazer e de repente me vejo trabalhando e não enrolando.

Sabe aquela vontade de "só terminar isso aqui que eu vou embora?" Não termine! Se você é do tipo procrastinador como eu, deixe pra terminar amanhã o que você começou a fazer hoje. Sua produtividade vai aumentar bastante.

quinta-feira, 12 de novembro de 2009

Remédio pior que a doença

Naquela manhã cheguei cedinho ao escritório para começa a codificar tudo o que eu tinha passado parte da minha noite pensando. Pensando, não, alternando entre curtos cochilos, momentos de completa euforia por ter achado a solução mais simples possível e momentos de decepção e desespero por ter esquecido um corner-case.

Vai me dizer que nunca teve uma noite destas?

O problema era trivial: fazer uma infraestrutura para reinserir dados que foram removidos do sistema por algum engano. Simples, não é? Pois é, a solução também deveria ser. Porém, 'dobrar' o sistema para fazer isso era muito complexo.

Os dados removidos tinham que ser re-gerados praticamente do zero e combinados da maneira correta com os pedaços que não haviam sido removidos. Eu nem preciso dizer o quão complexo e error-prone é 're-gerar' um dado fora do pipeline normal, não é? Sempre tem aquele campo que é preenchido por aquela função que é simplesmente impossível de desacoplar do sistema ou que ninguém sabe direito como ele é gerado. Credo, está dando enjôo só de lembrar do problema!

Na verdade, na minha cabeça, esta feature nem era necessária pois estes dados seriam naturalmente re-inseridos no sistema depois de alguns dias. Eu já havia feito a mudança para que o pipeline recolocasse-os de volta. Mudança simples, por sinal. Porém, segundo o resto do time, fazer a recuperação instantânea dos dados era crucial...

Bom, se era necessário, tinha que ser feita, não é?

Abri meu Vi e comecei a implementar aquelas linhas que já tinham me custado várias horas de sono.

A fase 1, a mais trivial, estava quase pronta, com testes e tudo. Para a fase 2, conversei com um colega que conhecia melhor o sistema e confirmei: minhas horas de sono não foram gastas em vão. A solução ia funcionar!

Peguei um copão de café, voltei para minha mesa, reatachei meu screen, adrenalina total, escrevi duas linhas de código e, simplesmente, parei.

Parei pois me veio à cabeça a imagem do Torsten saindo todo feliz de uma reunião com os PMs (Product Managers) e falando: "Pronto, eliminei mais três features, agora só faltam mais duas!". Acho que não digitei nenhum caracter a mais, virei para o lado e falei com o colega que havia me dado as dicas: "Eu acho que não vale a pena implementar esta feature. É tão complexa! É a única solução possível, mas é complexa de mais, não encaixa no sistema. Ele não foi feito para isso! Vai gerar overhead de complexidade e de manutenção que realmente não vai valer a pena.". Ele disse: "É mesmo.".

Pronto, agora era só convencer o resto do time que qualquer solução era pior que o próprio problema e o meu final de semana estava salvo.

Resultado: conseguimos convencer o resto do time, o sistema ficou muito melhor sem a feature, pude gastar meu tempo melhorando outras partes e implementando features realmente necessárias.

O que fiz no final de semana seguinte? Dormi as horas de sono que haviam ficado pendentes.

sexta-feira, 6 de novembro de 2009

O que você faria diferente?

Essa semana não deu pra postar no blog: feriado, depois muito trabalho. Vamos ver se semana que vem eu consigo, ou quem sabe meus colegas de blog dão as caras.

Semana passada estive na PUC pra falar aos alunos formandos em sistemas de informação sobre a carreira deles. Na verdade eu só falei mesmo sobre a minha carreira, que é a única que eu conheço bem, mas espero que tenha ajudado.

O que eu falo pra eles se resume no seguinte:
  1. Faça o que você gosta. Nao adianta ganhar dinheiro ou ter prestígio se você vai passar a maior parte das horas da sua vida fazendo coisas que não quer.
  2. Fazer o que gosta não é suficiente. É preciso ter certas competencias como gostar de aprender, comunicar-se bem em português e inglês, saber trabalhar em equipe e, naturalmente, ter uma base forte de fundamentos da computação, principalmente algoritmos e estruturas de dados.
  3. Se quiser fazer mestrado, faça agora. Se quiser fazer uma pós latu sensu, faça daqui a cinco anos quando tiver mais experiência. Doutorado, só se você quiser muito mesmo.
  4. Não tema o desemprego, tema sim as fracas condições de trabalho e salários oferecidos ao profissional de informática. Cabe a você informar o mercado de que esse profissional não é uma peça intercambiável da sua empresa. É melhor estar desempregado do que em um emprego ruim, pois ficar em um emprego ruim mostra ao mercado que empregos assim são aceitáveis.
Uma aluna fez um comentário dias depois: "eu queria ter escutado essa palestra no primeiro período".

Isso me fez pensar: o que eu faria diferente na minha carreira? Achei difícil responder essa pergunta. Foi mais fácil pensar o que eu NÃO faria diferente: 
  1. Eu não faria um MBA.
  2. Eu não deixaria de fazer meu doutorado, apesar de não ter seguido carreira acadêmica.
  3. Eu não trocaria de profissão.
Talvez eu tivesse seguido a carreira acadêmica se houvesse uma oportunidade interessante. Infelizmente, nenhuma das oportunidades que eu vislumbrava me interessou.

Acho que o que eu teria feito diferente seria escrever mais código. Meu doutorado de 7 anos foi composto de alguns grandes esforços bem concentrados, separados por longos períodos de nada. Eu devia ter enchido meus períodos de nada participando de algum projeto open source.

E você? Faria alguma coisa diferente?