A Raiz De Todos Os Medos 

Mar/10
10

Todas as profiss√Ķes envolvem algum tipo de responsabilidade. Toda responsabilidade envolve algum tipo de risco. Gerenciar o risco corretamente √© que torna a pessoa um profissional.

Em desenvolvimento de software existem diversos riscos associados ao projeto, mas vamos focar hoje nos riscos associados ao código.

A qualidade do código é proporcional ao quão limpo ele está. Manter a qualidade do software é a principal forma de minimizar o risco.

Em um desenvolvimento desatento presume-se que a √ļnica fun√ß√£o do c√≥digo √© prover a funcionalidade requerida nos requisitos funcionais, o velho e bom “est√° funcionando”. Este tipo de desenvolvimento √© t√£o comum que gerou a m√°xima “Primeiro faz funcionar, depois faz funcionar bem. Depois faz funcionar r√°pido.” Este tipo de pensamento pode funcionar no meio¬†acad√™mico quando voc√™ est√° descobrindo um algoritmo novo,¬† mas fora do mundo acad√™mico esse pensamento √© uma anti-regra. Ele viola o mais fundamental prop√≥sito de qualquer empresa: lucro, pois obriga √† re-edi√ß√£o do software pelo menos 2 vezes.

Em c√≥digo feito para compor produtos de software essa regra n√£o pode ser seguida, contudo, sabemos que n√£o √© f√°cil acertar √† primeira em um bom modelo ou em uma implementa√ß√£o perform√°tica. Ser capaz de alterar o software √© um requisito constante, e √© a ess√™ncia de toda a teoria por tr√°s de todos os paradigmas de programa√ß√£o (de orientado a instru√ß√Ķes a orientado a objetos): queremos nosso c√≥digo f√°cil de mudar. Ent√£o porque n√£o o mudamos mais frequentemente?

A principal razão está no fato do código ter sido escrito de forma confusa e suja (não limpa). O código escrito dessa forma viola o requisito constante de ser capaz de alterar o software facilmente. A grande maioria de desenvolvedores produz código assim e não é simples treinar uma pessoa a fazer de forma diferente.

A segunda raz√£o √© a escolha de modelo falhos ou falidos. Modelos falidos s√£o aqueles que parecem resolver o problema e s√£o tradicionalmente passados de boca em boca como solu√ß√£o para certo problema, mas com o passar do tempo eles n√£o resistem. Formas de¬†programa√ß√£o¬†que eram consideradas o topo da qualidade num paradigma, caem para anti-padr√Ķes em outro. Um exemplo simples: o modelo do Struts 1 funcionou durante um tempo, mas √© hoje um modelo falido.

Modelos falhos s√£o aqueles que resolveram o problema num certo momento mas n√£o resistem √† adi√ß√£o da necessidade de mais funcionalidades. ¬†Um exemplo √© a classe java.util.Date. Conseguiu resolver o problema de trabalhar com datas durante um tempo, mas o modelo era fraco para abarcar todas as necessidades do contexto de datas. √Č um modelo falho.

Modelos podem ser compostos de várias classes ou poucas, não é relevante. O problema acontece quando detectamos que o modelo falhou ou faliu e precisamos remodelá-lo. Quem vai querer mexer naquele código legado escrito há mais de 3 anos por um pessoa que não está mais na empresa ?

A primeira reação a esta situação é medo. Medo de alterar um código que está funcionando mas que precisa ser alterado, no todo ou em parte; de forma que mantenha as funcionalidades atuais e novas sejam adicionadas. O medo advém da falta de controle sobre o estado de funcionamento do código.

C√≥digo limpo pode ajudar a entender melhor o¬†c√≥digo¬†que est√° escrito, mas n√£o o ajuda a verificar que ap√≥s uma altera√ß√£o ele ainda funciona como esperado. Precisamos de algo mais eficaz, mais din√Ęmico. Precisamos de testes.

A escrita de testes não é um luxo, é uma necessidade inerente à produção de código. Escrever testes para o seu código é equivalente a colocar mecanismos de segurança em centrais nucleares. Não podemos apenas confiar que nunca vai acontecer nenhum problema ou que nunca precisaremos de fazer alguma manutenção.

Porque a principal preocupação da maioria dos desenvolvedores é colocar o código a funcionar mas sem criar nenhuma forma de verificar que o código continua funcionando, os mesmos desenvolvedores são depois dominados pelo medo de mudar o código que eles mesmos escreveram.

A escrita de c√≥digo que verifica a¬†consist√™ncia das funcionalidades do¬†c√≥digo de produ√ß√£o √© uma necessidade e n√£o uma op√ß√£o. A menos √© claro que voc√™ esteja disposto a gastar rios de dinheiro e tempo em altera√ß√Ķes arriscadas, ou se o seu software for para durar 3 meses.

Como escrever esses testes e quais testes escrever?

O relacionamento entre as opera√ß√Ķes necess√°rias num projeto de software e as¬†respectivas¬†√°reas de teste apresentadas pelo V-model mostra que existem v√°rios tipos de teste conforme o objetivo e o¬†n√≠vel¬†de certeza que queremos ter. ¬†Isto nos leva a 4 tipos principais de teste: Teste Unit√°rio, Teste de Integra√ß√£o, Teste de Sistema e Teste de Aceita√ß√£o.

A responsabilidade de cada¬†n√≠vel¬†n√£o √© um assunto pac√≠fico, mas vou deixar uma¬†ideia¬†do que estamos falando. O Teste Unit√°rio verifica que a unidade de codifica√ß√£o est√° em conformidade. A unidade de codifica√ß√£o depende do paradigma de programa√ß√£o usado. Em Orienta√ß√£o a¬†Objetos pode ser uma classe ou um pacote de classes. O teste de integra√ß√£o verifica que o funcionamento de v√°rias unidades de codifica√ß√£o trabalhando juntas est√° em conformidade. O teste de sistema, verifica que o sistema funciona em conformidade em diversos ambientes, com diversas n√≠veis de carga, com diversos n√≠veis e tipos de recursos (mem√≥ria, CPU, banco de dados, etc..) , em diversas configura√ß√Ķes e distribui√ß√Ķes em¬†rede. O teste de aceita√ß√£o verifica que o sistema atende os requisitos funcionais especificados para ele.

Existem outros tipos de teste como teste de mercado que determina como os usuários reagem ao sistema, como o utilizam, com que frequência, etc. Este tipo de testes não faz parte do desenvolvimento do software e está mais ligado a como o software é aceito enquanto um produto no mercado. Este tipo de testes não são cobertos aqui, mas também eles tendem a minimizar o medo e as falhas (comerciais, neste caso).

O cenário necessário é que todos os 4 tipos de teste sejam automatizados e verificados constantemente.  Este é o conceito de Integração Continua. Embora  o nome remeta aos testes de integração, todos os 4 tipos de teste podem ser avaliados continuamente.

A correção de um defeito nos requisitos funcionais é muito mais caro que um defeito na unidade de codificação.  Por esta razão os testes de aceitação devem ser escritos primeiro para testar se o requisito realmente atende o usuário. Caso não, o requisito é abandonado antes mesmo do código que o faz funcionar ter sido escrito. No inicio a codificação necessária ao funcionamento desse requisito é simulada com valores fixo para permitir que o requisito em si mesmo seja validado. Coisas como navegação, regras de validação de dados e outros tipos de input, são também validados por estes testes. Lembrar que estes testes são automatizados e executados continuamente. Se o requisito mudar, o teste terá que mudar em conformidade.

Os desenvolvedores partem então para a escrita do código real que irá prover aquelas funcionalidades. Na medida em que esse código é escrito também os seus respectivos testes de integração e unidade são escritos. Estes testes garantem que o código funciona, e os testes de aceitação garantem que este funcionamento está em conformidade com os requisitos funcionais.

Contudo, todo o sistema conta com requisitos n√£o funcionais que devem tamb√©m ser atendidos pelo software. Para testar estes, testes de sistema s√£o¬†inclu√≠dos. Estes testes completam os outros tr√™s, garantindo que o c√≥digo est√° em conformidade com os que os usu√°rios esperam tanto do ponto de vista do objetivo do software, como do ponto de vista da usabilidade, ¬†instala√ß√£o, manuten√ß√£o, atualiza√ß√£o, etc…

Entenda-se que dado um conjunto de testes para um software √© sempre¬†poss√≠vel¬†criar mais testes que completem esse conjunto. Em teoria este crescimento n√£o tem limite. Na pr√°tica ele tem que ser limitado pelo grau de confian√ßa que precisamos do software. Algumas m√©tricas como a cobertura (n√ļmero de linhas de¬†c√≥digo¬†executadas durante os testes) ¬†podem ajudar a definir esses graus de confian√ßa, ou testes¬†espec√≠ficos¬†podem ser desenhados para medir esse grau.

A raiz de todos os medos, que os desenvolvedores sentem face a um código que não escreveram, ou que não lembram que escreveram, é a falta de mecanismos automáticos de verificação da continuidade da consistência da funcionalidades.

A adi√ß√£o de testes automatizados √© uma necessidade do desenvolvimento de software, √© um “osso do of√≠cio” e n√£o uma op√ß√£o, embora possamos argumentar que a profundidade e tipo dos testes est√° relacionada a vari√°veis como o tempo espect√°vel para a vida do software, a facilidade em atualizar o¬†c√≥digo depois de instalado (imposs√≠vel¬†num software que controla uma sonda espacial, por exemplo) e o¬†n√≠vel¬†de risco associado √† falha do software (elevado em software de que depende a vida de algu√©m). Estas vari√°veis alteram o¬†pormenor¬†associado √† escrita e detalhe dos testes e n√£o o fato deles serem necess√°rios.

Agora pense: quão apavorado você fica ao mexer no código do seu projeto atual?

Um comentário para “A Raiz De Todos Os Medos”

  1. Parabéns, Sérgio ! Excelente artigo !

Comente

Artigos