Actualisation de l'architecture des outils de développement: migration des outils de développement vers TypeScript

Tim van der Lippe
Tim van der Lippe

Cet article fait partie d'une série de posts de blog décrivant les modifications que nous apportons à l'architecture de DevTools et à sa création.

Après la migration vers les modules JavaScript et la migration vers les composants Web, nous poursuivons aujourd'hui notre série d'articles de blog sur les modifications que nous apportons à l'architecture de DevTools et à sa création. (Si vous ne l'avez pas encore vue, nous avons publié une vidéo sur notre travail de mise à niveau de l'architecture des outils pour les développeurs vers le Web moderne, avec 14 conseils pour améliorer vos projets Web.)

Dans cet article, nous allons décrire notre parcours de 13 mois, qui nous a permis de passer du vérificateur de type du compilateur Closure à TypeScript.

Introduction

Compte tenu de la taille du codebase DevTools et de la nécessité de rassurer les ingénieurs qui y travaillent, l'utilisation d'un vérificateur de type est une nécessité. À cette fin, DevTools a adopté le compilateur Closure dès 2013. L'adoption de Closure a permis aux ingénieurs DevTools d'apporter des modifications en toute confiance. Le compilateur Closure effectuait des vérifications de type pour s'assurer que toutes les intégrations système étaient correctement typées.

Cependant, au fil du temps, d'autres outils de vérification des types sont devenus populaires dans le développement Web moderne. TypeScript et Flow en sont deux exemples notables. De plus, TypeScript est devenu un langage de programmation officiel chez Google. Bien que ces nouveaux vérificateurs de type aient gagné en popularité, nous avons également constaté que nous livrions des régressions qui auraient dû être détectées par un vérificateur de type. Nous avons donc décidé de réévaluer notre choix de vérificateur de type et de déterminer les prochaines étapes de développement dans DevTools.

Évaluer les vérificateurs de type

Comme DevTools utilisait déjà un vérificateur de type, la question à laquelle nous devions répondre était la suivante:

Continuons-nous à utiliser Closure Compiler ou migrons-nous vers un nouveau vérificateur de type ?

Pour répondre à cette question, nous avons dû évaluer les vérificateurs de type sur plusieurs caractéristiques. Étant donné que notre utilisation d'un vérificateur de type est axée sur la confiance des ingénieurs, l'aspect le plus important pour nous est la correction des types. En d'autres termes, à quel point un vérificateur de type est-il fiable pour détecter les problèmes réels ?

Notre évaluation s'est concentrée sur les régressions que nous avions publiées et sur l'identification de leurs causes. Nous supposons ici que, comme nous utilisions déjà le compilateur Closure, Closure n'aurait pas détecté ces problèmes. Nous devons donc déterminer si un autre vérificateur de type aurait pu le faire.

Correction des types en TypeScript

TypeScript étant un langage de programmation officiellement pris en charge par Google et dont la popularité augmente rapidement, nous avons décidé de l'évaluer en premier. TypeScript était un choix intéressant, car l'équipe TypeScript elle-même utilise DevTools comme l'un de ses projets de test pour suivre sa compatibilité avec la validation de types JavaScript. La sortie du test de référence de référence avait montré que TypeScript détectait un grand nombre de problèmes de type, des problèmes que le compilateur Closure ne détectait pas nécessairement. De nombreux problèmes étaient probablement à l'origine des régressions que nous livrions. Cela nous a convaincus que TypeScript pouvait être une option viable pour DevTools.

Lors de notre migration vers les modules JavaScript, nous avons déjà découvert que Closure Compiler détectait plus de problèmes qu'auparavant. Le passage à un format de module standard a permis à Closure de mieux comprendre notre codebase, ce qui a amélioré l'efficacité des vérificateurs de type. Toutefois, l'équipe TypeScript utilisait une version de référence de DevTools antérieure à la migration des modules JavaScript. Nous avons donc dû déterminer si la migration vers les modules JavaScript avait également réduit le nombre d'erreurs détectées par le compilateur TypeScript.

Évaluer TypeScript

Les outils pour les développeurs existent depuis plus de 10 ans. Au fil du temps, ils sont devenus une application Web de taille considérable et dotée de nombreuses fonctionnalités. Au moment de la rédaction de cet article, DevTools contient environ 150 000 lignes de code JavaScript first party. Lorsque nous avons exécuté le compilateur TypeScript sur notre code source, le volume d'erreurs était écrasant. Nous avons pu déterminer que, même si le compilateur TypeScript émettait moins d'erreurs liées à la résolution du code (environ 2 000 erreurs), 6 000 erreurs supplémentaires étaient présentes dans notre codebase liées à la compatibilité des types.

Cela a montré que, bien que TypeScript ait pu comprendre comment résoudre les types, il a détecté un nombre important d'incompatibilités de types dans notre codebase. Une analyse manuelle de ces erreurs a montré que TypeScript était (la plupart du temps) correct. TypeScript a pu les détecter, contrairement à Closure, car le compilateur Closure déduisait souvent qu'un type était un Any, tandis que TypeScript effectuait une inférence de type basée sur les attributions et déduisait un type plus précis. Par conséquent, TypeScript a effectivement mieux compris la structure de nos objets et découvert des utilisations problématiques.

Un point important à noter est que l'utilisation du compilateur Closure dans les outils pour les développeurs incluait l'utilisation fréquente de @unrestricted. Annoter une classe avec @unrestricted désactive effectivement les vérifications strictes des propriétés du compilateur Closure pour cette classe spécifique. Cela signifie qu'un développeur peut modifier une définition de classe à volonté sans sécurité de type. Nous n'avons trouvé aucun contexte historique expliquant pourquoi l'utilisation de @unrestricted était prédominante dans le codebase DevTools. Toutefois, cela a entraîné l'exécution du compilateur Closure dans un mode de fonctionnement moins sécurisé pour de grandes parties du codebase.

Une analyse croisée de nos régressions avec les erreurs de type détectées par TypeScript a également révélé un chevauchement, ce qui nous a amenés à penser que TypeScript aurait pu éviter ces problèmes (à condition que les types eux-mêmes soient corrects).

Effectuer un appel any

À ce stade, nous devions choisir entre améliorer notre utilisation de Closure Compiler ou migrer vers TypeScript. (Étant donné que Flow n'était pas compatible avec Google ni avec Chromium, nous avons dû renoncer à cette option.) Après avoir discuté avec des ingénieurs Google qui travaillent sur les outils JavaScript/TypeScript et reçu leurs recommandations, nous avons choisi le compilateur TypeScript. (Nous avons également récemment publié un article de blog sur la migration de Puppeteer vers TypeScript.)

Les principales raisons du choix du compilateur TypeScript sont l'amélioration de la correction des types, ainsi que l'assistance des équipes TypeScript internes de Google et les fonctionnalités du langage TypeScript, telles que interfaces (par opposition à typedefs dans JSDoc).

Le choix du compilateur TypeScript nous a obligés à investir massivement dans le code de base des outils de développement et son architecture interne. Nous avons donc estimé qu'il nous faudrait au moins un an pour migrer vers TypeScript (prévu pour le troisième trimestre 2020).

Procéder à la migration

La question la plus importante restait: comment allons-nous migrer vers TypeScript ? Nous avons 150 000 lignes de code et nous ne pouvons pas les migrer en une seule fois. Nous savions également que l'exécution de TypeScript sur notre codebase révélerait des milliers d'erreurs.

Nous avons évalué plusieurs options:

  1. Obtenir toutes les erreurs TypeScript et les comparer à une sortie "d'or" Cette approche serait semblable à celle de l'équipe TypeScript. Le plus grand inconvénient de cette approche est la fréquence élevée des conflits de fusion, car des dizaines d'ingénieurs travaillent sur le même codebase.
  2. Définissez tous les types problématiques sur any. TypeScript supprimerait alors les erreurs. Nous n'avons pas choisi cette option, car notre objectif était de migrer les types de manière correcte, ce qui aurait été compromis par la suppression.
  3. Corrigez manuellement toutes les erreurs TypeScript. Cela impliquerait de corriger des milliers d'erreurs, ce qui est chronophage.

Malgré l'effort important que cela implique, nous avons opté pour l'option 3. Nous avons choisi cette option pour d'autres raisons: par exemple, elle nous a permis d'auditer tout le code et d'effectuer un examen décennal de toutes les fonctionnalités, y compris de leur implémentation. D'un point de vue commercial, nous n'apportions pas de valeur ajoutée, mais nous maintenions le statu quo. Il a donc été plus difficile de justifier l'option 3 comme étant la bonne.

Toutefois, en adoptant TypeScript, nous étions convaincus que nous pourrions éviter de futurs problèmes, en particulier en ce qui concerne les régressions. L'argument était donc moins"Nous ajoutons une nouvelle valeur commerciale " que "Nous nous assurons de ne pas perdre la valeur commerciale obtenue".

Compatibilité du compilateur TypeScript avec JavaScript

Après avoir obtenu l'adhésion et développé un plan d'exécution du compilateur Closure et du compilateur TypeScript sur le même code JavaScript, nous avons commencé avec quelques petits fichiers. Notre approche était principalement ascendante: nous avons commencé par le code principal, puis remonté l'architecture jusqu'aux panneaux de haut niveau.

Nous avons pu paralléliser notre travail en ajoutant de manière préventive @ts-nocheck à chaque fichier de DevTools. Pour "corriger TypeScript", vous devez supprimer l'annotation @ts-nocheck et résoudre les erreurs détectées par TypeScript. Nous étions ainsi sûrs que chaque fichier avait été vérifié et que le plus grand nombre possible de problèmes de type avaient été résolus.

En général, cette approche a fonctionné avec peu de problèmes. Nous avons rencontré plusieurs bugs dans le compilateur TypeScript, mais la plupart d'entre eux étaient obscurs:

  1. Un paramètre facultatif avec un type de fonction qui renvoie any est traité comme obligatoire: #38551
  2. L'attribution d'une propriété à une méthode statique d'une classe interrompt la déclaration: #38553
  3. La déclaration d'une sous-classe avec un constructeur sans argument et d'une super-classe avec un constructeur avec arguments omet le constructeur enfant: #41397

Ces bugs soulignent que, dans 99% des cas, le compilateur TypeScript constitue une base solide sur laquelle s'appuyer. Oui, ces bugs obscurs causaient parfois des problèmes pour DevTools, mais la plupart du temps, ils étaient suffisamment obscurs pour que nous puissions facilement les contourner.

Le seul problème qui a causé de la confusion était la sortie non déterministe des fichiers .tsbuildinfo (problème 37156). Chez Chromium, nous exigeons que deux builds du même commit Chromium génèrent exactement le même résultat. Malheureusement, nos ingénieurs de compilation Chromium ont découvert que la sortie .tsbuildinfo n'était pas déterministe: crbug.com/1054494. Pour contourner ce problème, nous avons dû effectuer un monkey-patch du fichier .tsbuildinfo (qui contient essentiellement du JSON) et le post-traiter pour renvoyer une sortie déterministe: https://crrev.com/c/2091448. Heureusement, l'équipe TypeScript a résolu le problème en amont, et nous avons pu supprimer notre solution de contournement. Merci à l'équipe TypeScript d'avoir pris en compte les signalements de bugs et de les avoir résolus rapidement.

Dans l'ensemble, nous sommes satisfaits de la justesse (de type) du compilateur TypeScript. Nous espérons que Devtools, en tant que grand projet JavaScript Open Source, a contribué à renforcer la compatibilité JavaScript dans TypeScript.

Analyser les conséquences

Nous avons pu faire de bons progrès pour résoudre ces erreurs de type et augmenter progressivement la quantité de code vérifiée par TypeScript. Toutefois, en août 2020 (neuf mois après le début de cette migration), nous avons fait un point et découvert que nous ne respecterions pas notre échéance avec notre rythme actuel. L'un de nos ingénieurs a créé un graphique d'analyse pour montrer la progression de la "typification" (nom que nous avons donné à cette migration).

Progression de la migration TypeScript

Progression de la migration TypeScript : suivi des lignes de code restantes à migrer

Les estimations de la date à laquelle nous n'aurions plus aucune ligne à supprimer variaient de juillet 2021 à décembre 2021, soit presque un an après notre échéance. Après avoir discuté avec la direction et d'autres ingénieurs, nous avons décidé d'augmenter le nombre d'ingénieurs travaillant sur la migration vers la prise en charge du compilateur TypeScript. Cela a été possible, car nous avons conçu la migration de manière à ce qu'elle puisse être parallélisée afin que plusieurs ingénieurs travaillant sur plusieurs fichiers différents ne se trouvent pas en conflit les uns avec les autres.

À ce stade, le processus de TypeScriptification est devenu un effort d'équipe. Grâce à cette aide supplémentaire, nous avons pu terminer notre migration fin novembre 2020, 13 mois après le début, et plus d'un an avant notre estimation initiale.

Au total, 771 changelists (similaires à une requête pull) ont été envoyées par 18 ingénieurs. Notre bug de suivi (https://crbug.com/1011811) compte plus de 1 200 commentaires (presque tous des posts automatisés à partir de listes de modifications). Notre feuille de suivi comptait plus de 500 lignes pour tous les fichiers à convertir en TypeScript, leur affectation et la liste de modifications dans laquelle ils ont été convertis.

Atténuer l'impact des performances du compilateur TypeScript

Le plus gros problème auquel nous faisons face aujourd'hui est la lenteur des performances du compilateur TypeScript. Compte tenu du nombre d'ingénieurs qui compilent Chromium et DevTools, ce goulot d'étranglement est coûteux. Malheureusement, nous n'avons pas pu identifier ce risque avant notre migration. Ce n'est que lorsque nous avons migré la majorité des fichiers vers TypeScript que nous avons découvert une augmentation notable du temps passé sur les builds Chromium: https://crbug.com/1139220

Nous avons signalé ce problème à l'équipe de compilation Microsoft TypeScript en amont, mais malheureusement, elle a déterminé que ce comportement était intentionnel. Nous espérons qu'ils reviendront sur cette décision, mais en attendant, nous nous efforçons de réduire autant que possible l'impact de la lenteur sur les performances côté Chromium.

Malheureusement, les solutions dont nous disposons aujourd'hui ne sont pas toujours adaptées aux contributeurs qui ne sont pas employés Google. Les contributions Open Source à Chromium sont très importantes (en particulier celles de l'équipe Microsoft Edge). Nous recherchons donc activement des alternatives qui conviendront à tous les contributeurs. Toutefois, nous n'avons pas encore trouvé de solution de remplacement adaptée.

État actuel de TypeScript dans DevTools

Pour le moment, nous avons supprimé le vérificateur de type du compilateur Closure de notre codebase et nous nous appuyons uniquement sur le compilateur TypeScript. Nous pouvons écrire des fichiers rédigés en TypeScript et utiliser des fonctionnalités spécifiques à TypeScript (interfaces, génériques, etc.), ce qui nous aide au quotidien. Nous sommes plus confiants que le compilateur TypeScript détectera les erreurs de type et les régressions, ce que nous espérions lorsque nous avons commencé à travailler sur cette migration. Cette migration, comme tant d'autres, a été lente, nuancée et souvent difficile, mais nous pensons qu'elle en valait la peine.

Télécharger les canaux de prévisualisation

Envisagez d'utiliser Chrome Canary, Dev ou Bêta comme navigateur de développement par défaut. Ces canaux de prévisualisation vous donnent accès aux dernières fonctionnalités de DevTools, vous permettent de tester les API de plate-forme Web de pointe et vous aident à détecter les problèmes sur votre site avant vos utilisateurs.

Contacter l'équipe des outils pour les développeurs Chrome

Utilisez les options suivantes pour discuter des nouvelles fonctionnalités, des mises à jour ou de tout autre élément lié aux outils pour les développeurs.