Olá Habr! Apresento a sua atenção uma tradução do
post de Dan Abramov
, “Os elementos da engenharia da interface do usuário”, sobre problemas e tarefas contemporâneos que devem ser resolvidos em uma boa interface. O autor examina os problemas fundamentais no desenvolvimento de interfaces, cuja interpretação e solução por si só - sem o uso de bibliotecas e estruturas prontas - podem fornecer uma compreensão aprofundada das soluções no campo de desenvolvimento de front-end existente no mercado.

Nota do tradutorO texto é escrito e traduzido na primeira pessoa. O autor do original em inglês é
Dan Abramov , o desenvolvedor da biblioteca React para criar interfaces de usuário complexas.
Na minha
última publicação, escrevi sobre o quão importante é ser capaz de reconhecer lacunas no próprio conhecimento. Pode ter parecido que eu estava lhe oferecendo desculpas para ser mediocridade. Nem um pouco! Mas, de fato, nosso conhecimento é um tópico abrangente de conversação.
Estou convencido de que você pode começar o seu conhecimento "imediatamente" e não há necessidade de estudar a tecnologia (pilha tecnológica para desenvolvimento da web - aprox. Tradutor) em uma determinada ordem. Mas também acredito que o acúmulo de experiência e habilidades profissionais no campo escolhido é de grande importância. Pessoalmente, sempre estive mais interessado em criar interfaces de usuário.
E me perguntei o que entendi e o que considero importante. Obviamente, eu estou familiarizado com tecnologias como Javascript e React. No entanto, as coisas mais importantes que acompanham a experiência são ilusórias e geralmente desaparecem ao tentar articulá-las com precisão. Eu nunca tentei colocá-los em palavras. Esta é minha primeira tentativa de sistematizar e descrever alguns deles.
Hoje, existem muitas maneiras diferentes de aprender tecnologia. Em qual biblioteca apostar em 2019? E em 2020? Devo aprender o Vue ou reagir? Ou angular? E o Redux ou o Rx? Preciso aprender Apollo? REST ou GraphQL? É fácil se perder aqui! Além disso, o autor também pode estar enganado.
Minhas maiores conquistas na cognição não estão relacionadas a nenhuma tecnologia específica. Comecei a entender mais quando resolvia um problema específico de interface do usuário (interface do usuário - aprox. Tradutor). Ao mesmo tempo, às vezes encontrava bibliotecas e padrões de outras pessoas que me ajudavam a resolver o problema. E às vezes ele escrevia suas próprias decisões (boas e terríveis).
Essa combinação - que consiste em
entender o problema , experimentar
ferramentas e aplicar várias
soluções - me proporcionou a experiência e as habilidades mais valiosas.
Este post se concentra apenas nos problemas que eu lidei no desenvolvimento de interfaces de usuário.
Se você estava desenvolvendo interfaces com o usuário, provavelmente encontrou alguns desses problemas - diretamente ou ao usar bibliotecas. Nos dois casos, recomendo que você trabalhe em um aplicativo simples sem bibliotecas, tente reproduzir e resolver esses problemas. Não existe uma solução verdadeira para nenhum deles. A experiência vem ao conhecer esses problemas e explorar possíveis soluções, dados os pontos fortes e fracos de cada um.
Consistência
Você gostou do post e a inscrição apareceu: "Você e mais três amigos gostaram." Você clicou no botão Curtir novamente e a inscrição desapareceu. Parece fácil! Mas é possível que essa inscrição esteja presente em vários lugares na tela. É possível que exista também uma indicação visual adicional (como a cor de fundo do botão), que também deve mudar. E a lista de "likers", que foi recebida anteriormente do servidor e exibida quando você passa o mouse sobre o mouse, agora deve incluir seu nome. E se você foi para outra seção ou clicou no botão Voltar, a postagem não deve "esquecer" que ela gosta. Como você pode ver, até a integridade local de um único usuário cria várias tarefas difíceis. Ao mesmo tempo, outros usuários também podem interagir com os dados exibidos em você (por exemplo, como a postagem que você está visualizando). Como mantemos os dados sincronizados em diferentes partes da tela? Como e quando devemos verificar os dados locais com o servidor e receber / enviar alterações?
Responsividade
As pessoas permitem a falta de feedback visual para suas ações apenas por um tempo muito limitado. Para ações
contínuas do usuário, como rolagem, a falta de reação do aplicativo é possível apenas pelo período mais curto. Mesmo pulando um quadro a 16 milissegundos já parece com erros e inacabado. Para ações
discretas (
únicas ), como um clique, de acordo com alguns estudos, os usuários normalmente percebem atrasos na resposta de menos de 100 milissegundos. Se a ação levar mais tempo, é necessário mostrar um indicador visual. No entanto, existem várias tarefas contra-intuitivas. Os indicadores que causam uma mudança no modelo da página ou passam por vários estágios alternados podem levar a ação a demorar mais para "sentir" do que realmente era. Da mesma forma, a resposta de um aplicativo em 20 milissegundos pulando um quadro da animação pode parecer mais lenta que uma animação completa em 30 milissegundos. Nossa consciência não funciona como referências. Como mantemos os aplicativos responsivos?
Tempo de resposta (latência)
A computação em computador e a transmissão de dados em uma rede levam tempo. Às vezes, podemos ignorar o tempo de cálculo se isso não afetar a capacidade de resposta nos dispositivos dos usuários (no entanto, verifique se você testou seu código em dispositivos antigos e de orçamento). No entanto, o processamento do tempo de transmissão de dados pela rede não pode ser evitado - pode ser calculado em segundos! Um aplicativo não pode simplesmente "travar" enquanto aguardamos o carregamento de dados ou códigos. Isso significa que qualquer ação que exija novos dados, códigos ou ativos é potencialmente assíncrona e deve manipular o estado de sua carga. Isso é verdade para a grande maioria das telas e elementos. Como lidar adequadamente com o atraso na transmissão de dados sem exibir uma cascata de spinners giratórios ou "buracos" vazios na interface? Como evitar mudanças no layout da página? E como alterar dependências assíncronas sem a necessidade de reescrever códigos constantemente?
Navegação
Esperamos que a interface seja "estável" ao interagir com ela. Os elementos não devem desaparecer repentinamente. A navegação, dentro do aplicativo (por exemplo, links) e externa (por exemplo, o botão Voltar no navegador) também deve aderir a esse princípio. Por exemplo, alternar entre as guias
/profile/likes
e
/profile/follows
following em uma seção do usuário não deve anular o conteúdo do campo de pesquisa fora desta seção. Mesmo mudar para outra tela deve parecer uma caminhada para outra sala. As pessoas esperam que, quando voltarem, encontrarão todas as coisas onde as deixaram (e, talvez, ficarão felizes com algumas coisas novas). Se você estava no meio da fita, clicou na guia perfil e voltou para a fita - então definitivamente não deseja rolar novamente a fita desde o início ou esperar até que o estado passado da fita seja carregado. Como projetar um aplicativo para lidar com a navegação arbitrária do usuário sem perder o contexto importante?
Staleness
Podemos fazer a implementação do botão Voltar instantaneamente adicionando um cache local ao aplicativo. Para isso, armazenaremos os dados necessários no cache (dados do estado anterior - aprox. Tradutor). Podemos até atualizar teoricamente o cache para manter os dados atualizados. No entanto, a implementação do armazenamento em cache envolve novos desafios. O cache pode estar desatualizado. Se eu mudei o avatar, ele deve ser atualizado inclusive no cache. Se eu publiquei uma nova postagem, ela deve aparecer imediatamente no cache, caso contrário, o cache se tornará inválido. Esse código pode eventualmente se tornar muito complexo e difícil de manter. E se o processo de publicação falhar? Quanto tempo dura o cache na memória? Quando recuperamos o conjunto de dados, mesclamos os novos dados com os dados armazenados em cache anteriormente ou nos livramos do cache antigo e colocamos em cache todo o conjunto novamente? Como a paginação e a classificação devem ser apresentadas no cache?
Entropia
A segunda lei da termodinâmica lê aproximadamente o seguinte: "Com o tempo, tudo se transforma em uma bagunça completa" (não literalmente, é claro). Isso também é válido para interfaces de usuário. Não podemos prever as ações de um usuário específico e sua sequência. A qualquer momento, nosso aplicativo pode estar em um número enorme (gigantesco!) De diferentes estados. Tentamos o nosso melhor para tornar o resultado previsível e limitado de acordo com nosso design. Não queremos olhar para a captura de tela com o bug e pensar conosco: "Como isso aconteceu?" Para
N estados possíveis, há
N × (N - 1) transições possíveis entre eles. Por exemplo, se cinco estados diferentes são possíveis para um botão (normal, ativo, pairar, realçado e desabilitado), o código responsável pela alteração do botão deve estar correto para 5 × 4 = 20 transições possíveis - ou proibir explicitamente algumas delas. Como lidamos com o aumento combinatório em possíveis estados e criamos resultados visuais previsíveis?
Prioridade
Algumas coisas são mais importantes que outras. Talvez sua interface de diálogo deva aparecer estritamente "acima" do botão com o qual foi chamada e ir além do contêiner pai. Ou, uma tarefa agendada apenas (ou seja, o resultado de um clique) pode ser mais importante do que uma tarefa de longa duração que já foi iniciada. À medida que o aplicativo aumenta o zoom, suas diferentes partes, escritas por diferentes pessoas ou até equipes, começam a competir por recursos limitados, como poder de computação do processador, tráfego de rede, espaço na tela ou tamanho do pacote. Às vezes, você pode distribuir itens em uma única escala de "importância", semelhante à regra CSS do
z-index
.
Mas geralmente não termina com nada de bom . Qualquer desenvolvedor sinceramente considera seu código importante. Mas se tudo é igualmente importante, significa - nada é importante. Como podemos fazer com que as partes independentes do aplicativo
interajam, em vez de lutar por recursos limitados?
Acessibilidade
Sites que não são adaptados para pessoas com deficiência não são um problema altamente especializado. Por exemplo, na Inglaterra, cada quinto usuário enfrenta esse problema (
aqui está um infográfico visual). Eu senti isso em mim. Apesar de ter apenas 26 anos, quase não uso sites com fontes finas e um esquema de cores opaco. Tento usar o trackpad com menos frequência, mas tenho medo do dia em que preciso usar um site inadequado para o teclado. Não devemos transformar nossos aplicativos em um pesadelo para pessoas com deficiência - e a boa notícia é que não é tão difícil. Você precisa começar explorando soluções e ferramentas. Além disso, devemos tornar simples e compreensível que designers e desenvolvedores tomem as decisões corretas. O que podemos fazer para garantir que a disponibilidade de nossos aplicativos seja ativada por padrão e não seja uma revisão tardia?
Internacionalização
Nossas aplicações devem funcionar em todo o mundo. Sim, as pessoas falam idiomas diferentes, mas, além disso, o suporte à escrita é necessário da direita para a esquerda e com o mínimo esforço dos desenvolvedores. Como podemos suportar diferentes idiomas e scripts sem perder a capacidade de resposta e o tempo de resposta do aplicativo?
Entrega
Devemos entregar o código do aplicativo no computador do usuário final. Que método e formato de transmissão usaremos? Nesta pergunta, cada resposta será um compromisso com seu próprio conjunto de pontos fortes e fracos. Por exemplo, aplicativos nativos precisam baixar todo o código com antecedência, devido ao seu tamanho enorme. Embora os aplicativos Web normalmente tenham tempos de inicialização muito mais curtos, são forçados a lidar com muita latência e downloads durante o uso. Como decidimos que tipo de atraso escolher entre essas duas opções? Como otimizamos os tempos de resposta com base nas estatísticas de uso do usuário? Quais dados precisamos ter para tomar a melhor decisão?
Flexibilidade (Resiliência)
Ninguém gosta de encontrar bugs em seus próprios programas. No entanto, alguns erros inevitavelmente chegarão à produção. E é muito importante - o que acontecerá então. Alguns erros causam um comportamento incorreto, mas estritamente definido e predefinido. Por exemplo, seu código mostra um estado inadequado para uma determinada condição. Mas e se, como resultado de um bug, o aplicativo parar completamente de renderizar? Nesse caso, não poderemos continuar a execução significativa do programa, pois a saída visual não será determinada. Um erro ao renderizar uma postagem a partir do feed não deve "interromper" a renderização de todo o feed ou colocar o aplicativo em um estado instável, o que levará a erros adicionais. Como podemos escrever código que isola os erros ao renderizar ou receber dados em uma das partes e continua a operação correta do restante do aplicativo? O que significa tolerância a falhas ao criar interfaces com o usuário?
Abstração
Em um aplicativo pequeno, podemos codificar e resolver todos os problemas acima na testa. Mas os aplicativos tendem a crescer. Queremos poder
reutilizar, dividir e combinar diferentes partes do aplicativo e fazê-lo junto com outras pessoas. Queremos definir limites compreensíveis entre partes de um único todo que serão aceitos por pessoas diferentes e, ao mesmo tempo, evitar uma lógica muito rígida, pois muitas vezes muda e evolui no processo de trabalho. Como criamos abstrações que ocultam os detalhes da implementação da interface do usuário? Como podemos evitar a recorrência desses problemas com o crescimento do aplicativo?
Claro, existem muitos outros problemas que eu não mencionei. Esta lista não é de forma alguma completa ou exaustiva. Por exemplo, não toquei no tópico de colaboração entre design e desenvolvimento, o tópico de depuração ou teste. Talvez voltemos a isso outra vez.
É tentador ler sobre esses problemas, tendo em mente como solução uma estrutura específica para exibição de dados ou uma biblioteca para recebimento de dados. Mas recomendo que você imagine que essas soluções não existem e tente ler novamente. Como você tentaria resolver esses problemas? Tente realizar suas idéias em um aplicativo simples. Ficarei feliz em ver os resultados de suas experiências no Github. (Basta marcar Dan Abramov no
Twitter e anexar links aos repositórios - aprox. Tradutor).
O que é especialmente interessante nesses problemas é que a maioria deles se manifesta em qualquer escala. Você pode encontrá-los ao trabalhar em um pequeno widget, como uma dica de ferramenta, e em aplicativos enormes, como o Twitter ou o Facebook.
Pense nos elementos da interface do usuário não triviais do aplicativo que você gosta de usar e, em seguida, execute a lista dos problemas acima. Você pode descrever as trocas que os desenvolvedores fizeram? Tente reproduzir um comportamento semelhante do zero!
Eu percebi muito sobre o desenvolvimento de boas interfaces de usuário, experimentando esses problemas em aplicativos pequenos sem usar bibliotecas e estruturas de terceiros. Eu recomendo isso para quem deseja obter um entendimento profundo de soluções e trade-offs ao desenvolver interfaces complexas.