Como coloquei um MVP em produção três dias

E como não foi preciso abrir mão de qualidade de software para isso.

Maniero
5 min readAug 26, 2020
Photo by Nicolas Hoizey on Unsplash

Antes de começar gostaria de trazer três disclaimers:

  1. Foi possível definir um MVP com o M de mínimo levado ao extremo (o que acho que deveria ser o padrão, mas muita gente tem dificuldade de entender isso 😜). Então com pouquíssimo desenvolvimento foi possível colocar algo em produção.
  2. Esse é um artigo sobre software e qualidade, então não vou falar muito do processo de descoberta e como gerei as hipóteses que pretendo validar com esse MVP.
  3. Qualidade tem várias faces. Vou limitar qualidade a testes automatizados que permitem e dão segurança para evoluir o produto, CI e CD.

Depois que escrevi “A utopia da perfeição tecnológica” eu fiquei pensando em trazer um exemplo prático de que eu escrevi e de preferência open source pra que vocês pudessem ter acesso ao código.

O Produto: Let me contrib!

O Let me Contrib! é uma plataforma que tem como objetivo fazer o match das habilidades de pessoas desenvolvedoras e issues no Github (seria o Tinder do Github?). Até o momento que eu escrevi esse artigo haviam três features principais:

  1. Cadastrar uma issue (importando do Github);
  2. Votar a complexidade de uma issue;
  3. Buscar issues por linguagem e complexidade.

É necessário saber onde investir o tempo

Eu poderia ter elaborado e preparado toda uma plataforma para habilitar entrega contínua mas resolvi isso com dois cliques usando Heroku. “Enable automatic deploys” e em seguida “Wait for CI to pass before deploy”. Pronto! Habemus CD.

O mesmo vale para CI. Poderia ter criar instalado um servidor com Jenkins ou GoCD. Mas com certeza isso não é necessário para um MVP (e é provável que isso não seja necessário na vida: Leia sobre a síndrome do Não Inventado aqui).

Ainda falando em escolhas de ferramenta, eu optei em não usar nenhum framework front-end. Estou usando o bom e velho HTML gerado do lado do servidor. Utilizei o framework Django por causa do sistema de migração de banco de dados. Assim, não precisei me preocupar muito com banco de dados.

Adiando decisões

Todas as decisões que foram possíveis de ser adiadas eu adiei. Qual a arquitetura eu segui? A que funciona. E que funciona rápido! Design system? Nah, só vou ter dois botões e uns campos. Se levei no máximo 5 minutos pra fazer o CSS desses campos foi muito. Qual o throughput da aplicação? Não faço a mínima ideia. E se der problema, coloco um cache na frente.

Mesmo adiando todas essas decisões ainda sim o projeto está com uma excelente pontuação no lighthouse.

Estratégia de testes: Invertendo a Piramid

Photo by Moritz Mentges on Unsplash

Dentre as decisões que eu decidi adiar, está uma estratégia de testes escalável. Quando falamos de testes é sabido que na base da pirâmide estão os testes mais baratos com testes unitários. No topo estão os testes de mais valor, porém, mais caros, como por exemplo end-to-end (E2E).

Como eu disse no começo do artigo, eu não abri mão de escrever testes, afinal, eu entrego muito mais rápido com eles. Porém eu decidi inverter a pirâmide e escrever majoritariamente testes E2E e testes de integração. O motivo?

Testes unitários tendem a ser um contrato da arquitetura.

Quando falamos de testes unitários, em especial os com uma granularidade bem pequena e com mocks, estamos falando do contrato da arquitetura. Se eu testo uma classe de forma isolada, estou tornando essa classe um componente importante da minha arquitetura que pode ser utilizado isoladamente por diversos clientes.

O problema é: Uma das decisões que eu adiei pra conseguir entregar rápido foi a arquitetura. Eu realmente não acredito que eu vou seguir (se o projeto der certo) com a arquitetura do jeito que está. Portanto, não quero testar o comportamento de pequenos componentes da minha arquitetura e sim o comportamento do software como um todo já que o comportamento em si não deve mudar.

Quando eu realmente tomar a decisão sob qual o modelo arquitetônico vou seguir, os testes não me atrapalharão durante o refactor. Já os testes de integração e e2e vão me garantir que o software está funcionando indiferente do modelo de arquitetura que eu segui.

Óbvio que esse modelo de testes não estala. Hoje eu tenho basicamente 37 testes e eles levam certa de 40 segundos para executar no CI. Conforme o projeto evolua, essa estratégia vai ficar cara tanto porque o setup vai ser bastante complicado, bem como os testes vão demorar muito.

Mas a curto prazo, esses testes servem para guiar meu desenvolvimento (sim TDD com e2e em aplicações simples é completamente possível) e me dar segurança para reescrever e redesenhar a arquitetura.

Mas não teve nenhum teste unitário?

Teve sim. Algumas partes eu decidi testar unitariamente, principalmente as que eu sabia que haveriam muitos casos de testes.

Dois exemplos:

Esse último, por exemplo, eu me arrependo um pouco de ter escrito a nível do modelo, eu não testaria E2E validando o que o usuário vê na tela para todos os casos, mas acho que seria melhor se eu tivesse escrito um teste que faz uma request para o endpoint que faz a votação. Afinal, eu vejo que possivelmente vou alterar essa implementação.

Até quando posso adiar decisões?

Adie o máximo possível. Mas cuidados com os efeitos colaterais dessa estratégia. Isso tudo foi muito possível porque eu optei em utilizar uma arquitetura super simples e que me atendia muito bem nesse momento. Conforme o produto evolui, é importante evoluir a arquitetura e consequentemente evoluir a estratégia de testes.

O que vem a seguir?

Provavelmente nada porque acho que o produto não vai atingir nenhuma OKR que eu defini. Mas certamente os próximos passos seriam em direção de definir uma estratégia de evolução do produto para suportar os objetivos a curto e médio prazo.

--

--