Engenharia

Toda página autenticada deve viver dentro do shell

Uma página que "funciona" mas derruba a navegação ao seu redor é uma regressão mesmo quando todos os testes estão verdes.

ASR

Apollo Space Research

Apollo Space

· 11 min de leitura

Você clica numa página de detalhe e a sidebar desaparece. O org switcher sumiu. O header com sua conta e sua busca sumiu. A página em si renderiza bem, os dados carregam, os botões funcionam, os testes estão verdes, mas agora você está em pé sobre um único painel flutuante sem nenhuma forma de voltar a não ser o botão de voltar do navegador. Você não saiu do app. O app saiu de você.

Todo time que lança um produto com um shell de navegação esbarra nisso, e a maioria esbarra mais de uma vez. Uma página nova é construída como sua própria rota de nível superior, ela passa no review porque a página funciona, e quietamente rasga um buraco no produto na primeira vez que uma pessoa real navega até ela.

Uma página que “funciona” mas derruba a navegação ao seu redor é uma regressão mesmo quando todos os testes estão verdes.

Este post é sobre por que isso continua acontecendo, por que sua suíte de testes é a última coisa que vai pegar isso, e a única regra que tornamos inegociável para acabar com isso.

A versão ingênua: uma página é uma rota, uma rota é uma página

O modelo mental óbvio é o que todo router te ensina no primeiro dia. Uma página é uma URL. Você adiciona uma página adicionando uma rota. A rota aponta para um componente, o componente renderiza a tela, e pronto. Página de configurações nova? Rota nova. View de detalhe nova? Rota nova. É limpo, é componível, e mapeia perfeitamente em como o framework pensa.

Também funciona perfeitamente até o exato momento em que não funciona.

O problema é que uma rota renderiza o que quer que você aponte para ela, e nada mais. Se você aponta uma rota de nível superior para um componente de página nu, o framework vai felizmente renderizar exatamente aquilo: a página, sozinha, ocupando a viewport inteira. A sidebar, o contexto de org, o header, todo o enquadramento que faz a tela parecer parte de uma aplicação, só existem se algum componente pai os colocou ali. Uma rota de nível superior não tem pai fazendo isso. Então a página renderiza, os testes confirmam que renderizou, e a navegação está simplesmente ausente. Não quebrada. Ausente. O que é pior, porque ausência não lança erro.

Aqui está a parte que torna isso durável: a página nua é o caminho de menor resistência. É mais rápido fazer o scaffold de uma rota standalone do que costurar uma tela nova num layout existente que você não escreveu. O ferramental te empurra para isso. O demo de qualquer componente de UI o mostra flutuando isolado, porque é assim que você demonstra um componente. Então a versão errada é a mais fácil de construir, a mais fácil de copiar de um tutorial, e a mais fácil de mergear, e o custo só aparece quando alguém clica.

Duas formas de montar uma tela: uma rota nua de nível superior renderiza a página sozinha sem sidebar, sem org switcher, sem header, um beco sem saída; a rota envolvida pelo shell renderiza a mesma página enquadrada pela navegação que permite a uma pessoa seguir adiante a partir dela.

Por que a suíte de testes é o vigia errado

Você pensaria que um teste pegaria uma sidebar faltando. Quase nunca pega, e a razão é estrutural, não uma falha de diligência.

Um teste de componente monta a página isolada de propósito, esse é o ponto inteiro de um teste unitário, checar uma coisa sem arrastar o universo junto. Então o teste renderiza a página de detalhe sozinha, garante que os dados aparecem e os botões disparam, e fica verde. Ele está correto. Está testando exatamente o que foi escrito para testar. A coisa que ele não consegue ver é tudo que deliberadamente deixou de fora: a página sempre deveria renderizar dentro de um shell, e um teste que a monta sozinha apagou exatamente o contexto onde o bug mora.

O bug não está na página. Está na relação da página com tudo ao seu redor. Uma página que “funciona” mas derruba a navegação ao seu redor é uma regressão mesmo quando todos os testes estão verdes, e este é exatamente o tipo de regressão que uma suíte verde é construída para não notar.

Essa é uma categoria inteira de defeito para a qual testes unitários são cegos por construção. O componente é individualmente perfeito e coletivamente errado. Seu número de cobertura sobe enquanto o produto fica um pouco mais quebrado, porque cobertura mede se cada peça funciona isolada, e esta falha é feita de peças que cada uma funciona isolada. Testes verdes numa página autocontida te dizem que a página é autocontida. Eles nunca iam te dizer que ela não deveria ser.

É por isso que “os testes passam” é a frase mais perigosa no trabalho de front-end. Ela é verdadeira. Também está respondendo uma pergunta que ninguém fez. A pergunta nunca foi esta página renderiza, era uma pessoa consegue alcançar esta página, usá-la e sair dela sem ficar presa, e essa pergunta só tem resposta quando você coloca a página de volta na aplicação à qual ela pertence e clica ao redor como um humano faria.

Cobertura te diz que cada parte funciona. Não diz nada sobre se as partes somam para um lugar onde uma pessoa pode ficar de pé.

A regra: o shell enquadra tudo, a página é um convidado

Então escrevemos a regra e demos a ela peso real. Toda página autenticada renderiza dentro do shell da aplicação, nunca como uma rota nua, sem sidebar. O shell é o anfitrião. A página é um convidado dentro dele.

Concretamente, o shell é a parte do produto que nunca vai embora enquanto você está logado: a sidebar, o org switcher, o header. Ele é dono da navegação. Uma tela nova não pode ser irmã do shell, montada ao lado dele no topo do router. Ela é montada dentro dele, como uma seção que a sidebar já sabe alcançar, ou como um view-state dentro de uma área enquadrada existente. A página herda seu enquadramento de graça porque o enquadramento nunca foi trabalho da página prover.

Isso inverte o padrão. No modelo ingênuo, o enquadramento é algo de que você se lembra de adicionar, e esquecer é silencioso. No modelo do shell, o enquadramento é algo que você teria que deliberadamente arrancar, e arrancá-lo é barulhento. O caminho fácil e o caminho correto se tornam o mesmo caminho. Você não pode acidentalmente lançar uma página flutuante porque não existe slot de nível superior para flutuá-la, a página só tem um lar dentro do shell.

Uma tela nova é montada como convidada dentro do shell em vez de como irmã ao lado dele: o shell é dono da sidebar, do org switcher e do header, e a página se encaixa numa seção que a navegação já alcança.

Há uma exceção honesta, e nomeá-la afia a regra em vez de enfraquecê-la. Algumas poucas superfícies autenticadas são feitas para serem em tela cheia sem sidebar, um inspetor focado que você abre para estudar uma coisa e depois fecha, como uma view de trace de página inteira. Essa é uma decisão deliberada, modelada assim de propósito, com um caminho claro de volta. É o oposto do bug, que é uma página sem sidebar por acidente e que não oferece saída. O teste para a exceção é simples: alguém decidiu que esta tela deveria ficar sozinha, ou ela só calhou de ficar? Se ninguém decidiu, é o bug.

Como você de fato pega isso: seja o usuário, na superfície real

Se testes não conseguem ver essa classe de bug, algo tem que ver. A coisa que vê é uma pessoa, ou um agent agindo como uma, clicando através do produto deployado da forma que um cliente vai clicar.

A ideia ingênua de “pronto” para uma página nova é: ela renderiza, os testes passam, mergeia. Substituímos isso por uma barra mais dura específica para este modo de falha. Antes de uma página contar como construída, três coisas têm que ser verdade, em ordem. Ela tem um lar na navegação, você consegue nomear a seção da sidebar à qual ela pertence. Ela renderiza dentro do shell, o enquadramento está ali porque o shell o colocou, não porque a página o reconstruiu. E alguém a alcançou pela navegação no produto rodando e confirmou que o shell ficou no lugar o caminho todo: a sidebar não desapareceu, o caminho de volta era óbvio, a página nunca foi um beco sem saída.

Esse último passo é o que não pode ser falsificado. Uma página pode passar em todos os testes e ainda assim deixar um usuário preso, e o único instrumento que detecta o ficar preso é a experiência de estar preso, o que significa de fato fazer o clique, na superfície real, e perceber se você consegue sair. Não aceitamos “deveria funcionar”. Aceitamos “naveguei até ela, a sidebar se manteve, e eu consegui sair”. Essas são afirmações diferentes, e só a segunda é evidência. Uma página que “funciona” mas derruba a navegação ao seu redor é uma regressão mesmo quando todos os testes estão verdes, e o único lugar onde essa verdade fica visível é o próprio clique.

É tentador tratar isso como frescura. Não é. Uma página flutuante é um bug pequeno que produz um sentimento grande, o sentimento de um app que não se sustenta junto, onde um clique errado te derruba em algum lugar que o produto esqueceu de mobiliar. Usuários não registram isso como um bug. Registram como essa coisa parece quebrada, e estão certos da forma que mais importa, que é a única forma que importa: como foi usá-la.

A virada: uma aplicação é um lugar, e um lugar tem que se sustentar

O que é um produto, de verdade, debaixo das features?

É um lugar onde uma pessoa pode se mover por dentro sem se perder. Toda aplicação séria, as que parecem sólidas, as que você confia trabalho de verdade, compartilha uma propriedade quieta: onde quer que você esteja dentro dela, você sempre consegue dizer onde está e sempre consegue ver como ir para outro lugar. A navegação está sempre ali. Essa constância não é decoração em cima do produto. Ela é o produto, no sentido de que é o que transforma uma pilha de telas num único lugar coerente que você pode habitar. Tire-a de uma tela e você não lançou uma página com uma falha menor. Você socou um buraco no chão do lugar, e a pessoa que pisa nele para de confiar no chão.

É por isso que esta regra merece seu status de inegociável enquanto regras mais sofisticadas não. Não é sobre uma sidebar. É sobre se a coisa que estamos construindo se sustenta junta como um lugar, se uma pessoa pode vagar por qualquer canto dela e ainda se sentir amparada pelo mesmo enquadramento, ainda ver o caminho de casa. Uma página que renderiza perfeitamente e quebra esse sentimento falhou no único trabalho que uma tela numa aplicação tem, que nunca foi só exibir seus dados. Era ser um cômodo num prédio em que você consegue achar o caminho.

As features vão continuar se multiplicando. Sempre vai haver mais uma página para adicionar, e a forma rápida de adicioná-la sempre vai ser a flutuante. A regra se mantém mesmo assim: a página vive dentro do shell, ou não é lançada, porque o momento em que deixamos uma tela derrubar o enquadramento para economizar uma hora é o momento em que o produto para de ser um lugar e começa a ser uma pilha.


É isso que estamos construindo na Apollo Space: um sistema operacional para trabalho de verdade, onde toda superfície que você alcança ainda parece o mesmo prédio em que você entrou. Se você já clicou um link a mais e se viu de pé numa página sem caminho de volta, você já sabe por que o shell não é a parte que você pula, é a parte que faz o resto ser um lugar que vale a pena estar.

A Apollo cuida da operação repetitiva da sua empresa pro seu time não precisar.

Entre na lista de espera: acesso antecipado, preço de usuário fundador e um lugar na primeira fila enquanto a gente constrói.

Entrar na lista de espera