Pular para conteúdo
Olá 👋🏾, eu sou
Victor
Magalhães
ele/dele
vhfmag@pm.me
vidas negras importam, vidas LGBT importam e é fascismo sim
Mídias sociais
Tema
Tema

Desenvolvendo o hotsite da Jusbrasil sobre Covid19

No longíquo dia 12 de março, a Jusbrasil fechou seus escritórios devido à disseminação do COVID19, 6 dias antes da prefeitura de Salvador decretar situação de emergência. A crise, hoje tão clara e presente que é impossível ignorar, era então uma ameaça assustadora, mas distante.

Como de praxe com tudo que cresce exponencialmente, essa percepção mudou rápido e, duas semanas depois, todos nós já estávamos imersos em gráficos em escala logarítimica, falando em achatar a curva, insistindo para que nossa família, amigos e vizinhos se cuidassem e temorosos pelo emprego de muitos. Afinal, as empresas podiam ou não suspender o contrato de trabalho? E reduzir salários? Quais são os impactos das férias coletivas? Esse tal auxílio emergencial, sai mesmo? Quem tem direito?

No meio de todas essas incertezas, ficou clara a necessidade de centralizar informação curada e confiável tirando as principais dúvidas da população sobre a pandemia. Não só isso, mas também como tribunais pelo Brasil estão operando, se estão; como tribunais influentes estão decidindo; os principais materiais, fontes de referência e apps oficiais; e, claro, bate-papos com referências em direito e empreendedorismo sobre como lidar com a crise.

E assim nasceu o projeto do hotsite do COVID19 do Jusbrasil: nosso portal oficial de informação jurídica relacionada ao coronavírus, tema desse post.

Requisitos

Depois de colocar no ar um MVP que contemplava o funcionamento de tribunais (arquivado aqui) feito com a ferramenta no-code webflow, nos debruçamos sobre o escopo do projeto e seus requisitos, técnicos ou não.

O hotsite devia:

  • Disponibilizar:
    • Um FAQ curado
    • Informações atualizadas sobre o funcionamento dos principais tribunais do país
    • Nossas lives sobre o tema
    • Demais materiais relacionados
  • Ser fácil de alimentar sem a ajuda de um desenvolvedor
  • Ter bom SEO
  • Estar pronto o quanto antes

Com isso em mãos e o projeto aprovado, era hora de pôr a mão na massa.

A stack do projeto

Finalmente chegamos à parte técnica! O site foi feito em Typescript usando o framework Next.js, baseado em React, e alimentado pelo CMS Strapi. Pelo prazo apertado e pelo site ser mais uma teia de documentos do que uma aplicação, optamos por não implementar testes automatizados. A seguir, explico um pouco do processo de decisão por trás de nossas escolhas.

Next.js

Optamos por construir o site usando o Next.js, o que nos permitiu ter um site feito com React pronto para SSR com o mínimo de configuração, nos poupando tempo de desenvolvimento por nos permitir começar mais rápido a efetivamente codar e usando uma stack familiar aos desenvolvedores da empresa. Nossa experiência com o framework foi, em geral, ótima, bastando rodar npx create-next-app covid19 --example with-relay-modern para ter um boilerplate funcional se conectando à nossa API; bastaram minutos para estarmos com um projeto funcional onde podíamos colaborar.

Explicando as buzzwords:

  • React: uma biblioteca de renderização para a Web baseada em Javascript feita para rodar no navegador (client-side), diferente de Ruby on Rails e Laravel, que geram o HTML no servidor
  • Server-Side Rendering (SSR): conceito nascido junto com os frameworks e bibliotecas client-side, como React, Angular, Vue, Backbone, Svelte, etc. É a ideia de gerar o HTML no servidor, mesmo ao usar bibliotecas client-site
  • Next.js: um framework web opinionado baseado no React que torna fácil implementar SSR e já deixa seu web app pronto para boas práticas (precarrega links quando se passa o mouse por cima, facilita de implementar service workers, etc)

Os únicos contras que eu citaria são:

O Next.js te obriga a usar CSS Modules, uma tecnologia não utilizada no Jusbrasil

A motivação para tal é evitar bugs onde a ordem de inclusão de arquivos CSS muda o resultado final: digamos que um projeto tem 2 páginas (home e sobre) e 3 arquivos CSS (global.css, home.css e sobre.css), e que o usuário 1 acessa primeiro a home e depois a sobre (home.css é inserido antes de sobre.css) e que o usuário 2 acessa primeiro a sobre e depois a home (home.css é inserido depois de sobre.css). Nesse cenário, regras CSS com seletores de mesma especificidade aplicados por home.css e sobre.css nos mesmos elementos poderiam ter resultados visualmente diferentes para os dois usuários. Por esse ser um problema comum com SPAs em geral e com o Next.js em particular, o framework adotou a restrição na versão 9.

Essa mudança teve impacto no tempo necessário para um desenvolvedor ser produtivo no projeto. CSS Modules são fáceis o suficiente de usar, então isso não foi um problema tão grande, mas gostaríamos que tivesse sido possível desabilitar a restrição através de configuração, permitindo que empresas mantenham suas stacks e convenções ao adotar o framework.

Links no Next.js são… confusos

Antes de adotar o framework, eu consultei alguns amigos que já o tinham usado para saber como tinha sido a experiência e se eles recomendavam. A maioria dos feedbacks foi positiva, mas um em particular foi uma expressão inquestionável de um trauma: Next.js é o pior framework que existe!!!. A origem do trauma? Links que não funcionavam.

Para navegar internamente em um site feito com Next.js, não se pode simplesmente usar a âncora do HTML (<a>), pelo menos não sem abrir mão da experiência de um SPA. Deve-se usar o componente Link fornecido pelo framework. Não bastando isso, não se pode apenas incluir a URL da página de destino diretamente se esta for uma rota dinâmica.

Imagine, por exemplo, que você tem uma rota /produtos/[id] (o que equivale a um arquivo pages/produtos/[id].js), a que se deseja linkar. Talvez nosso impulso inicial fosse usar <a href="/produtos/ventilador">Ventilador</a>, mas precisaríamos usar o componente Link (<Link href="/produtos/ventilador"><a>Ventilador</a></Link>) e adequar seu uso à rota dinâmica (<Link href="/produtos/[id]" as="/produtos/ventilador"><a>Ventilador</a></Link>).

O código é maior e menos legível, usos incorretos de Link passavam pelo code review e concorrentes como o Sapper resolvem esse problema com âncoras comuns (e contam vantagem por isso).

Strapi

Como o conteúdo do site precisaria ser modificado com frequência e alimentado por diferentes times, percebemos de cara que precisaríamos de um CMS headless (e você acabou de marcar duas palavras no bingo das buzzwords). CMS ou Content Management System é um sistema que te permite criar, deletar e modificar conteúdo, como o Wordpress e o Drupal. Tradicionalmente, um CMS é associado a um site em particular e é o responsável por gerá-lo, como é o caso das duas ferramentas. Quando esse não é o caso e cabe à página pegar o conteúdo através de uma API, dizemos que esse CMS é headless.

Existem diversas opções, desde SaaS como o Contentful até opções open source e self-hosted. Optamos pelo Strapi pela flexibilidade do CMS e por ser possível hospedá-lo na nossa infraestrutura, evitando gastos desnecessários.

A experiência foi positiva: os tipos de campo disponíveis atenderam a maior parte das nossas necessidades, a interface é intuitiva e ele aguentou nossa carga com uma performance satisfatória. Como sempre, encontramos alguns problemas:

Não existem campos do tipo URL

Nós ficaríamos um pouco mais seguros sabendo que o Strapi está validando por nós que não temos campos de URL sendo preenchidos incorretamente.

Os tipos de campo do Strapi
Devíamos ter criado uma instância do Strapi de dentro do projeto

Nós escolhemos usar uma imagem docker do CMS pra acelerar o deploy do serviço no nosso cluster Kubernetes. Ledo engano. Por causa dessa escolha, dado o tempo limitado pra execução, tivemos dificuldades em mudar configurações (ex: tamanho máximo de upload), em instalar plugins (ex: um otimizador de imagens) e tivemos problemas ao gerenciar a instância dentro de nossa infraestrutura, perdendo os metadados (os dados continuavam no banco de dados, mas o Strapi não sabia que as coleções existiam).

O último ponto é interessante o suficiente pra merecer uma explicação: como o banco de dados fica em um persistent volume, ele persistia; mas os metadados parecem ser armazenados junto com a aplicação, e fazer deploy de uma instância mudando suas configurações nos fez perder os metadados e precisar recriar as coleções, uma a uma, para ter acesso aos dados que estavam persistidos no banco de dados.

Typescript

Assim como a mão de Midas transforma tudo o que toca em ouro, eu gostaria de deixar todo projeto que eu toco estaticamente tipado. Então, como eu coordenei esse projeto, ele é feito em Typescript. 🤷 Essa é uma decisão um tanto controversa na comunidade Javascript:

Por um lado, como Javascript não é estaticamente tipado, Typescript pode ser uma complicação a mais com benefícios limitados. Eu discordo porque mesmo sem checagem em tempo de execução, tipos são uma ferramenta de comunicação. Perguntas como “que campos essa API retorna?” e “que propriedades esse componente espera?”, que envolveriam procurar a resposta no código fonte ou em outros trechos do código que usem a API ou o componente, podem ser facilmente respondidas pelo seu editor com Typescript, sem mudança de contexto. Sem tipo algum, mudanças de contrato (ex: acrescentar ou renomear uma propriedade num componente React) ficam implícitas no código: elas só podem ser vistas quando o código consome o componente e faz uso da nova propriedade. Com tipos, o contrato é declarado em código.

Outra vantagem é que se pode comunicar uma restrição para o desenvolvedor e para o compilador, dificultando que bugs cheguem em produção. Por exemplo: imagine que, num site de rastreamento de encomendas, a API retorne um status (WAITING, SENT, DELIVERED) e um objeto mapeie cada valor para um texto a ser exibido na UI (“esperando ser enviado”, etc); agora, imagine que um novo status é adicionado (WAITING_TAKEOUT). Com tipos, o compilador pode avisar em tempo de desenvolvimento que o objeto precisa mapear o novo valor de status!

Por outro lado, entusiastas de sistemas fortes de tipo consideram o Typescript permissivo demais. Eu posso concordar, mas isso é uma feature, não um bug. Javascript é permissivo, então uma linguagem que se propõe a adicionar um sistema de tipos estáticos em cima de Javascript também precisa ser.

Dito isso, o Typescript certamente pode ser uma complicação, especialmente quando se trata de dados vindos de uma API. Diferentes métodos retornam diferentes propriedades de um mesmo objeto, e alguns componentes aceitam todas as variantes, outros só algumas. Para resolver isso, acabei criando um sistema de tipos excessivamente complexo para os dados vindos da API do Strapi.

Como o tipo dependia da profundidade de um tipo no retorno da API (ex: se eu pego posts, que tem autores, o post tem autores, mas cada autor tem sua lista de posts como ids), a complexidade aumentou, e de repente alguns componentes aceitavam o tipo raiz, outros o tipo aninhado. Depois, adicionamos parsing (ex: data vem como string, o parser transforma em Date) e adicionamos mais dois tipos no bolo. Adicionar um tipo novo era confuso e complexo.

Me fez ter saudade do GraphQL.

De todo modo, tipos ajudaram a reduzir o trabalho necessário pra revisar o código de 12 pessoas trabalhando simultaneamente num projeto sem testes.

Resultado

Duas semanas e 260 commits depois, finalmente lançamos o site no dia 9 de abril! Desde então, mais de 3 mil pessoas se informam toda semana sobre seus direitos e sobre o prazo de seus processos e tem acesso a conteúdos relevantes para nosso contexto de pandemia. E assim o Jusbrasil segue fechando o justice gap! 🤘


Esse artigo foi postado originalmente no blog de Victor Magalhães.