TDD - Testes Depois do Deploy

Publicado por Murillo Flores no dia dev

Quando escrevi meu post de estréia aqui no Ship It!, no longíquo Agosto de 2015, eu disse que se pudésse dar só uma dica sobre o futuro, seria essa: “Use TDD”. De lá pra cá, entre o preço do Dólar e o futuro político do país muitas coisas mudaram, mas não mudou a minha certeza de que essa é a dica de ouro no mundo do desenvolvimento de software. E quando digo TDD estou me referindo, é claro, a Test Driven Development (Desenvolvimento Guiado por Testes), prática redescoberta e evangelizada por Kent Beck que você certamente já ouviu falar. Neste post, porém, eu quero falar sobre uma outra técnica que pode ajudar bastante a nos tornarmos desenvolvedores mais completos. Eu chamei esta técnica de Testes Depois do Deploy cuja sigla também é TDD.

Desenvolvedor que usa TDD saboreando um saboroso café Desenvolvedor que usa TDD saboreando um saboroso café

Este nome foi roubado das piadas internas comuns entre os desenvolvedores: “Nós praticamos TDD, a gente Testa Depois do Deploy”. Mas não deixe o nome te enganar: Eu não quero pregar o oposto do Test Driven Development nesta técnica. Ela não é sobre fazer os testes só depois de colocar o seu código no ar. Ela é sobre as verificações e perguntas (que com licença poética vou chamar de testes) que fazemos logo depois de colocar um novo trecho de código no ar, que devem ser complementares aos testes que criamos antes de escrever o código e aos testes automatizados de regressão.

PORQUE FAZER

Ainda que o desenvolvimento tenha sido guiado por testes, feito por um programador experiente, revisado por uma colega ainda mais experiente, validado pela equipe de qualidade e exaustivamente executado sobre o conjunto de testes automatizados, vale a máxima: Produção é produção, e vice-versa. Isso quer dizer que mesmo que seus códigos sejam sempre extremamente bem feitos, existe a possibilidade de que um código seu vá enfrentar uma situação única quando for ao ar, uma situação que você não conseguiu prever. Isso ocorre porque por mais que existam boas maneiras de se aproximar das condições encontradas no ambiente de produção é muito difícil reproduzí-lo fielmente.

Essa situações únicas, que só existem no ambiente de produção, podem fazer seu código ocasionar algum tipo de problema. Todas as técnicas que empregamos antes de colocar o código no ar visam diminuir a chance desse tipo de problema acontecer. Quase sempre elas conseguem isso, mas pela singularidade deste ambiente, é inevitável que alguma vez elas falhem. Quando isso acontecer, é melhor estar no controle da situação, conhecer o problema antes de que ele se torne crítico ou possa causar muitos danos, e é justamente nesse sentido que os Testes Depois do Deploy podem ajudar. Criando o hábito de executá-los é possível se adiantar em relação a problemas que muitas vezes podem ficar latentes por um período e culminar em algum problema maior depois de um tempo. Executar estes testes provavelmente também vai te fazer se sentir mais seguro e responsável sendo, em última análise, a atitude que se espera de um profissional sério e preocupado com aquilo que desenvolve e entrega.

Eu realmente acredito que devemos manter uma atitude profissional e estarmos legitimamente atentos aquilo que entregamos. Uma boa maneira de fazer isso é garantindo que a entrega cumpra com aquilo a que se propõe. Não há, porém, um padrão de facto sobre o que deve ser observado/testado quando acabamos de colocar um novo trecho no ar. Isso é dependente de contexto, claro, mas acredito que existam algumas regras que possam ser generalizadas e apresento a seguir aquilo que funciona bem como generalização para mim aqui na Resultados Digitais.

OS TESTES

O objetivo dos Testes Depois do Deploy é garantir que o que deveria funcionar, funciona em produção. Desse ponto de vista, existem alguns aspectos sobre os quais um trecho de código normalmente tem que funcionar. Em geral eu penso nesses três, em ordem: Aspectos funcionais, aspectos técnicos, e aspectos de uso. Para cada um deles eu executo um ou mais testes, dependendo de que código foi pro ar. Esses testes devem sempre ser executados diretamente na aplicação, em produção.

Aspectos funcionais

A primeira coisa que costumo verificar quando um novo código acabou de ficar disponível é se a função que esse trecho se dispõe a desempenhar, está de fato funcionando. Se o código, por exemplo, altera um componente de edição de texto, adicionando a este componente a possibilidade de destacar um texto selecionado com negrito, o primeiro teste será: selecionar um texto e destacá-lo com negrito. Esse teste em geral é bastante simples, parece óbvio demais, mas é super importante. Se esse teste falhar nenhum dos outros faz sentido. Além disso esse parece ser o teste mais intuitivo, que todo mundo executa para “checar se o deploy deu certo”.

Tão importante quanto, mas talvez não tão executado quanto, “o que não deveria funcionar não funciona” é o que eu sempre faço na sequência. Seguindo o mesmo exemplo, se o meu componente para destacar textos não deve destacar textos que já tenham um destaque com itálico, por exemplo, o meu segundo teste será: selecionar um texto que já está destacado com itálico tentar destacá-lo com negrito e constatar que isso não acontece.

Por fim eu tento sempre enxergar, conhecendo o código, o produto e as alterações que estão entrando no ar, quais são as outras funcionalidades do sistema que tem alguma dependência funcional com a área alterada pelo novo código. Verifico, então, se não houve nenhuma alteração acidental (ou cruzada) nessa área. Eu costumo pensar nesse teste como “Outras coisas que deveriam funcionar, que poderiam ter sido afetadas, mas continuam funcionando”. Ainda no mesmo exemplo, se uma outra área do sistema usa o mesmo componente de edição de texto que eu alterei, mas nessa área os textos não podem ter nenhum destaque, é importante verificar se a possibilidade de destacar um texto com negrito não foi adicionada a essa área acidentalmente.

Recapitulando, os testes funcionais devem garantir que:

  • O que deveria funcionar, funciona;
  • O que não deveria funcionar, não funciona;
  • Outras coisas que deveriam funcionar, e que poderiam ter sido afetadas, continuam funcionando.

Aspectos técnicos: Sistemas de apoio

Atingido esse ponto, sabemos que os aspectos funcionais estão corretos. Ou seja, para o usuário final a aplicação está funcionando. Agora é importante verificar se não introduzimos nenhum erro no sistema não perceptível para o usuário, ou que pelo menos não o impede de realizar nenhuma tarefa, mas que pode se tornar um problema no futuro.

Esse tipo de erro em geral está associado a processos que rodam em background no sistema principal, ou a sistemas paralelos que de alguma maneira dependem dos dados criados ou manipulados no sistema principal.

Seguindo com o exemplo do editor vamos supor que o texto editado pelo usuário seja enviado, após a edição ter sido salva, para um serviço de indexação que processará esse texto e criará indíces para facilitar as buscas. Esse serviço pode ser incapaz de processar textos com destaque negrito. Desse modo a funcionalidade de destaque na edição estará correta para o usuário, mas o serviço de indexação começará a falhar (afetando as buscas).

Por não serem perceptíveis para o usuário no curto prazo esse tipo de problema geralmente se manifesta primeiro em sistemas de monitoramento ou em logs da aplicação. Por isso todos os testes de aspectos técnicos que eu costumo executar se baseiam em analisar esses sistemas de monitoramento e log, procurando por erros, exceções, logs estranhos e coisas do gênero. Alguns dos sistemas que usamos aqui na Resultados Digitais, que são os que eu verifico sempre depois de colocar algo novo no ar são:

  • Rollbar
  • New Relic errors
  • Logentries
  • CloudWatch

Cada sistema terá naturalmente um conjunto diferente de ferramentas de monitoramento e log que se deve analisar. O importante ao realizar esta análise é manter sempre o olhar crítico e desconfiar de tudo que possa vir a se tornar um problema, analisando a causa raiz para tentar correlacioná-la com aquilo que acabou de ir pro ar.

Resumindo, as verificações que costumo fazer ao analisar esses sistemas de monitoramento são:

  • Verificar que nenhuma exceção começou a ser disparada em serviços dependentes ou processos em background;
  • Verificar que não existe nenhum novo log de erro relacionado ao que acaba de ir para o ar;
  • Verificar que as métricas de “saúde” (tempo de resposta, quantidade de falhas, etc) dos serviços relaciondos continuam em níveis normais.

Aspectos tecnicos: Performance

Se parece tudo certo funcionalmente e tecnicamente é hora de considerar o tempo como uma variável do sistema. A ideia destes testes que chamo aqui de “Testes dos aspectos técnicos de performance” é entender como o código que acaba de ir para o ar se comportará e afetará o funcionamento do sistema ao longo do tempo. No caso do RD Station, existem algumas medidas que costumo ter sempre em mente na hora de validar o código sobre esse aspecto: APDEX, tempo de resposta e tempos de processamento por camada da aplicação. Manter essas métricas em níveis aceitáveis é super importante, tanto para melhorar a experiência dos usuários quanto para garantir a manutenibilidade do sistema a longo prazo. Ao analisar cada uma dessas métricas, é importante se perguntar coisas como:

  • Essa métrica piorou depois que o novo código foi pro ar?
  • Como ela se comportará quando o sistema estiver em uma situação de stresse (alta carga, por exemplo)?

Nem sempre o efeito sobre essas métricas é imediato. Muitas vezes uma alteração parece inofensiva logo que entra no ar, mas em uma situação de maior carga pode afetar diretamente a performance da aplicação. Por isso é necessário um olhar atento e um bom conhecimento do sistema para conseguir projetar os efeitos de pequenas alterações ao longo do tempo.

Mas no final, o esforço é recompensador. Levar essas análises a sério e entender de verdade como o código que colocamos no ar todos os dias pode afetar a performance contribui para que conheçamos cada vez melhor os pontos fortes e as fraquezas do sistema e para que, quando a hora chegar, tenhamos conhecimento suficiente para desenvolver o que for necessário para escalar para 10 vezes, e não 10%.

As ferramentas que usamos por aqui para acompanhar essas métricas são principalmente o Libratto e o NewRelic. Essas ferramentas indicam o impacto sobre a performance e a partir delas é fácil refletir tanto sobre o histórico quanto projetar o comportamento futuro dessas métricas.

O propósito destes testes, por tanto, é conseguir responder a pergunta:

  • O código que acabei de colocar no ar pode afetar negativamente a performance do sistema?

Aspectos de uso

Por fim, mas de maneira nenhuma menos importante, é preciso verificar que tudo que funcionou nos seus próprios testes ao longo do processo de desenvolvimento e deploy, também está funcionando para os usuários. A pergunta por trás desse “teste” é: Será que os usuários ainda estão conseguindo usar o sistema para realizar o que precisam?

Uma boa maneira de verificar isso é acompanhar métricas de uso em tempo real: quantas pessoas estão usando o sistema, quantas vezes a ação X está sendo executada, quantos comentários, etc. Na maioria das vezes essas métricas estão disponíveis e são fáceis de acessar, mas se não estiverem, os logs do sistema podem apontar também se o uso continua normal. Com eles, claro, será necessário um pouco mais de esforço, mas pode funcionar.

Uma outra excelente maneira de verificar se nenhum impacto sobre o uso foi causado é acompanhar os canais de suporte e feedback aos usuários. Tipicamente esses canais incluem um endereço de email para o qual as dúvidas são enviadas. Olhar os últimos “tickets” que estão chegando é um excelente meio de começar. Algumas vezes conversas por chat também são um canal disponível, e se for o caso vale conferir as últimas dúvidas que chegaram por este canal, já que ele costuma ser mais rápido do que o email.

Resumindo, a intenção destes testes é responder a pergunta:

  • Os usuários continuam conseguindo usar o sistema sem dificuldades?

Sobre ser um bom desenvolvedor

Ser um bom desenvolvedor vai muito além de conhecer muitas linguagens de programação ou conhecer em detalhe o funcionamento de uma delas. Ser um bom desenvolvedor é mais do que dominar um assunto e virar uma referência nele. É mais do que saber escrever de cabeça a melhor regex para reconhecer um endereço de email. Ser um bom desenvolvedor, para mim, é se importar genuinamente com a qualidade de tudo que se está desenvolvendo e entregando, em todas as fases. Nessa visão o trabalho não está concluído quando o código foi pro ar, ele apenas entra em uma nova fase onde devemos mais uma vez conseguir entender que o que estamos entregando faz sentido e tem qualidade.

Fazer testes e verificações depois do deploy, muitas vezes desafiando as minhas próprias hipóteses, sempre funcionou muito bem para mim nesse sentido, e espero que ter lido sobre possa ter te ajudado de alguma maneira também.

P.S.: Se fizer sentido para você executar os Testes Depois do Deploy seguindo uma técnica parecida com a que uso, as perguntas que costumo fazer e que foram aparecendo ao longo do post são as seguintes:

Aspectos funcionais

  • O que deveria funcionar, funciona?
  • O que não deveria funcionar, não funciona?
  • Outras coisas que deveriam funcionar, e que poderiam ter sido afetadas, continuam funcionando?

Aspectos técnicos: Sistemas de apoio

  • Alguma nova exceção começou a ser disparada em serviços dependentes ou processos em background?
  • Surgiu algum novo log de erro relacionado ao que acaba de ir para o ar?
  • As métricas de “saúde” (tempo de resposta, quantidade de falhas, etc) dos serviços relacionados continuam em níveis normais?

Aspectos tecnicos: Performance

  • O código que acabei de colocar no ar pode afetar negativamente a performance do sistema?

Aspectos de uso

  • Os usuários continuam conseguindo usar o sistema sem dificuldades?

Murillo Flores

Murillo Flores

Full Stack Developer

Comentários