Comment Google crée des frameworks Web

Il est de notoriété publique que Google utilise un référentiel unique pour partager le code - les 2 milliards de lignes de celui-ci - et qu'il utilise le paradigme de développement basé sur le tronc.

Pour de nombreux développeurs extérieurs à l'entreprise, c'est surprenant et contre-intuitif, mais cela fonctionne vraiment bien. (L'article lié ci-dessus donne de bons exemples, je ne les répéterai donc pas ici.)

La base de code de Google est partagée par plus de 25 000 développeurs de logiciels Google dans des dizaines de bureaux dans le monde entier. Lors d'une journée de travail typique, ils commettent 16 000 modifications dans la base de code. (la source)

Cet article traite des spécificités de la création d'un framework web open source (AngularDart) dans ce contexte.

Une seule version

Lorsque vous utilisez le développement basé sur le tronc dans un seul et vaste référentiel, vous n'avez qu'une seule version de tout. C'est assez évident. Cependant, il est toujours bon de le signaler ici, car cela signifie que - chez Google - vous ne pouvez pas avoir l'application FooBar qui utilise AngularDart 2.2.1 et une autre application BarFoo qui est sur 2.3.0. Les deux applications doivent être sur la même version - la dernière.

C'est pourquoi les googleurs disent parfois que tous les logiciels de Google sont à la pointe de la technologie.

Si toute votre âme crie «dangereux! en ce moment, c'est compréhensible. Selon le tronc ('master' dans la terminologie git) d'une bibliothèque avec votre code de production, cela semble dangereux. Mais il y a un rebondissement à venir.

74 mille tests par commit

AngularDart définit 1601 tests (ici). Mais lorsque vous validez une modification du code AngularDart dans le référentiel Google, il exécute également des tests pour tous les utilisateurs de Google qui dépendent du framework . Pour le moment, cela représente environ 74 000 tests (en fonction de l'ampleur de votre changement - une heuristique ignore les tests que le système sait que vous n'affectez pas).

C'est bien d'avoir plus de tests.

Je viens de faire un changement qui ne se manifeste que 5% du temps, simulant quelque chose comme une condition de course dans l'algorithme de vérification de la réinsertion de détection de changement (j'ai ajouté && random.nextDouble() >0,05 à cette instruction if). Cela ne s'est manifesté dans aucun des 1601 tests lorsque je les ai exécutés (une fois). Mais cela a cassé un tas de tests clients.

La vraie valeur ici, cependant, est que ce sont des tests d' applications réelles . Non seulement ils sont nombreux, mais ils reflètent également la façon dont le framework est utilisé par les développeurs (pas seulement les auteurs du framework). Ceci est important: les propriétaires de framework n'évaluent pas toujours correctement comment leur framework est utilisé.

Cela aide également à ce que ces applications soient en production et que des milliards de dollars les traversent chaque mois. Il y a une grande différence entre les applications de démonstration qu'un auteur de framework met sur pied pendant son temps libre et les vraies applications de production avec des dizaines ou des centaines d'années-personnes investies dans elles. Pour que le web soit pertinent à l'avenir, nous devons mieux accompagner le développement de ce dernier.

Alors, que se passe-t-il si le framework rompt certaines des applications qui y sont construites?

Vous le cassez, vous le réparez

Lorsque les auteurs d'AngularDart veulent introduire un changement de rupture, ils doivent aller le corriger pour leurs utilisateurs . Étant donné que tout chez Google réside dans un seul dépôt, il est trivial de savoir qui ils cassent et ils peuvent commencer à réparer immédiatement.

Toute modification de rupture apportée à AngularDart inclut également toutes les corrections apportées à cette modification dans toutes les applications Google qui en dépendent. Ainsi, la rupture et le correctif entrent dans le repo simultanément et - bien sûr - après un examen du code approprié par toutes les parties concernées.

Donnons un exemple concret. Lorsqu'un membre de l'équipe AngularDart apporte une modification qui affecte le code dans l'application AdWords, il accède au code source de cette application et le corrige. Ils peuvent exécuter les tests existants d'AdWords dans le processus et en ajouter de nouveaux. Ensuite, ils mettent tout cela dans leur liste de modifications et demandent un examen. Étant donné que leur liste de modifications touche le code à la fois dans le référentiel AngularDart et dans le référentiel AdWords, le système exige automatiquement l'approbation de l'examen du code de ces deux équipes. Ce n'est qu'alors que le changement peut être soumis.

Cela a pour effet évident d'empêcher le développement de cadres dans le vide. Les développeurs de framework AngularDart ont accès à des millions de lignes de code qui sont construites avec leur plate-forme, et ils touchent régulièrement ce code eux-mêmes. Ils n'ont pas besoin de supposer comment leur cadre est utilisé. (La mise en garde évidente est qu'ils ne voient que le code Google et non le code de tous les Workivas, Wrikes et StableKernels du monde qui utilisent également AngularDart.)

Le fait de devoir mettre à jour le code de vos utilisateurs ralentit également le développement. Pas autant que vous ne le pensez (regardez les progrès d'AngularDart depuis octobre), mais cela ralentit toujours les choses. C'est à la fois bon et mauvais, selon ce que vous attendez d'un framework. Nous y reviendrons.

En tous cas. La prochaine fois que quelqu'un chez Google dit qu'une version alpha d'une bibliothèque est stable et en production, vous savez maintenant pourquoi.

Changements à grande échelle

Et si AngularDart devait faire un changement majeur (par exemple, passer de 2.x à 3.0) et que ce changement casse 74 000 tests? L'équipe ira-t-elle les réparer tous? Vont-ils apporter des modifications à des milliers de fichiers source, dont la plupart n'ont pas été créés?

Oui.

L'un des avantages d'avoir un système de type sonore est que votre outillage peut être beaucoup plus utile. Dans Sound Dart, les outils peuvent être sûrs qu'une variable est d'un certain type, par exemple. Pour le refactoring, cela signifie que de nombreuses modifications peuvent être complètement automatiques, sans avoir besoin de confirmation du développeur.

Lorsqu'une méthode de la classe Foo passe de bar()à baz(), vous pouvez créer un outil qui parcourt l'intégralité du référentiel Google unique, trouve toutes les instances de cette classe Foo et de ses sous-classes, et modifie toutes les mentions de bar()en baz(). Avec le système de type sonore de Dart, vous pouvez être sûr que cela ne cassera rien. Sans types de sons, même un changement aussi simple peut vous causer des ennuis.

Une autre chose qui aide avec les changements à grande échelle est dart_style, le formateur par défaut de Dart. Tout le code Dart de Google est formaté à l'aide de cet outil. Au moment où votre code atteint les réviseurs, il a été automatiquement formaté à l'aide de dart_style, il n'y a donc aucun argument sur l'opportunité de placer la nouvelle ligne ici ou là. Et cela s'applique également aux refactors à grande échelle.

Indicateurs de performance

Comme je l'ai dit plus haut, AngularDart bénéficie des tests de ses dépendants. Mais ce ne sont pas que des tests. Google mesure très rigoureusement les performances de ses applications, et la plupart (toutes?) Des applications de production ont donc des suites de référence.

Ainsi, lorsque l'équipe AngularDart introduit une modification qui ralentit le chargement d'AdWords de 1%, elle le sait avant de procéder au changement. Lorsque l'équipe a déclaré en octobre que les applications AngularDart étaient 40% plus petites et 10% plus rapides depuis août, elles ne parlaient pas de quelques minuscules exemples d'applications synthétiques de TodoMVC. Ils parlaient d'applications de production réelles et critiques avec des millions d'utilisateurs et des mégaoctets de code de logique métier.

Note latérale: outil de construction hermétique

Vous vous demandez peut-être: comment ce gars a-t-il su quels tests dans l'énorme référentiel interne exécuter après avoir introduit le bogue floconneux dans AngularDart? Il ne sélectionnait sûrement pas les 74 000 tests à la main, et tout aussi sûrement, il n'exécutait pas tous les tests chez Google. La réponse réside dans quelque chose qui s'appelle Bazel.

À cette échelle, vous ne pouvez pas avoir une série de scripts shell pour créer des éléments. Les choses seraient irrégulières et d'une lenteur prohibitive. Ce dont vous avez besoin est un outil de construction hermétique.

«Hermétique» dans ce contexte est très similaire à «pur» dans le contexte des fonctions. Vos étapes de construction ne peuvent pas avoir d'effets secondaires (comme les fichiers temporaires, les modifications de PATH, etc.), et elles doivent être déterministes (la même entrée mène toujours à la même sortie). Lorsque c'est le cas, vous pouvez exécuter les builds et les tests sur n'importe quelle machine à tout moment et vous obtiendrez un résultat cohérent. Vous n'en avez pas besoin make clean. Vous pouvez donc envoyer vos builds / tests pour construire des serveurs et les paralléliser.

Google a passé des années à développer un tel outil de création. Il était open source l'année dernière sous le nom de Bazel.

Et grâce à cette infrastructure, les outils de test internes peuvent déterminer les builds / tests concernés par chaque modification et les exécuter le cas échéant.

Qu'est-ce que tout cela veut dire?

L'objectif explicite d'AngularDart est d'être le meilleur de sa catégorie en termes de productivité, de performances et de fiabilité pour la création de grandes applications Web. Nous espérons que cet article couvre la dernière partie - la fiabilité - et pourquoi il est important que les applications Google critiques comme AdWords et AdSense utilisent le cadre. Ce n'est pas seulement l'équipe qui se vante de ses utilisateurs - comme expliqué ci-dessus, avoir de grands utilisateurs internes rend AngularDart moins susceptible d'introduire des changements superficiels. Cela rend le cadre plus fiable.

Si vous recherchez un cadre qui effectue des révisions majeures et introduit des fonctionnalités majeures tous les quelques mois, AngularDart n'est certainement pas pour vous. Même si l'équipe voulait construire le cadre de cette manière, je pense qu'il ressort clairement de cet article qu'elle ne le pouvait pas. Nous croyons sincèrement, cependant, qu'il y a de la place pour un cadre moins tendance mais fiable.

À mon avis, la meilleure prédiction du support à long terme d'une pile technologique open source est que c'est une grande partie de l'activité du principal responsable. Prenez Android, dagger, MySQL ou git comme exemples. C'est pourquoi je suis heureux que Dart ait enfin un framework Web préféré (AngularDart), une bibliothèque de composants préférée (AngularDart Components) et un framework mobile préféré (Flutter) - qui sont tous utilisés pour créer des applications Google critiques pour l'entreprise.

[Matan Lurey et Kathy Walrath ont contribué à cet article.]

[Discutez sur Reddit, HN, Twitter.]