Expérience de développement de test React pour Aviasales

Salut, je voulais partager mon expérience dans le développement d'un cas de test pour Aviasales.


Je suis récemment tombé sur un emploi de développeur React chez Aviasales. J'ai envoyé une demande, après quoi le lendemain, les RH m'ont répondu et m'ont informé que je devrais faire une tâche de test. Je n'aime vraiment pas faire de tâches de test, car je dois consacrer beaucoup de temps à leur mise en œuvre, et en cas d'échec, cela sera perdu. Mais j'ai accepté ...


Vous pouvez trouver la tâche de test ici sur le lien .


Voici un lien vers mon référentiel de la tâche terminée.


Je me suis limité à terminer la mission en une journée (bien que j'aie apporté des améliorations mineures après la publication: j'ai terminé le croquis, pour ainsi dire).


Ce que j'ai choisi pour le développement:


  1. J'ai choisi NextJS comme base, car je ne voulais pas bricoler la configuration de l'environnement pour Webpack, et vous pouvez déployer le projet lui-même en quelques clics.
  2. Je voulais Ă©crire rapidement et j'ai choisi le package React-IOC en conjonction avec MobX, au lieu de Redux. Il s'agit d'un package qui vous permet d'Ă©crire des applications via des services qui ressemblent Ă  des services angulaires.
  3. J'ai utilisé Web Worker pour qu'il n'y ait pas de décalage dans l'interface lors du tri d'une grande quantité de données.
  4. Je n'ai pas utilisé Typescript dans le but de ne pas écrire de code supplémentaire avec une perte de temps inutile sur une tâche de test.
  5. Sur la base du paragraphe 4, je n'ai pas non plus Ă©crit de tests.
  6. J'ai ajouté deux packages supplémentaires au projet: debounce, RxJS. Le premier est nécessaire pour créer des rappels simples, par exemple, en changeant l'état de téléchargement afin que le spinner n'affiche pas la charge si le téléchargement prend très peu de temps. J'utilise toujours le deuxième package pour créer un script d'action, par exemple, pour traiter les états en cas d'erreur lors de l'envoi d'une requête au serveur.

La procédure pour la première étape de développement:


  1. Référentiel initialisé.
  2. Projet NextJS initialisé.
  3. Ajout d'une page de base d'index avec un message Hello World.
  4. Création d'un service ticket.provider qui interagit avec le serveur api.
  5. Création du service ticket.service, qui injecte ticket.provider et remplit l'observateur avec un tableau de tickets affichés
  6. Création de ticket.filter.service, qui stocke les données filtrées injectées à partir de ticket.service via @computed

La deuxième étape de développement:


  1. A créé des composants et peint des styles pour eux en utilisant la disposition fournie dans le référentiel de tâches.
  2. J'ai fait un spinner du téléchargement et mis sa valeur à partir des services.
  3. Connecté toute la logique de service aux composants.
  4. Ajout d'utilitaires avec formatage des données, comme le temps et l'argent.

J'ai alors décidé d'essayer l'interface «tactile» et j'ai trouvé des failles lors de l'utilisation de l'application:


  1. Il y a des ralentissements d'interface lors du changement de filtre et de tri, j'ai donc transféré le stockage, la récupération et le filtrage des données vers Web Worker, après quoi les décalages ont complètement disparu.
  2. Spinner n'a pas corrigé l'animation de la ligne de saut, je l'ai donc remplacée par un affichage visuel des lignes avec une animation scintillante.
  3. Pour faciliter le rendu des données, j'ai transféré les appels de mise en forme des données vers Web Worker, cela réduit la charge sur le rendu des composants

Ensuite, j'ai terminé mon travail et à la fin de la journée, j'ai envoyé une tâche pour vérification.


Comme je me suis limité dans le temps, je n'ai pas optimisé davantage l'application, à savoir, je n'ai pas utilisé React.memo (...). Je n'ai pas non plus commencé à remplacer la fenêtre et le routeur par des injecteurs dans les services. Pardonnez-moi pour cela, c'est un défaut.


Mais le lendemain, j'ai ajouté la fonctionnalité de se souvenir de l'état du filtre dans la ligne d'URL du navigateur, après quoi j'étais déjà satisfait du travail effectué.


J'ai attendu une réponse de leur part pendant 7 jours, ils n'ont répondu à aucun de mes messages. Ce fut une expérience désagréable, pour ainsi dire. Mais ils ont quand même répondu, et la réponse était extrêmement pénible à lire. Le message peut être vu ci-dessous.


Jetons un coup d'Ĺ“il aux points:


[1]. "Le travail a été fait très soigneusement"


Je ne vois pas oĂą, pas un argument, car il n'y a pas d'exemples.


[2]. «Il semble que le développement ait poursuivi les objectifs de« ça marche »et ignoré« comment ça marche ».


Pas un argument, pas d'exemples.


[3]. "Sur la base de nos exigences pour les niveaux de candidats, la tâche terminée atteint à peine au milieu."


Quelles sont les exigences? O are sont-ils?


[4]. «Nous prévoyons que les filtres ne seront pas codés en dur et s'adapteront aux données. Si vous accédez aux aviasales, vous pouvez voir que les tickets sont affichés immédiatement dès le premier versement, et nous n'attendons pas que tout le monde se charge. "


Cette exigence ne faisait pas partie de la tâche ci-jointe. Et l'application Aviasales elle-même, à mon avis, n'a pas d'interface de référence, et sauter des tickets n'est pas la meilleure solution.


[5]. «Pourquoi filter.service connaît-il le routeur et la fenêtre? Il ne devrait pas contrôler l'état de l'application et avoir de telles dépendances. »


Parce que les services sont créés pour contrôler l'état de l'ensemble de l'application, ou le module où ils existent. Ici, l'auteur suggère clairement d'écrire toute la logique des composants.


[6]. «Travailleurs Web. Quel est le profit? Dans ce cas, beaucoup de temps est consacré aux opérations asynchrones et la charge enregistrée dans le thread principal est consacrée aux objets sérialiser / désérialiser (et aux objets observables). Si nous parlons d'optimisations, cela valait la peine de commencer non pas par la suppression dans le Web Worker, mais par la résolution des problèmes dans le thread principal. »


Comment gérer le traitement de grandes quantités de données dans le thread principal et en même temps sans décalage d'interface? Il sera extrêmement intéressant pour moi de découvrir si quelqu'un a un exemple, puis d'écrire dans les commentaires. Apparemment, il me manque quelque chose.


[7]. "Cela n'a aucun sens d'ajouter des rxjs au projet juste pour implémenter une nouvelle tentative et un chargement séquentiel."


Pourquoi cela n'a-t-il pas de sens? Vous pouvez importer des fonctions strictement nécessaires dans RxJS; si les packages et les fonctions d'agitation d'arbre ne fonctionnent pas correctement, cela n'augmentera pas la taille de l'application.


[8]. «Le projet ne peut pas être mis à l'échelle et maintenu. Les constructions comme (ticket.segments || [{}, {}]).map((ticket) => ...) très lourdes. "


Passons en revue les critères de mise à l'échelle et de support: accessibilité (les services résolvent ce problème), risques ( ticket.segments || [{}, {}] - ce n'est qu'un exemple de la façon de traiter les cas si l'entrée ne contient pas de données. Un exemple est mauvais, mais l'approche de la structure nullable au moins, mais j'essaye de me conformer), du code propre (enfin, au moins je sais ce que c'est :), même si j'ai essayé de tout écrire comme il se doit). Il semble que tout soit capturé, encore une fois pas un argument.


[9]. «J'ai eu l'impression qu'il n'y a absolument aucune compréhension du fonctionnement de React sous le capot. Il existe de nombreuses erreurs critiques dans l'utilisation des accessoires pour les composants qui ont un très mauvais effet sur les performances. Les mécanismes d'optimisation de React sont ignorés. »


Je ne peux pas comprendre où sont les problèmes décrits. Quel genre de mécanismes?


[10]. "Création de fonctions de gestionnaire dans le rendu."


Je n'ai aucune fonction de gestionnaire dans le rendu, est-ce que quelqu'un comprend ce qui est écrit ici? J'ai même transféré le traitement du formatage vers Web Worker


[11]. «Créer un nouvel objet vide et le transférer sur le ticket. ticket-list-loading.jsx:10 ou Ticket.tsx:24 "


Il n'y a rien de critique ici, à part ma paresse de retirer séparément le composant ticket de chargement. Passer un objet vide ne viole aucun principe de programmation, sauf qu'ici il fallait le faire via ticket = ticket || {}; ticket = ticket || {}; mais cela est purement dû au fait que le temps de développement était limité à un jour et qu'il faudrait plus de temps pour corriger tous les défauts mineurs.


[12]. "Array index comme clés sur la liste des tickets"


Il y a plutôt une question à savoir pourquoi api ne renvoie pas d'éléments avec id. Par conséquent, lors de la réception de données du serveur, je devrais générer des clés uniques pour chaque élément, ce que je n'ai pas fait dans le cadre de la tâche de test, car même dans un projet réel, c'est une approche douteuse.


Et enfin, la conclusion: «En général, React n'a pas fonctionné (
Total: pour le niveau intermédiaire, nous nous attendons à ce qu'il n'y ait pas de problèmes critiques avec la réaction, mais il y en a beaucoup dans le travail effectué. »


Il n'y a déjà aucun commentaire ...


Merci si vous lisez jusqu'au bout. J'ai une opinion très négative sur Aviasales, donc je poste tout ici pour que vous puissiez vous évaluer si vous devez les contacter ou non.


Texte intégral du message:


Le travail est fait très soigneusement. Il semble que pendant le développement, les objectifs étaient «ça marche», et «comment ça marche» a été ignoré. Sur la base de nos exigences pour les niveaux de candidats, la tâche terminée atteint à peine le milieu.


Performance et structure:
Nous prévoyons que les filtres ne seront pas codés en dur et s'adapteront aux données. Si vous accédez aux aviasales, vous pouvez voir que les tickets sont affichés immédiatement dès que le premier versement apparaît et nous n'attendons pas que tout le monde se charge.


De plus, des points plus techniques.


  1. Pourquoi filter.service connaît-il le routeur et la fenêtre? Il ne doit pas contrôler l'état de l'application et avoir de telles dépendances.
  2. Travailleurs Web. Quel est le profit? Dans ce cas, beaucoup de temps est consacré aux opérations asynchrones et la charge enregistrée dans le thread principal est consacrée aux objets sérialiser / désérialiser (et aux objets observables). Si nous parlons d'optimisations, cela valait la peine de commencer non pas par la suppression dans le Web Worker, mais par la résolution des problèmes dans le thread principal.
  3. Cela n'a aucun sens d'ajouter des rxjs au projet uniquement pour implémenter une nouvelle tentative et un chargement séquentiel.
  4. Le projet ne peut pas être mis à l'échelle et maintenu. Les constructions comme ( ticket.segments || [{}, {}]).map((ticket) => ticket ) sont très lourdes.
    RĂ©agissez:
    J'ai eu l'impression qu'il n'y a absolument aucune compréhension du fonctionnement de React sous le capot. Il existe de nombreuses erreurs critiques dans l'utilisation des accessoires pour les composants qui ont un très mauvais effet sur les performances. Les mécanismes d'optimisation de React sont ignorés. En bref sur les problèmes:
  5. Création de fonctions de gestionnaire dans le rendu.
  6. Créez un nouvel objet vide et transférez-le sur le ticket. ticket-list-loading.jsx:10 ou Ticket.tsx:24 .
  7. Les index de tableau sous forme de clés sur une liste de tickets.
    En général, React n'a pas fonctionné :(

Total: pour le niveau intermédiaire, nous prévoyons qu'avec réagir, il n'y aura pas de problèmes critiques, mais il y en a beaucoup dans le travail effectué.

Source: https://habr.com/ru/post/fr468715/


All Articles