Bonjour, Habr! Je présente à votre attention une traduction du
post de Dan Abramov
«Les éléments de l'ingénierie de l'interface utilisateur» sur les problèmes et tâches contemporains qui doivent être résolus dans une bonne interface. L'auteur examine les problèmes fondamentaux dans le développement d'interfaces, dont l'interprétation et la solution seules - sans utiliser de bibliothèques et de cadres prêts à l'emploi - peuvent donner une compréhension approfondie des solutions dans le domaine du développement frontal existant sur le marché.

Note du traducteurLe texte est écrit et traduit à la première personne. L'auteur de l'original en anglais est
Dan Abramov , le développeur de la bibliothèque React pour la construction d'interfaces utilisateur complexes.
Dans ma
dernière publication, j’ai écrit sur l’importance de pouvoir reconnaître les lacunes dans ses propres connaissances. On aurait pu croire que je vous proposais des excuses pour être médiocre. Pas du tout! Mais en fait, nos connaissances sont un vaste sujet de conversation.
Je suis convaincu que vous pouvez commencer vos connaissances «dès le départ» et qu'il n'est pas nécessaire d'étudier la technologie (pile technologique pour le développement Web - environ Translator) dans un certain ordre. Mais je crois aussi que l'accumulation d'expérience et de compétences professionnelles dans le domaine choisi est d'une grande importance. Personnellement, j'ai toujours été le plus intéressé par la création d'interfaces utilisateur.
Et je me suis demandé ce que je comprends et ce que je trouve important? Bien sûr, je connais bien des technologies telles que Javascript et React. Cependant, les choses les plus importantes qui viennent avec l'expérience sont insaisissables et se dérobent généralement lorsque vous essayez de les articuler avec précision. Je n'ai jamais essayé de les mettre en mots. Il s'agit de ma première tentative de systématiser et de décrire certains d'entre eux.
Aujourd'hui, il existe de nombreuses façons d'apprendre la technologie. Sur quelle bibliothèque parier en 2019? Et en 2020? Dois-je apprendre Vue ou React? Ou angulaire? Et Redux ou Rx? Dois-je apprendre Apollo? REST ou GraphQL? C'est facile de se perdre ici! En outre, l'auteur peut également se tromper.
Mes plus grandes réalisations en cognition ne sont liées à aucune technologie particulière. J'ai commencé à mieux comprendre lorsque je résolvais un problème spécifique d'interface utilisateur (interface utilisateur - environ traducteur). En même temps, j'ai parfois trouvé des bibliothèques et des modèles d'autres personnes qui m'ont aidé à résoudre le problème. Et parfois, il écrivait ses propres décisions (bonnes et terribles).
Cette combinaison - consistant à
comprendre le problème , à expérimenter avec des
outils et à appliquer diverses
solutions - m'a donné l'expérience et les compétences les plus précieuses.
Ce post se concentre uniquement sur les problèmes que j'ai traités dans le développement des interfaces utilisateur.
Si vous développiez des interfaces utilisateur, vous avez probablement rencontré certains de ces problèmes - directement ou lors de l'utilisation de bibliothèques. Dans les deux cas, je vous recommande de travailler sur une application simple sans bibliothèques, d'essayer de reproduire et de résoudre ces problèmes. Il n'y a pas de vraie solution pour aucun d'entre eux. L'expérience vient de la connaissance de ces problèmes et de l'exploration de solutions possibles, compte tenu des forces et des faiblesses de chacun.
Cohérence
Vous avez aimé le message et l'inscription est apparue: "Vous et 3 autres de vos amis avez apprécié." Vous avez de nouveau cliqué sur le bouton J'aime et l'inscription a disparu. Cela semble facile! Mais il est possible qu'une telle inscription soit présente à plusieurs endroits sur l'écran. Il est possible qu'il existe également une indication visuelle supplémentaire pour des éléments similaires (par exemple, la couleur d'arrière-plan du bouton), qui devrait également changer. Et la liste des «likers», qui avait été précédemment reçue du serveur et affichée lorsque vous survolez la souris, devrait maintenant inclure votre nom. Et si vous êtes allé dans une autre section ou avez cliqué sur le bouton Retour, le message ne doit pas "oublier" qu'il a votre goût. Comme vous pouvez le voir, même l'intégrité locale pour un seul utilisateur crée un certain nombre de tâches difficiles. Dans le même temps, d'autres utilisateurs peuvent également interagir avec les données qui vous sont affichées (par exemple, comme le message que vous consultez). Comment gardons-nous les données synchronisées dans différentes parties de l'écran? Comment et quand devons-nous vérifier les données locales avec le serveur et recevoir / envoyer les modifications?
Réactivité
Les gens ne permettent le manque de rétroaction visuelle pour leurs actions que pour un temps très limité. Pour les actions utilisateur
continues , comme le défilement, l'absence de réaction de l'application n'est possible que pour la période la plus courte. Même sauter une image à 16 millisecondes semble déjà bogué et inachevé. Pour certaines actions
discrètes (ponctuelles), comme un clic, selon certaines études, les utilisateurs perçoivent normalement des retards dans la réponse de moins de 100 millisecondes. Si l'action prend plus de temps, il est nécessaire d'afficher un indicateur visuel. Cependant, il existe plusieurs tâches contre-intuitives. Les indicateurs qui provoquent un changement dans le modèle de page ou qui passent par plusieurs étapes alternées peuvent faire durer l'action plus longtemps qu'elle ne l'est réellement. De même, la réponse d’une application dans les 20 millisecondes en sautant une image de l’animation peut «sembler» plus lente qu’une animation complète dans les 30 millisecondes. Notre conscience ne fonctionne pas comme des repères. Comment garder les applications réactives?
Temps de réponse (latence)
L'informatique et la transmission de données sur un réseau prennent du temps. Parfois, nous pouvons ignorer le temps de calcul s'il n'affecte pas la réactivité sur les appareils des utilisateurs (cependant, assurez-vous que vous avez testé votre code sur des appareils anciens et économiques). Cependant, le traitement du temps de transmission des données sur le réseau ne peut être évité - il peut être calculé en quelques secondes! Une application ne peut pas simplement «se bloquer» pendant que nous attendons le chargement des données ou du code. Cela signifie que toute action qui nécessite de nouvelles données, du code ou des actifs est potentiellement asynchrone et doit gérer l'état de sa charge. Cela est vrai pour la grande majorité des écrans et des éléments. Comment gérer correctement le retard dans la transmission des données sans afficher une cascade de filateurs en rotation ou des "trous" vides dans l'interface? Comment éviter les changements de mise en page? Et comment changer les dépendances asynchrones sans avoir besoin d'une réécriture constante du code?
La navigation
Nous nous attendons à ce que l'interface soit «stable» lors de l'interaction avec elle. Les éléments ne doivent pas disparaître subitement. La navigation, à la fois à l'intérieur de l'application (par exemple, les liens) et externe (par exemple, le bouton Retour dans le navigateur) doit également respecter ce principe. Par exemple, le basculement entre les onglets
/profile/likes
et
/profile/follows
following dans une section utilisateur ne doit pas annuler le contenu du champ de recherche en dehors de cette section. Même passer à un autre écran devrait ressembler à une promenade dans une autre pièce. Les gens s'attendent à ce qu'à leur retour, ils trouvent toutes les choses où ils les ont laissés (et, peut-être, ils seront satisfaits de nouvelles choses). Si vous étiez au milieu de votre bande, cliquez sur l'onglet de profil, puis revenez à la bande - alors vous ne voulez certainement pas faire défiler à nouveau la bande depuis le début ou attendre que l'état passé de la bande soit chargé. Comment concevoir une application pour gérer une navigation utilisateur arbitraire sans perdre de contexte important?
Staleness
Nous pouvons rendre la mise en œuvre du bouton Retour instantanée en ajoutant un cache local à l'application. Pour ce faire, nous stockons les données nécessaires dans le cache (données de l'état précédent - environ Translator). Nous pouvons même théoriquement mettre à jour le cache pour maintenir les données à jour. Cependant, la mise en œuvre de la mise en cache entraîne de nouveaux défis. Le cache est peut-être obsolète. Si j'ai changé l'avatar, il devrait être mis à jour, y compris dans le cache. Si j'ai publié un nouveau message, il devrait apparaître immédiatement dans le cache, sinon le cache deviendra invalide. Un tel code peut éventuellement devenir trop complexe et difficile à maintenir. Et si le processus de publication échoue? Combien de temps le cache est-il en mémoire? Lorsque nous récupérons l'ensemble de données, fusionnons-nous les nouvelles données avec les données mises en cache plus tôt, ou supprimons-nous l'ancien cache et mettons-nous en cache l'ensemble à nouveau? Comment la pagination et le tri doivent-ils être présentés dans le cache?
Entropie
La deuxième loi de la thermodynamique se lit approximativement comme suit: «Au fil du temps, tout se transforme en un gâchis complet» (pas textuellement, bien sûr). Cela est également vrai pour les interfaces utilisateur. Nous ne pouvons pas prédire les actions d'un utilisateur particulier et leur séquence. À tout moment, notre application peut être dans un nombre énorme (gigantesque!) D'états différents. Nous faisons de notre mieux pour rendre le résultat prévisible et limité conformément à notre conception. Nous ne voulons pas regarder la capture d'écran avec le bug et penser à nous-mêmes: "Comment est-ce arrivé?" Pour
N états possibles, il y a
N × (N - 1) transitions possibles entre eux. Par exemple, si cinq états différents sont possibles pour un bouton (normal, actif, survolé, mis en surbrillance et désactivé), le code responsable de la modification du bouton doit être correct pour 5 × 4 = 20 transitions possibles - ou interdire explicitement certains d'entre eux. Comment gérer l'augmentation combinatoire des états possibles et créer une sortie visuelle prévisible?
Priorité
Certaines choses sont plus importantes que d'autres. Peut-être que votre interface de dialogue devrait apparaître strictement «au-dessus» du bouton avec lequel elle a été appelée, et aller au-delà du conteneur parent. Ou, une tâche juste planifiée (c'est-à-dire le résultat d'un clic) peut être plus importante qu'une tâche de longue durée qui a déjà commencé. Au fur et à mesure que l'application se rapproche, ses différentes parties, écrites par différentes personnes ou même des équipes, commencent à rivaliser pour des ressources limitées, telles que la puissance de calcul du processeur, le trafic réseau, l'espace d'écran ou la taille de l'ensemble. Parfois, vous pouvez distribuer des éléments sur une seule échelle «d'importance», similaire à la règle CSS
z-index
.
Mais généralement, cela ne se termine pas par quelque chose de bon . Tout développeur considère sincèrement son code comme important. Mais si tout est également important, cela signifie - rien n'est important. Comment pouvons-nous faire
interagir les parties indépendantes de l'application plutôt que de lutter pour des ressources limitées?
Accessibilité
Les sites non adaptés aux personnes handicapées ne sont pas un problème hautement spécialisé. Par exemple, en Angleterre, un utilisateur sur cinq est confronté à ce problème (
voici une infographie visuelle). Je l'ai senti sur moi. Malgré le fait que je n'ai que 26 ans, j'utilise rarement des sites avec des polices fines et un jeu de couleurs opaques. J'essaie d'utiliser le trackpad moins souvent, mais j'ai peur du jour où je dois utiliser un site inapproprié pour le clavier. Nous ne devons pas transformer nos demandes en cauchemar pour les personnes handicapées - et la bonne nouvelle est que ce n’est pas si difficile. Vous devez commencer par explorer des solutions et des outils. De plus, nous devons faire en sorte que les concepteurs et les développeurs puissent prendre les bonnes décisions de manière simple et compréhensible. Que pouvons-nous faire pour garantir que la disponibilité de nos applications est activée par défaut et ne pas être une révision tardive?
L'internationalisation
Nos applications devraient fonctionner dans le monde entier. Oui, les gens parlent différentes langues, mais en plus de cela, le support pour l'écriture est nécessaire de droite à gauche, et avec un minimum d'effort de la part des développeurs. Comment prendre en charge différents langages et scripts sans perdre la réactivité et le temps de réponse des applications?
La livraison
Nous devons fournir le code d'application à l'ordinateur de l'utilisateur final. Quelle méthode et quel format de transmission utiliserons-nous? Dans cette question, chaque réponse sera un compromis avec ses propres forces et faiblesses. Par exemple, les applications natives doivent télécharger tout leur code à l'avance en raison de leur taille énorme. Alors que les applications Web ont généralement des temps de démarrage beaucoup plus courts, mais sont obligées de gérer beaucoup de latence et de téléchargements pendant l'utilisation. Comment décider du type de retard à choisir parmi ces deux options? Comment optimiser les temps de réponse en fonction des statistiques d'utilisation des utilisateurs? De quelles données avons-nous besoin pour prendre la meilleure décision?
Flexibilité (résilience)
Personne n'aime rencontrer de bugs dans ses propres programmes. Cependant, certains bugs arriveront inévitablement en production. Et c'est très important - que se passera-t-il alors. Certains bogues provoquent un comportement incorrect, mais strictement défini et prédéfini. Par exemple, votre code affiche un état inapproprié pour une condition donnée. Mais que se passe-t-il si, à la suite d'un bogue, l'application arrêtait complètement le rendu? Dans ce cas, nous ne pourrons pas continuer l'exécution significative du programme, car la sortie visuelle ne sera pas déterminée. Une erreur lors du rendu d'un article à partir du flux ne doit pas «interrompre» le rendu de l'ensemble du flux ni mettre l'application dans un état instable, ce qui entraînera d'autres erreurs. Comment pouvons-nous écrire du code qui isole les erreurs lors du rendu ou de la réception de données dans l'une des parties et continue le bon fonctionnement du reste de l'application? Que signifie la tolérance aux pannes lors de la création d'interfaces utilisateur?
Abstraction
Dans une petite application, nous pouvons coder en dur et résoudre tous les problèmes ci-dessus sur le front. Mais les applications ont tendance à se développer. Nous voulons pouvoir
réutiliser, bifurquer et combiner différentes parties de l'application et le faire avec d'autres personnes. Nous voulons définir des frontières compréhensibles entre les parties d'un même ensemble qui seront acceptées par différentes personnes et en même temps éviter une logique trop rigide, car elle change et évolue souvent dans le processus de travail. Comment créer des abstractions qui masquent les détails de la mise en œuvre de l'interface utilisateur? Comment éviter la récurrence de ces problèmes avec la croissance de l'application?
Bien sûr, il y a beaucoup plus de problèmes que je n'ai pas mentionnés. Cette liste n'est en aucun cas complète ou exhaustive. Par exemple, je n'ai pas abordé le sujet de la collaboration entre la conception et le développement, le sujet du débogage ou des tests. Peut-être y reviendrons-nous une autre fois.
Il est tentant de lire sur ces problèmes, en gardant à l'esprit comme solution un cadre spécifique pour l'affichage des données ou une bibliothèque pour la réception des données. Mais je vous recommande d'imaginer que ces solutions n'existent pas et d'essayer de relire. Comment tenteriez-vous de résoudre ces problèmes? Essayez de réaliser vos idées sur une application simple. Je serai heureux de voir les résultats de vos expériences sur Github. (Il suffit de taguer Dan Abramov sur
Twitter et de joindre des liens vers des référentiels - environ Traducteur).
Ce qui est particulièrement intéressant dans ces problèmes, c'est que la plupart d'entre eux se manifestent à n'importe quelle échelle. Vous pouvez les rencontrer lorsque vous travaillez sur un petit widget, comme une info-bulle, et dans d'énormes applications telles que Twitter ou Facebook.
Pensez aux éléments d'interface utilisateur non triviaux de l'application que vous souhaitez utiliser, puis parcourez à nouveau la liste des problèmes ci-dessus. Pouvez-vous décrire les compromis que les développeurs ont faits? Essayez de reproduire un comportement similaire à partir de zéro!
J'ai réalisé beaucoup de choses sur le développement de bonnes interfaces utilisateur, en expérimentant ces problèmes dans de petites applications sans utiliser de bibliothèques et de frameworks tiers. Je le recommande à tous ceux qui souhaitent acquérir une compréhension approfondie des solutions et des compromis lors du développement d'interfaces complexes.