Magazine Internet

10 questions à poser lors d’un entretien que chaque développeur JavaScript doit savoir

Publié le 28 juillet 2018 par Rhw @RevueHW

Les clés de la maîtrise du JavaScript

Dans la plupart des entreprises, la direction doit faire confiance aux développeurs pour donner des entretiens techniques afin d’évaluer les compétences des candidats. Si vous réussissez bien, vous devrez éventuellement passer une entrevue. Voici comment ça se passe.

Ça commence avec les gens

« Rien ne prédit mieux les résultats d’une entreprise qu’une équipe exceptionnelle. Si vous voulez vaincre la difficulté, vous devez d’abord investir sur ce. »

Comme le dit Marcus Lemonis, concentrez-vous sur les 3 P : « Personnes, Processus, Produit »

La meilleure façon d’évaluer un candidat est un exercice de programmation par paire….

Jumeler le programme avec le candidat. Laissez le candidat conduire. Regardez et écoutez plus que vous ne parlez. Un bon projet pourrait être de tirer des tweets de l’API Twitter et de les afficher sur un timeline.

Cela dit, aucun exercice ne vous dira tout ce que vous devez savoir. Une entrevue peut aussi être un outil très utile, mais ne perdez pas de temps à poser des questions sur la syntaxe ou les bizarreries linguistiques. Renseignez-vous sur l’architecture et les paradigmes – les grandes décisions qui peuvent avoir un impact majeur sur l’ensemble du projet.

La syntaxe et les fonctionnalités sont faciles à Google. Il est beaucoup plus difficile pour Google d’acquérir la sagesse de l’ingénierie logicielle ou les paradigmes et idiomes communs que les développeurs JavaScript acquièrent avec l’expérience.

JavaScript est spécial et joue un rôle essentiel dans presque toutes les applications volumineuses. Pourquoi JavaScript est significativement différent des autres langages ?

Voici quelques questions qui vous aideront à explorer ce qui compte vraiment :

  1. Pouvez-vous nommer deux paradigmes de programmation importants pour les développeurs d’applications JavaScript ?

10 questions à poser lors d’un entretien que chaque développeur JavaScript doit savoir

JavaScript est un langage multi-paradigme, supportant la programmation impérative/procédurale avec la programmation orientée objet (POO) et la programmation fonctionnelle. JavaScript prend en charge la POO avec héritage prototypique.

Bonne réponse :

  • Héritage prototypique (aussi : prototypes, OLOO).
  • Programmation fonctionnelle (aussi : fermetures, fonctions de première classe, lambdas).

Mauvaises réponses :

Aucune idée de ce qu’est un paradigme, aucune mention de programmation oo ou fonctionnelle prototypique.
  1. Qu’est-ce que la programmation fonctionnelle ?

10 questions à poser lors d’un entretien que chaque développeur JavaScript doit savoir

La programmation fonctionnelle produit des programmes en composant des fonctions mathématiques et évite l’état partagé et les données mutables. Lisp (spécifié en 1958) a été parmi les premiers langages à soutenir la programmation fonctionnelle, et a été fortement inspiré par le lambda-calcul. Lisp et de nombreux langages de la famille Lisp sont encore couramment utilisés aujourd’hui.

La programmation fonctionnelle est un concept essentiel en JavaScript (l’un des deux piliers de JavaScript). Plusieurs utilitaires fonctionnels communs ont été ajoutés à JavaScript dans ES5.

Bonnes réponses :

  • Fonctions pures/pureté de fonction.
  • Évitez les effets secondaires.
  • Composition de fonction simple.
  • Exemples de langages fonctionnels : Lisp, ML, Haskell, Erlang, Clojure, Orme, F Sharp, OCaml, etc …
  • Mention des fonctionnalités supportant FP : fonctions de première classe, fonctions d’ordre supérieur, fonctions comme arguments/valeurs.

Mauvaises réponses :

  • Aucune mention de fonctions pures/éviter les effets secondaires.
  • Incapable de fournir des exemples de langages de programmation fonctionnels.
  • Incapable d’identifier les fonctionnalités de JavaScript qui activent FP.

  1. Quelle est la différence entre l’héritage classique et l’héritage prototypique ?

10 questions à poser lors d’un entretien que chaque développeur JavaScript doit savoir

Héritage de classe (Class Inheritance) : les instances héritent des classes (comme un blueprint – une description de la classe), et créent des relations de sous-classe : taxonomies de classe hiérarchiques. Les instances sont généralement instanciées via des fonctions constructeurs avec le mot clé `new`. L’héritage de classe peut utiliser ou non le mot-clé `class` de ES6.

Héritage Prototypique : les instances héritent directement d’autres objets. Les instances sont généralement instanciées via des fonctions d’usine ou `Object.create()`. Les instances peuvent être composées de nombreux objets différents, permettant un héritage sélectif facile.

En JavaScript, l’héritage prototypique est plus simple et plus flexible que l’héritage de classe.

Bonnes réponses :

  • Classes : créez un couplage serré ou des hiérarchies/taxonomies.
  • Prototypes : mentions d’héritage concaténé, délégation de prototype, héritage fonctionnel, composition d’objet.

Mauvaises réponses :

Aucune préférence pour l’héritage prototypique et la composition sur l’héritage de classe.
  1. Quels sont les avantages et les inconvénients de la programmation fonctionnelle par rapport à la programmation orientée objet ?

10 questions à poser lors d’un entretien que chaque développeur JavaScript doit savoir

Avantages POO : Il est facile de comprendre le concept de base des objets et facile à interpréter la signification des appels de méthode. La POO tend à utiliser un style impératif plutôt qu’un style déclaratif, qui se lit comme un ensemble simple d’instructions que l’ordinateur doit suivre.

Inconvénients POO : Généralement, POO dépend de l’état partagé. Les objets et les comportements sont généralement liés ensemble sur la même entité, qui peut être accédée au hasard par un nombre quelconque de fonctions avec un ordre non déterministe, ce qui peut conduire à un comportement indésirable comme les conditions de course.

Avantages PF : En utilisant le paradigme fonctionnel, les programmeurs évitent tout état partagé ou effets secondaires, ce qui élimine les bogues causés par la concurrence de plusieurs fonctions pour les mêmes ressources. Avec des fonctionnalités telles que la disponibilité du style sans point (aussi connu comme programmation tacite), les fonctions ont tendance à être radicalement simplifiées et facilement recomposées pour le code plus généralement réutilisable par rapport à la POO.

La PF a aussi tendance à privilégier les styles déclaratifs et dénotationnels, qui n’énoncent pas des instructions pas à pas pour les opérations, mais se concentrent plutôt sur ce qu’il faut faire, laissant les fonctions sous-jacentes s’occuper de cela. Ce qui laisse une grande latitude pour le refactoring et l’optimisation des performances, vous permettant même de remplacer des algorithmes entiers par des algorithmes plus efficaces avec très peu de changement de code. (par exemple, mémoriser ou utiliser une évaluation paresseuse au lieu d’une évaluation avide).

Le calcul qui utilise des fonctions pures est également facile à mettre à l’échelle sur plusieurs processeurs, ou à travers des clusters distribués sans crainte de conflits de ressources, de conditions de course, etc …

Inconvénients PF : La surexploitation des fonctionnalités de PF telles que le style sans point et les grandes compositions peut potentiellement réduire la lisibilité, car le code résultant est souvent plus abstrait, plus laconique et moins concret.

Plus de gens sont familiers avec la programmation OO et impérative que la programmation fonctionnelle, donc même les idiomes communs dans la programmation fonctionnelle peuvent être source de confusion pour les nouveaux membres de l’équipe.

PF a une courbe d’apprentissage beaucoup plus abrupte que la POO parce que la grande popularité de la POO a permis au langage et au matériel d’apprentissage de la POO de devenir plus conversationnels, alors que le langage de la PF tend à être beaucoup plus académique et formel. Les concepts de PF sont souvent écrits sur l’utilisation d’idiomes et de notations issus du lambda-calcul, des algèbres et de la théorie des catégories, ce qui nécessite une connaissance préalable de ces domaines.

Bonnes réponses :

  • Mentions de problèmes avec l’état partagé, différentes choses en compétition pour les mêmes ressources, etc …
  • Connaissance de la capacité de PF à simplifier radicalement de nombreuses applications.
  • Connaissance des différences dans les courbes d’apprentissage.
  • Articulation des effets secondaires et comment ils affectent la maintenabilité du programme.
  • Conscient qu’une base de code hautement fonctionnelle peut avoir une courbe d’apprentissage abrupte.
  • Conscient qu’une base de code hautement OOP peut être extrêmement résistante au changement et très fragile par rapport à une base de code PF équivalente.
  • Conscient que l’immutabilité donne lieu à un historique d’état du programme extrêmement accessible et malléable, permettant d’ajouter facilement des fonctionnalités telles que l’annulation/la répétition infinie, le rembobinage/la relecture, le débogage temporel et ainsi de suite. L’immutabilité peut être obtenue dans l’un ou l’autre paradigme, mais une prolifération d’objets à état partagé complique l’implémentation dans la POO.

Mauvaises réponses :

Incapable de lister les désavantages d’un style ou d’un autre – Toute personne expérimentée avec l’un ou l’autre style aurait dû se heurter à certaines des limitations.
  1. Quand l’héritage classique est-il un choix approprié ?

10 questions à poser lors d’un entretien que chaque développeur JavaScript doit savoir

La réponse est jamais, ou presque jamais. Certainement jamais plus d’un niveau. Les hiérarchies de classes multi-niveaux sont un anti-patron. On a lancé ce défi pendant des années, et les seules réponses qu’on n’a jamais entendues font partie de l’une de plusieurs idées fausses communes. Plus souvent, le défi est rencontré avec le silence.

« Une fonctionnalité est parfois utile et parfois dangereuse et s’il y a une meilleure option, alors utilisez toujours la meilleure option. » ~ Douglas Crockford

Bonnes réponses :

  • Rarement, presque jamais ou jamais.
  • Un seul niveau est parfois correct, à partir d’une classe de base du framework telle que React.Component.
  • « Favoriser la composition d’objet sur l’héritage de classe. »

  1. Quand l’héritage prototypique est-il un choix approprié ?

10 questions à poser lors d’un entretien que chaque développeur JavaScript doit savoir

Il existe plusieurs types d’héritage prototypique :

  • Délégation (c.-à-d., La chaîne prototype).
  • Concaténative (c’est-à-dire, mixins, `Object.assign()`).
  • Fonctionnel (à ne pas confondre avec la programmation fonctionnelle : une fonction utilisée pour créer une fermeture pour un état privé/encapsulation).

Chaque type d’héritage prototypique possède son propre ensemble de cas d’utilisation, mais tous sont également utiles dans leur capacité à activer la composition, ce qui crée des relations has-a ou uses-a ou can-do par opposition à la relation is-a créé avec l’héritage de classe.

Bonnes réponses :

  • Dans les situations où les modules ou la programmation fonctionnelle ne fournissent pas une solution évidente.
  • Lorsque vous devez composer des objets à partir de plusieurs sources.
  • Chaque fois que vous avez besoin d’héritage.

Mauvaises réponses :

  • Aucune connaissance de quand utiliser des prototypes.
  • Aucune connaissance de mixins ou `Object.assign()`.

  1. Que veut dire « favoriser la composition d’objet par rapport à l’héritage de classe » ?

10 questions à poser lors d’un entretien que chaque développeur JavaScript doit savoir

Ceci est une citation de « Design Patterns : Éléments de logiciels orientés objet réutilisables ». Cela signifie que la réutilisation de code devrait être réalisée en assemblant de plus petites unités de fonctionnalité dans de nouveaux objets au lieu d’hériter des classes et de créer des taxonomies d’objet.

En d’autres termes, utilisez les relations can-do, has-a, ou use-a au lieu des relations is-a.

Bonnes réponses :

  • Éviter les hiérarchies de classes.
  • Éviter les problèmes de classe de base fragile.
  • Éviter le couplage serré.
  • Éviter la taxonomie rigide (une relation is-a forcée qui est finalement erronée pour de nouveaux cas d’utilisation).
  • Éviter le problème de la banane gorille (« ce que vous vouliez était une banane, ce que vous avez eu était un gorille tenant la banane, et la jungle entière »).
  • Rendre le code plus flexible.

Mauvaises réponses :

  • Ne pas mentionner aucun des problèmes ci-dessus.
  • Incapable d’articuler la différence entre la composition et l’héritage de classe, ou les avantages de la composition.

  1. Qu’est-ce que la liaison de données bidirectionnelle et le flux de données à sens unique, et en quoi sont-ils différents ?

10 questions à poser lors d’un entretien que chaque développeur JavaScript doit savoir

La liaison de données bidirectionnelle signifie que les champs de l’interface utilisateur sont liés à la modélisation dynamique des données de telle sorte que lorsqu’un champ de l’interface utilisateur change, les données du modèle changent avec lui et inversement.

Le flux de données à sens unique signifie que le modèle est la seule source de vérité. Les modifications dans l’interface utilisateur déclenchent des messages qui signalent l’intention de l’utilisateur au modèle (ou « stocker » dans React). Seul le modèle a l’accès pour changer l’état de l’application. L’effet est que les données circulent toujours dans une seule direction, ce qui les rend plus faciles à comprendre.

Les flux de données à sens unique sont déterministes, alors que la liaison bidirectionnelle peut entraîner des effets secondaires plus difficiles à suivre et à comprendre.

Bonnes réponses :

  • React est le nouvel exemple canonique du flux de données à sens unique, donc les mentions de React sont un bon signal. Cycle.js est une autre implémentation populaire du flux de données à sens unique.
  • Angulaire est un framework populaire qui utilise la liaison bidirectionnelle.

Mauvaises réponses :

Aucune compréhension de ce que l’un signifie. Incapable d’articuler la différence.
  1. Quels sont les avantages et les inconvénients des architectures monolithiques vs microservices ?

10 questions à poser lors d’un entretien que chaque développeur JavaScript doit savoir

Une architecture monolithique signifie que votre application est écrite comme une unité de code cohérente dont les composants sont conçus pour fonctionner ensemble, partageant le même espace mémoire et les mêmes ressources.

Une architecture microservices signifie que votre application est constituée de nombreuses applications indépendantes de plus petite taille, capables de fonctionner dans leur propre espace mémoire et de s’adapter indépendamment l’une de l’autre sur plusieurs machines distinctes.

Avantages de monolithique : Le principal avantage de l’architecture monolithique est que la plupart des applications ont généralement un grand nombre de problèmes transversaux, tels que la journalisation, la limitation de débit et les fonctionnalités de sécurité telles que les pistes d’audit et la protection DOS.

Lorsque tout fonctionne à travers la même application, il est facile de raccorder des composants à ces préoccupations transversales.

Il peut également y avoir des avantages de performance, car l’accès à la mémoire partagée est plus rapide que la communication inter-processus (IPC).

Inconvénients de monolithique : les services d’applications monolithiques ont tendance à être fortement couplés et intriqués à mesure que l’application évolue, ce qui rend difficile l’isolation des services à des fins telles que la mise à l’échelle indépendante ou la maintenabilité du code.

Les architectures monolithiques sont également beaucoup plus difficiles à comprendre, car il peut y avoir des dépendances, des effets secondaires et de la magie qui ne sont pas évidents lorsque vous regardez un service ou un contrôleur particulier.

Avantages de microservice : Les architectures de microservice sont généralement mieux organisées, car chaque microservice a un travail très spécifique et ne s’occupe pas des tâches des autres composants. Les services découplés sont également plus faciles à recomposer et à reconfigurer pour répondre aux besoins de différentes applications (par exemple, servir à la fois les clients Web et l’API publique).

Elles peuvent également présenter des avantages en termes de performances, en fonction de leur organisation, car il est possible d’isoler les services hot et de les mettre à l’échelle indépendamment du reste de l’application.

Inconvénients de microservice : Au fur et à mesure que vous construisez une nouvelle architecture de microservice, vous risquez de découvrir beaucoup de problèmes transversaux que vous n’aviez pas anticipés au moment de la conception. Une application monolithique pourrait établir des aides magiques partagées ou des intergiciels pour gérer de telles préoccupations transversales sans trop d’efforts.

Dans une architecture de microservice, vous devrez soit assumer la surcharge de modules séparés pour chaque préoccupation intersectorielle, soit encapsuler des problèmes transversaux dans une autre couche de service à laquelle tout le trafic sera acheminé.

Finalement, même les architectures monolithiques tendent à acheminer le trafic à travers une couche de service externe pour des préoccupations transversales, mais avec une architecture monolithique, il est possible de retarder le coût de ce travail jusqu’à ce que le projet soit beaucoup plus mature.

Les microservices sont fréquemment déployés sur leurs propres machines ou conteneurs virtuels, ce qui entraîne une prolifération des tâches de démontage des machines virtuelles. Ces tâches sont fréquemment automatisées avec des outils de gestion de flotte de conteneurs.

Bonnes réponses :

  • Attitudes positives envers les microservices, malgré le coût initial plus élevé par rapport aux applications monolithiques. Conscient du fait que les microservices ont tendance à performer et à s’améliorer à long terme.
  • Pratique sur les microservices par rapport aux applications monolithiques. Structurer l’application afin que les services soient indépendants les uns des autres au niveau du code, mais faciles à regrouper en une application monolithique au début. Les frais généraux du microservice peuvent être retardés jusqu’à ce qu’il devienne plus pratique pour payer le prix.

Mauvaises réponses :

  • Aucune connaissance des différences entre les architectures monolithiques et microservices.
  • Aucune connaissance ou pratique peu les frais généraux additionnels des microservices.
  • Aucune connaissance des surcharges de performances supplémentaires provoquées par la communication IPC et réseau pour les microservices.
  • Trop négatif sur les inconvénients des microservices. Incapable d’articuler sur les façons de découpler les applications monolithiques de sorte qu’elles soient faciles à scinder en microservices quand le moment viendra.
  • Sous-estime l’avantage de microservices indépendamment évolutifs.

  1. Qu’est-ce que la programmation asynchrone, et pourquoi est-ce important en JavaScript ?

10 questions à poser lors d’un entretien que chaque développeur JavaScript doit savoir

La programmation synchrone signifie que, à l’exception des conditions et des appels de fonction, le code est exécuté séquentiellement de haut en bas, en bloquant les tâches de longue durée telles que les requêtes réseau et les E/S disque.

La programmation asynchrone signifie que le moteur tourne dans une boucle d’événement. Lorsqu’une opération de blocage est nécessaire, la demande est lancée et le code continue de s’exécuter sans bloquer le résultat. Lorsque la réponse est prête, une interruption est déclenchée, ce qui provoque l’exécution d’un gestionnaire d’événements, où le flux de contrôle continue. De cette manière, un thread de programme unique peut gérer de nombreuses opérations simultanées.

Les interfaces utilisateur sont asynchrones par nature et passent le plus clair de leur temps à attendre que l’entrée de l’utilisateur interrompe la boucle d’événements et déclenche les gestionnaires d’événements.

Le nœud est asynchrone par défaut, ce qui signifie que le serveur fonctionne à peu près de la même manière, qu’il attend une boucle pour une requête réseau et qu’il accepte plus de requêtes entrantes pendant que le premier est géré.

Ceci est important en JavaScript, car il s’agit d’un ajustement très naturel pour le code d’interface utilisateur, et très bénéfique pour les performances sur le serveur.

Bonnes réponses :

  • Une compréhension de ce que les moyens de blocage, et les implications de performance.
  • Une compréhension de la gestion des événements, et pourquoi c’est important pour le code de l’interface utilisateur.

Mauvaises réponses :

  • Peu familier avec les termes asynchrones ou synchrones.
  • Incapable d’articuler les implications de performances ou la relation entre le code asynchrone et le code d’interface utilisateur.


Retour à La Une de Logo Paperblog

A propos de l’auteur


Rhw 642 partages Voir son profil
Voir son blog

Magazine