Archive for the 'testes' Category

Ninguém liga para sua relação teste-para-código

Tenho visto muita gente recentemente se gabando da relação teste-para-código de seus projetos. As pessoas medem a quantidade de linhas de código do sistema em si e a quantidade de linhas de código dos testes automáticos e publicam a relação de uma para a outra, como se isso quisesse dizer algo sobre a qualidade do sistema. Permita-me ser bem claro:

Ninguém dá a mínima para a sua relação teste-para-código.

Nem para sua taxa de cobertura de testes, se é que isso interessa.

A menos que a relação seja zero-para-um, não há absolutamente nada de informativo neste número. A partir do momento em que você está escrevendo algum teste, fazer este número crescer não garante melhoria nenhuma na qualidade do sistema. Talvez você esteja escrevendo testes inúteis para verificar se sua linguagem de programação consegue atribuir um valor a uma variável e recuperá-lo de volta. Talvez você esteja repetindo os mesmos testes o tempo todo. Testar é bom, mas alguns testes são melhores do que outros e este número não é calculado sobre a qualidade dos testes, só a quantidade deles. Não há nenhuma conclusão útil que eu possa tirar do seu número sem dar uma olhada no seu código.

Cobertura de testes é outra métrica bem vazia. Claro que você deve tentar se aproximar tanto quanto possível de 100%, mas a verdade é que chegar a níveis de cobertura acima de 90% não é tão difícil assim. Considere, por exemplo, este trecho de código adaptado do Code Complete do Steve McConnell:

    totalWithholdings = 0;

    for ( id = 0; id < numEmployees; id++) {
        if (m_employee[id].governmentRetirementWithheld < MAX_GOVT_RETIREMENT) {
            governmentRetirement = ComputeGovernmentRetirement(m_employee[id]);
        }

        companyRetirement = 0;

        if (m_employee[id].WantsRetirement &&
            EligibleForRetirement(m_employee[id])) {
            companyRetirement = GetRetirement(m_employee[id]);
        }

        grossPay = ComputeGrossPay(m_employee[id]);

        personalRetirement = 0;
        if (EligibibleForPersonalRetirement(m_employee[id])) {
            personalRetirement = PersonalRetirementContribution(m_employee[id],
                companyRetirement, grossPay);
        }

        withholding = ComputeWithholding(m_employee[id]);
        netPay = grossPay - withholding - companyRetirement -
            governmentRetirement - personalRetirement;

        PayEmployee(m_employee[id], netPay);

        totalWithholdings = totalWithholdings + withholding;
        totalGovernmentRetirement = totalGovernmentRetirement +
            governmentRetirement;
        totalRetirement = totalRetirement + companyRetirement;
    }

    SavePayRecords(totalWithholdings, totalGovernmentRetirement,
        totalRetirement);

É possível obter 100% de cobertura para este trecho completo com apenas um teste: você só precisa garantir que há ao menos um funcionário (para que o loop seja executado) e que as três cláusulas condicionais avaliem para verdadeiro. Obviamente um único teste não é o suficiente para testar todas as nuances deste código e, de fato, McConnel mostra no livro que são necessários pelo menos dezessete testes diferentes. Mesmo assim se tivéssemos escrito aquele único caso de teste, enxergaríamos cobertura total.

Parece que virou moda ultimamente supervalorizar os testes e divulgar estas métricas vazias. No fim das contas elas funcionam como qualquer métrica para software, só dão uma visão bem abstrata do sistema e podem muitas vezes serem enganosas. Não há substituto para uma boa leitura do código.

Tendo dito isso tudo, eu definitivamente não recomendo tentar desenvolver software sem escrever testes automáticos. Já é difícil desenvolver software usando testes, mas é ainda pior sem eles. Só porque você sabe que não é possível provar ausência de defeitos através de testes, não quer dizer que não deva usá-los. Então, por favor, continue escrevendo-os (ou passe a escrever se você não escreve ainda). Só não aja como se eles fossem a última salvação da humanidade. Eles são só a primeira.

Testes não são para testar

Um teste é uma verificação feita sobre um programa para garantir que uma determinada entrada produza uma saída esperada. Testes são, de acordo com esta definição (propositalmente vaga), finitos e pontuais. Portanto não podem ser utilizados para verificar completamente a saída de um programa com entradas infinitas. A não ser que se disponha de tempo infinito.

Mas alguém tem tempo infinito?

Como a maioria dos programas úteis têm conjuntos de entrada extremamente grandes (e os infinitos nem são tão raros assim) e tempo não é um recurso muito abundante, testes só podem ser executados para um subconjunto necessariamente pequeno das entradas. Isso limita bastante a efetividade deles como recursos de validação. Não há como verificar se um programa está completamente correto com testes apenas, mas há várias técnicas que podem ser usadas para aproveitar melhor o pouco de certeza que eles podem oferecer. Testes não são perfeitos para validação, mas ainda são a melhor opção disponível.

Para que os testes sejam minimamente úteis, o programa precisa ser escrito com testabilidade em mente. A explosão combinatória pode derrubar os melhores programadores num piscar de olhos. Testar uma função de três parâmetros não costuma ser tão fácil quanto testar duas de dois. Quem quiser escrever testes um pouco menos complicados vai precisar tomar cuidado para não fazer tanta coisa dentro de uma função só. Do mesmo modo é mais fácil testar uma rotina que recebe um parâmetro de um sistema externo como entrada do que uma que faz a chamada internamente.

Estes são exemplos simples de separação de responsabilidades. Talvez sejam até óbvios, mas os defeitos ficam ainda mais destacados quando se está escrevendo o código dos testes junto com o código do programa em si. O maior valor dos testes está aí. Testabilidade é sinônimo de baixo acoplamento, que por sua vez é característica essencial do software bem escrito. Claro que testes servem para verificar corretude, mas somente de código bem feito. Atentar para os testes é mais do que validar saídas para entradas, é garantir que não está escrevendo uma grande bola de código entrelaçada de todos os lados.

É “guiado”, oras!

Provavelmente vou mexer em um vespeiro dos grandes com o texto de hoje. Pelo menos é o que eu vejo acontecer quando alguém resolve desafiar, questionar ou simplesmente comentar qualquer coisa no campo da Agilidade (com letra maiúscula mesmo). Neste pequeno mundo, você nunca sabe quando está mexendo com alguma coisa sagrada para alguma pessoa. Também é o que eu vejo acontecer quando programadores discutem a questão traduzir ou não traduzir. Então parece que são dois vespeiros ao invés de um só. Mas eu gosto de exercitar a iconoclastia sempre que possível e vou continuar com o texto apesar de tudo.

Não sei como surgiu a tradução que parece ser normalmente usada por todo mundo aqui pelas bandas do Brasil para “Test Driven Development”, mas eu simplesmente não consigo evitar um certo incômodo mental quando alguém cita “Desenvolvimento Orientado a Testes”.

Pronto falei. Podem jogar as pedras.

Não é que os símbolos tenham algum papel extremamente relevante quando usados por pessoas com o mesmo domínio sobre o vocabulário. Evidentemente, não há nenhum problema nas palavras em si quando as pessoas sabem do que se está falando. Mas é quem não sabe que me preocupa.

Esta expressão “orientado a” deve ter vindo do mesmo baú do qual foram tirados alguns outros termos da moda como Programação Orientada a Objetos e Arquitetura Orientada a Serviços (que, como todo bom modismo, também costumam ser completamente mal interpretados). Não vou recorrer ao lugar comum de consultar um dicionário, mas este termo pode levar muita gente a pensar em simplesmente atentar para os testes e projetar sistemas testáveis. Esta postura certamente é melhor que não dar atenção nenhuma aos testes e relegar a atividade ao décimo-oitavo plano para ser realizada um pouco antes da implantação no caso de sobrar algum tempo. “Desenvolvimento Orientado por Testes” é um pouco menos ambíguo. Mas prefiro “Desenvolvimento Guiado por Testes” tanto por ser mais fiel ao original quanto por eliminar o já sobrecarregado termo “orientado”.

Desenvolvimento guiado por testes envolve uma certa atenção aos testes, mas também uma porção de outras escolhas técnicas e filosóficas. Envolve obrigatoriamente escrever os testes antes do código. Afinal, você não pode ser guiado por algo que não existe. Quando se abraça esta filosofia, os testes acabam virando uma das principais razões da existência do código, logo abaixo das necessidades do usuário. Certamente estas últimas são muito mais importantes do que os primeiros, mas um conjunto de testes bem projetado será o melhor reflexo das necessidades do usuário.

Mas, como disse, sou um iconoclasta atento e não acho que este tipo de posicionamento mental seja ideal para todos os casos. Particularmente, quando estou investigando algum tipo de tecnologia que imagino que vou precisar e com a qual ainda não tenho muita familiaridade, prefiro desenvolver apenas orientado a testes. Mantenho sempre a mente atenta à testabilidade, mas não necessariamente chego a escrever os testes. Depois que consigo algum domínio sobre a tecnologia, costumo voltar ao desenvolvimento guiado por testes e faço tudo que posso para escrever os testes antes do código de produção.

De qualquer modo, os termos em si não são tão importantes. São apenas símbolos e, se usados no contexto correto, não costumam gerar nenhum tipo de confusão. O importante são as diferenças filosóficas denotadas por eles.

Acho que minha doidice por linguagens de programação está começando a gerar uma doidice por linguagens naturais. Vai saber…

Andando de costas

Aqui vai um pequeno resumo de como adiciono recursos aos meus sistemas:

  1. Eu identifico (ou alguém me chama a atenção para) alguma necessidade
  2. Escrevo alguns testes para a necessidade
  3. Escrevo o código para fazer os testes passarem
  4. Depois que os testes estão devidamente esverdeados, reestruturo o código para que fique um pouco mais elegante
  5. Repito até que a necessidade esteja satisfeita

Este é o modo TDD de fazer as coisas e pode parecer estranho para quem não está acostumado. Mas o que não é estranho para quem não está acostumado?

Por conseqüência este modo esquisito também é o modo XP. Pelo menos para quem segue a cartilha. E, olhando por este lado, parece que XP inverte bastante as coisas. Você faz os testes antes de fazer o código e (re)define a estrutura depois de ter o código pronto. É como programação para caranguejos: você anda e consegue chegar aonde precisa, mas o modo como isso é feito não é muito parecido com o que se vê normalmente. A não ser, é claro, que você esteja vivendo entre caranguejos.

O que eu inicialmente aprendi na escola foi que deveria pensar um pouco em como estruturar a aplicação antes de começar a escrever código. Para registrar (ou documentar, para usar uma palavra um pouco mais corporativa) a estrutura, poderiam ser elaborados diagramas usando uma linguagem gráfica padrão. Depois era hora de implementar o design. Implementar, em corporativês, é escrever o código correspondente ao design (que é a palavra usada para indicar a estrutura pré-projetada). Finalmente, depois que o código estivesse escrito, chegava a hora de testar a coisa.

Em algum ponto do tempo, devo ter esquecido isso tudo. Pelo que eu descrevi, parece que estou fazendo tudo ao contrário, andando de costas. Afinal, eu começo pelos testes e só no final do ciclo me preocupo com a estrutura. Mas a verdade é que continuo fazendo tudo na mesma ordem. Os testes que faço no início servem para que eu pense um pouco e defina uma estrutura mínima para o código que estou prestes a escrever. Eles definem como o novo pedaço de código vai se ligar ao resto da aplicação, assim como fazem os diagramas. Só que têm uma vantagem bem importante: depois que eu quiser validar tudo, os testes já estão prontos. Do mesmo modo, a atividade final de reestruturação é uma preocupação principalmente com a estrutura da aplicação e, como isso tudo é um ciclo, estar no final é o mesmo que estar no início.

BTUF

Não precisa ler o título de novo. Seus olhos não estão enganados: realmente é um T no lugar do D (ou R, dependendo de onde você esteja vindo). BTUF é o acrônimo para Big Testing Up-Front. BTUF é o que acontece quando você tenta desenvolver todos os testes possíveis e imagináveis para seu código antes de começar a escrevê-lo. Com a popularização de Test-Driven Development esta é uma modalidade para cascateamento do desenvolvimento em óbvia ascensão.

Testar também é projetar. Quando se escreve os testes antes do código de produção o que se faz essencialmente é projetar como o código vai parecer olhando do lado de fora. Às vezes é difícil resistir à tentação de projetar tudo antes para depois partir para a implementação. Para quem não consegue evitar, aqui vai uma bela notícia do jornal de ontem: isso não funciona. Apesar de todo mundo minimamente informado já ter lido o jornal, nunca é demais repetir.

Não funciona porque projetar qualquer coisa é infinitamente melhor quando é possível obter retorno. Desenvolver software é uma das atividades que dispõem de maior capacidade de retorno em tempo de projeto. Arquitetos precisam esperar que a equipe de construção termine a obra, roteiristas de cinema podem esperar anos pelo término das filmagens e músicos precisam esperar por um longo processo de mixagem para saber como seus projetos serão apreciados pelo público. Mas programadores podem simplesmente compilar seus programas. Desistir propositalmente deste retorno não é algo muito sábio a se fazer. Escrever todos os testes para um trecho de código antes de escrever é como desenhar vários retângulos e setas descrevendo-o detalhadamente antes de tentar fazê-lo funcionar. O risco de hiper-engenharia é o mesmo porque o código não está lá para gritar de agonia.

Então se você leu o artigo do José Oliveira, considere com muito carinho os conselhos dele. Mas leia o esclarecedor artigo do Ivan Sanchez também. Escreva os testes que conseguir imaginar antes de começar a escrever o código para fazê-los passar, mas por favor não fique muito tempo pensando. Você pode esperar para começar a escrever o código depois que todos os testes estiverem prontos, mas você não precisa e nem quer fazer isso.

Design não se faz somente com diagramas e TDD é só uma técnica de design que usa testes. Pode ser uma abordagem bem mais eficaz que setas e retângulos em alguns casos, já que força o programador a utilizar o código ao mesmo tempo que o projeta. Mas ainda assim é somente uma abordagem para design. BTUF, do mesmo modo, é somente um caso especial de BDUF no qual se usa testes para explicitar completamente o design antes de se escrever qualquer linha de código.

Erros de programação não existem…

…isso é coisa da sua imaginação.

Às vezes — e aqui sempre não seria uma aproximação tão grosseira — quem desenvolve um programa não sabe exatamente em que sistema ele será usado. É fácil ter uma ligeira idéia, mas há usuários demais para que seja possível ter certeza. Você já deve ter ouvido slogans do tipo “Escreva uma vez, rode em qualquer lugar”, mas isso é só propaganda. Claro que uma plataforma comum dá alguma segurança e aumenta a produtividade. Mas se você quer saber exatamente como seu programa se comporta nas mais variadas configurações, você precisa testá-lo. Não tem jeito. Você precisa soltar o coitado neste lugar inóspito que é o mundo real e ver como ele se sai. Pode parecer injusto, mas é para o bem dele.

Os usuários esperam que as coisas Simplesmente Funcionem ™ e saber o que eles esperam é um trabalho de requisitos e testes, não de programação. Isto não quer dizer que programadores não devam fazer isso, mas que as habilidades que eles devem ter vão além de escrever código. Isto envolve muito condicionamento, bastante experiência e um bocado de imaginação. Mas se um programa não funciona do modo esperado em alguma situação, ele não está errado. Se eu não programar o EclipseFP para saber que alguns módulos poderão ser importados de outra parte do mundo fora do meu controle, se seu formulário não estiver preparado para o espertinho que escreve os números por extenso ou se Joel Spolsky não previr que na Polônia o Alt-Direito é usado para digitar caracteres especiais, não quer dizer que nossos programas estejam incorretos. Eles estão corretos para algumas das possíveis situações.

Todo programa é escrito e testado com algum uso em mente. Mesmo que os testes não sejam escritos em linguagem de computador, todo mundo testa o que escreve pelo menos uma vez de forma manual. Obviamente estou simplificando um pouco as coisas aqui. Afinal existe gente que escreve programas e não precisa (ou não quer) se dar ao luxo (ou à sanidade) de executá-los antes da entrega. Mas, por favor, vamos desconsiderar esses prodígios (ou azarados) pelo bem do argumento. Se o código for executado e validado várias vezes de forma automática à medida que evolui, a chance de que deixe de funcionar diminui. Mas o fato é que, mesmo se você estiver escrevendo testes automáticos como deveria, eles não cobrem todas as situações. Eles não conseguem fazer isso. É simplesmente impossível porque o espaço de busca é potencialmente infinito.

Testes não podem garantir que um programa nunca vai se comportar de forma inesperada. Nada pode. Se seu programa for usado por uma variedade razoável de pessoas, alguém em algum lugar do mundo vai ter a chance de dizer “como diabos eles não pensaram nisso?”. Tudo que você pode fazer, se você tiver cuidado e reservar bastante tempo para pesquisa e testes, é garantir que quem vá dizer isso esteja em um lugar bastante remoto. Mas vai haver alguém. Sempre.

Erros de programação — diferentemente de fantasmas, lobisomens e sacis — não existem. O que existe mesmo são situações não previstas e testes que ainda não foram escritos.

Testes e especificações

Linguagens são umas criaturas muito flexíveis. As melhores linguagens de programação são as mais maleáveis, aquelas que permitem ao programador moldá-las às necessidades da sua aplicação. Quem usa a biblioteca de testes JUnit em Java, faz scripts de build com Rake em Ruby ou usa o Monad IO em Haskell praticamente não pensa nas primitivas da linguagem. Ao invés disso, a mente passeia pelos termos próprios ao domínio da aplicação. Não se pensa em classes, objetos, métodos e funções, mas em casos de teste, tarefas e comandos.

As linguagens naturais não são diferentes, estão o tempo todo mudando. Basta um grupo de pessoas se comunicando para que jargões e dialetos comecem a se formar. Novas palavras surgem e outras saem de moda com uma velocidade surpreendente. Há oito anos atrás, por exemplo, se a palavra “agilidade” fosse citada em uma conferência de desenvolvedores de software, poderia muito facilmente ser associada a soluções rápidas e sujas. Hoje para muita gente é sinônimo de alta iteratividade, valorização do feedback e adaptação constante. Parece mais com rápido e limpo do que com outra coisa qualquer.

Para que algumas palavras mudem de sentido, basta que sejam usadas por duas pessoas diferentes. “Especificação” é uma dessas famigeradas palavras com muitos significados. Muita gente presume que uma especificação é completa, ou seja, que prevê as saídas para todas as entradas possíveis. Por outro lado, outras tantas pessoas presumem apenas que uma especificação é não-ambígua, isto é, pode ser interpretada de um único modo.

Entretanto, a maioria das pessoas — e isto inclui programadores e seus clientes — não conseguem fazer especificações de nenhum desses dois tipos. Pelo menos não naturalmente. Não sem conscientemente se obrigar a isso. Estas especificações acabam saindo tanto ambíguas quanto incompletas. Seria bom que tivéssemos algum tipo de máquina capaz de garantir pelo menos uma dessas duas propriedades.

O que não é muito difícil de se achar…

Computadores são máquinas construídas exclusivamente para seguir instruções. Eles não podem interpretar a mesma instrução de dois modos diferentes. Podemos então escrever nossas especificações como um programa de computador e só vai haver um modo de interpretá-las. Assim eliminamos a ambigüidade e ainda ganhamos a habilidade de executar tudo automaticamente. Só que o nome “especificação” é um tanto quanto controverso, é melhor chamar estes programas somente de testes. Testes são executáveis. Podem não ser especificações, mas são executáveis.

Para executá-los basta fornecê-los ao computador adequado e eles vão rodar rapidamente. Muito mais rápido do que alguém poderia fazer manualmente. Programadores são especialmente bons em generalizar o comportamento esperado do sistema a partir de um conjunto de entradas. Se escrevermos testes para os casos excepcionais, podemos ficar razoavelmente seguros que o código está completo. Caso não esteja, quase sempre podemos encontrar mais testes. Esta segurança é somente estatística, pois é impossível testar todas as entradas na prática, mas é muito mais segurança do que se tem com uma linguagem natural.

Moral da estória: não dá para especificar tudo com testes automáticos, mas dá para especificar muita coisa.

A última palavra em rastreabilidade

A versão 0.5 do Motiro vai incluir um meio de acompanhar funcionalidades: saber o que entra no próximo release, o que fica de fora e votar no que você acha mais importante. Isso é bom para as equipes, que vão conhecer a vontade dos usuários e priorizar seu trabalho de acordo. Também é bom para os usuários, que vão saber o que esperar da evolução da aplicação e, melhor ainda, influenciar nesta evolução.

O Motiro já tem um pouco de integração com sistemas de controle de versão e, por isso, tive a grande idéia: prover algum meio de ligar um trecho de código à funcionalidade correspondente (“Este código aqui é para realizar a tradução da página”). Com isso poderíamos rastrear trechos de código para requisitos de usuário e vice-versa.

Acontece que minha idéia não é tão boa assim.

Na verdade, eu até diria que é uma má idéia. Fazer rastreamento desse jeito é jogar trabalho fora. Rastreabilidade é uma coisa ótima, mas acontece que no Motiro já usamos o que há de mais novo em termos de rastreabilidade.

Não, não temos nenhuma arma secreta escondida que não esteja publicada no site do projeto ou no próprio código fonte. Não usamos nenhuma ferramenta complicada, não desenhamos nenhum diagrama e muito menos mantemos uma matriz potencialmente gigantesca.

Não temos nada disso. Mas o Motiro é testado.

Nenhuma funcionalidade é desenvolvida e nenhum defeito é corrigido sem o teste correspondente. Portanto, para todo requisito há pelo menos um teste e descobri-lo é fácil, tão fácil quanto olhar seu título. Se quero saber que trecho de código implementa uma funcionalidade qualquer, é só olhar que trecho de código é executado pelo teste correspondente. Se quero saber por que alguma linha foi escrita, basta apagá-la e ver que teste falha.

Essa abordagem para rastreamento, ao contrário das outras que citei, não exige nenhum esforço extra por parte da equipe de desenvolvimento. Afinal, qualquer equipe que esteja realmente interessada em produzir software de qualidade vai ter algum mecanismo automático para verificação. Na maioria dos casos esse mecanismo é um conjunto de testes automáticos.

Testando os testes

“Ter testes automáticos para validar o código é muito bom. Você pode refatorar o código livremente, os testes te ajudam a saber que você não quebrou nada e bla bla bla.”

Você muito provavelmente já escutou essa conversa. Esse é o discurso padrão dos xispistas e de quase todo mundo na comunidade ágil. Todo mundo fala que os testes servem para manter a aplicação saudável, mas o código dos testes também é código e ele precisará ser refatorado mais cedo ou mais tarde. Já aprendemos que não dá pra acertar da primeira vez no código de produção, achar que no de teste dá seria ingenuidade.

Já que não dá, fazemos com os testes o mesmo que fazemos com a aplicação. Escrevemos um pouco de cada vez e refatoramos conforme necessário para não repetir idéias, minimizar o acoplamento e alcançar todas aquelas outras coisas boas que aprendemos na escola (ou que a experiência nos ensinou).

Quando estamos refatorando o código da aplicação, confiamos no código de teste para não nos deixar cair. Mas quando é o código de teste que estamos refatorando não há uma segunda rede protetora, não há testes que testem os testes que testam o que deve ser testado (eu sei que ficou confuso, foi de propósito). Na verdade, podemos ir em frente e escrever os meta-testes.

Mas talvez não precisemos.

Os testes não estão tão desprotegidos assim. Há a aplicação para garantir que eles não estão fazendo nada errado. Ela foi escrita especialmente para passar naqueles testes e pode ser usada para garantir que o comportamento deles não está mudando, que eles estão checando o que checavam anteriormente. Se não alterarmos o código da aplicação quando estivermos reestruturando o dos testes, sair de uma barra verde para outra é razoavelmente seguro.

Há uma sinergia muito grande entre os dois tipos de código. O código de teste segura o de produção e o código de produção segura o de teste. Quem já viu dois amigos tentando voltar para casa depois de saírem de uma festa embriagados sabe como é isso. Nenhum dos dois vai chegar muito longe sozinho, mas quando estão juntos um pode contar com o outro se tropeçar.

Pagando as dívidas

Estou lendo O valor do amanhã de Eduardo Giannetti. Ele é um economista (não um programador, como a maioria dos autores que leio) e este livro em particular é sobre trocas e negociações intertemporais. Quando escuto dizer que um economista está falando de trocas intertemporais, não consigo evitar pensar em juros aplicados ao mercado financeiro. O que achei interessante é que Giannetti vê os juros em um contexto maior, identificando-os, por exemplo, na biologia. Aparentemente isto não tem nada a ver com software ou tecnologia, mas é por isso que o subtítulo do blog fala em devaneios.

Acontece que existe no cérebro dos programadores um mecanismo que faz ele relacionar (quase) qualquer coisa que lê com seu trabalho. Talvez os mais antenados já tenham identificado aonde quero chegar: o conceito de dívida de projeto, que talvez você já conheça pelo termo em inglês design debt.

Giannetti destaca a diferença entre envelhecimento e a senescência, dois mecanismos geralmente referenciados apenas pelo primeiro termo. Envelhecimento é o simples passar do tempo, o mecanismo pelo qual avançamos em idade e experiência. Senescência é o mecanismo que debilita um organismo com o passar do tempo. A senescência é o custo pago pelo vigor da juventude. Assim como os juros financeiros, a senescência é um custo pago na velhice (futuro) em troca de um benefício na juventude (presente). O organismo dispõe de tanta energia na juventude justamente porque vai pagar por ela na velhice.

O universo do desenvolvimento de software não está livre dos juros. Para cada atalho que tomamos no presente, cada “jeitinho” que damos, serão cobrados juros no futuro. Assim como no mercado financeiro, a quantidade de juros envolvidos na transação depende do tamanho do empréstimo que tomamos. Assim como não se deve rolar muito as dívidas financeiras, não se deve postergar demais o pagamento das dívidas de projeto. Quanto mais tempo correr, mais juros terão que ser pagos. Isso é um bom negócio para quem empresta dinheiro, mas não para quem produz software.

Todos nós queremos sistemas que durem bastante tempo e que continuem gerando dividendos: sistemas velhos. Mas ninguém quer um sistema senil, um no qual a receita não compense o esforço gasto com o pagamento de juros. Então é melhor consertar hoje aquelas pequenas gambiarras que você fez ontem. Cada vez que pensar “puxa, isso deveria mesmo ser feito de outro jeito, mas vou fazer desse jeito aqui para ir mais rápido”, não pare na parte do pensamento. Uma abordagem muito mais indicada é fazer do jeito rápido, ter testes que comprovem o funcionamento e consertar para o jeito certo.

Nota: O Motiro foi oficialmente lançado nesta segunda-feira. Pensei em fazer um post para anunciar isso, mas este blog já está tão infectado com notícias sobre Motiro que preferi fazer somente esta notinha aqui. Maiores informações (e links para o código-fonte e downloads) no blog do projeto.