Dieser Beitrag ist Teil einer Reihe von Blogbeiträgen, in denen die Änderungen an der Architektur und der Erstellung von DevTools beschrieben werden.
Nach der Migration zu JavaScript-Modulen und der Migration zu Web-Komponenten setzen wir heute unsere Blogpost-Reihe über die Änderungen an der Architektur und der Erstellung von Devtools fort. Falls Sie es noch nicht gesehen haben: Wir haben ein Video über unsere Arbeit zur Modernisierung der Architektur der DevTools veröffentlicht. Darin finden Sie 14 Tipps zur Verbesserung Ihrer Webprojekte.
In diesem Beitrag beschreiben wir unsere 13-monatige Reise vom Closure Compiler-Typprüfer zu TypeScript.
Einführung
Angesichts der Größe der DevTools-Codebasis und der Notwendigkeit, den daran arbeitenden Entwicklern Sicherheit zu geben, ist die Verwendung eines Typprüfers unerlässlich. Zu diesem Zweck wurde 2013 der Closure Compiler in den DevTools eingeführt. Durch die Einführung von Closure konnten die DevTools-Entwickler Änderungen mit Zuversicht vornehmen. Der Closure-Compiler führte Typprüfungen durch, um sicherzustellen, dass alle Systemintegrationen korrekt getippt waren.
Im Laufe der Zeit wurden jedoch alternative Typprüfer in der modernen Webentwicklung immer beliebter. Zwei bekannte Beispiele sind TypeScript und Flow. Außerdem wurde TypeScript eine offizielle Programmiersprache bei Google. Diese neuen Typprüfer wurden immer beliebter, aber wir stellten auch fest, dass wir Rückschritte einbauten, die von einem Typprüfer erkannt hätten werden sollen. Deshalb haben wir uns entschieden, unsere Auswahl des Typprüfers noch einmal zu überdenken und die nächsten Schritte für die Entwicklung in den DevTools zu ermitteln.
Typprüfer bewerten
Da in den DevTools bereits ein Typprüfer verwendet wurde, mussten wir uns fragen:
Verwenden wir weiterhin den Closure Compiler oder migrieren wir zu einem neuen Typprüfer?
Um diese Frage zu beantworten, mussten wir die Typprüfer anhand verschiedener Merkmale bewerten. Da wir bei der Verwendung eines Typprüfers vor allem auf die Sicherheit der Entwickler achten, ist für uns die Typrichtigkeit der wichtigste Aspekt. Mit anderen Worten: Wie zuverlässig kann ein Typprüfer echte Probleme erkennen?
Bei unserer Bewertung haben wir uns auf die Regressionen konzentriert, die wir veröffentlicht hatten, und die zugrunde liegenden Ursachen ermittelt. Die Annahme hier ist, dass diese Probleme nicht vom Closure Compiler erkannt worden wären, da wir ihn bereits verwendet haben. Daher müssten wir feststellen, ob ein anderer Typprüfer dies hätte tun können.
Typkorrektheit in TypeScript
Da TypeScript eine offiziell von Google unterstützte Programmiersprache ist und immer beliebter wird, haben wir uns entschieden, TypeScript zuerst zu bewerten. TypeScript war eine interessante Wahl, da das TypeScript-Team selbst DevTools als eines seiner Testprojekte verwendet, um die Kompatibilität mit der JavaScript-Typprüfung zu verfolgen. Die Ausgabe des Referenztests hatte gezeigt, dass TypeScript eine große Anzahl von Typproblemen aufdeckte, die der Closure-Compiler nicht unbedingt erkannte. Viele dieser Probleme waren wahrscheinlich die Ursache für Regressionen, die wir freigegeben haben. Dies wiederum ließ uns glauben, dass TypeScript eine praktikable Option für DevTools sein könnte.
Bei der Migration zu JavaScript-Modulen hatten wir bereits festgestellt, dass der Closure Compiler mehr Probleme aufdeckte als zuvor. Durch die Umstellung auf ein standardmäßiges Modulformat konnte Closure unsere Codebasis besser verstehen und die Effektivität der Typprüfer steigern. Das TypeScript-Team verwendete jedoch eine Baseline-Version der DevTools, die vor der Migration der JavaScript-Module erstellt wurde. Daher mussten wir herausfinden, ob die Migration zu JavaScript-Modulen auch die Anzahl der Fehler reduziert hatte, die der TypeScript-Compiler erkennt.
TypeScript bewerten
DevTools gibt es seit über einem Jahrzehnt. In dieser Zeit hat es sich zu einer umfangreichen und funktionsreichen Webanwendung entwickelt. Zum Zeitpunkt der Erstellung dieses Blogposts enthält DevTools etwa 150.000 Zeilen selbst erstellten JavaScript-Codes. Als wir den TypeScript-Compiler auf unseren Quellcode anwendeten, war die schiere Menge an Fehlern überwältigend. Wir konnten feststellen, dass der TypeScript-Compiler zwar weniger Fehler im Zusammenhang mit der Codeauflösung ausgab (ungefähr 2.000 Fehler), es aber in unserer Codebasis noch weitere 6.000 Fehler im Zusammenhang mit der Typkompatibilität gab.
Das zeigte, dass TypeScript zwar in der Lage war, Typen zu lösen, aber eine erhebliche Anzahl von Inkompatibilitäten in unserer Codebasis fand.
Eine manuelle Analyse dieser Fehler hatte gezeigt, dass TypeScript (meistens) korrekt war.
Der Grund, warum TypeScript diese Probleme erkennen konnte und Closure nicht, ist, dass der Closure-Compiler einen Typ oft als Any
ableitete, während TypeScript eine genauere Typinferenz basierend auf Zuweisungen durchführte.
TypeScript konnte die Struktur unserer Objekte also besser nachvollziehen und problematische Verwendungen erkennen.
Ein wichtiger Punkt dabei ist, dass die Verwendung des Closure-Compilers in DevTools häufig die Verwendung von @unrestricted
erforderte.
Wenn eine Klasse mit @unrestricted
annotiert wird, werden die strengen Attributprüfungen des Closure-Compilers für diese bestimmte Klasse effektiv deaktiviert. Das bedeutet, dass ein Entwickler eine Klassendefinition nach Belieben ohne Typsicherheit erweitern kann.
Wir konnten keinen historischen Kontext finden, der erklärt, warum @unrestricted
in der DevTools-Codebasis so häufig verwendet wurde. Dies führte jedoch dazu, dass der Closure-Compiler für große Teile der Codebasis in einem weniger sicheren Betriebsmodus ausgeführt wurde.
Eine Kreuzanalyse unserer Regressionen mit den von TypeScript erkannten Typfehlern zeigte ebenfalls eine Überschneidung. Wir sind der Meinung, dass TypeScript diese Probleme hätte verhindern können, sofern die Typen selbst korrekt waren.
any
-Anruf
An diesem Punkt mussten wir uns entscheiden, ob wir die Nutzung des Closure Compilers verbessern oder zu TypeScript migrieren wollten. Da Flow weder bei Google noch in Chromium unterstützt wurde, mussten wir auf diese Option verzichten. Auf der Grundlage von Gesprächen mit und Empfehlungen von Google-Entwicklern, die an den JavaScript-/TypeScript-Tools arbeiten, haben wir uns für den TypeScript-Compiler entschieden. (Vor Kurzem haben wir auch einen Blogpost zur Migration von Puppeteer zu TypeScript veröffentlicht.)
Die Hauptgründe für den TypeScript-Compiler waren die verbesserte Typrichtigkeit. Weitere Vorteile waren die Unterstützung durch TypeScript-Teams intern bei Google und die Funktionen der TypeScript-Sprache, z. B. interfaces
(im Gegensatz zu typedefs
in JSDoc).
Die Entscheidung für den TypeScript-Compiler bedeutete, dass wir erheblich in die DevTools-Codebasis und ihre interne Architektur investieren mussten. Daher schätzten wir, dass wir mindestens ein Jahr für die Migration zu TypeScript benötigen würden (angestrebt war das 3. Quartal 2020).
Migration durchführen
Die größte Frage, die noch offen war: Wie gehen wir vor, um zu TypeScript zu migrieren? Wir haben 150.000 Codezeilen und können sie nicht auf einmal migrieren. Außerdem wussten wir,dass die Ausführung von TypeScript in unserer Codebasis Tausende von Fehlern aufdecken würde.
Wir haben mehrere Optionen geprüft:
- Alle TypeScript-Fehler abrufen und mit einer „Golden Copy“ vergleichen Dieser Ansatz ähnelt dem des TypeScript-Teams. Der größte Nachteil dieses Ansatzes ist die hohe Häufigkeit von Zusammenführungskonflikten, da Dutzende von Entwicklern an derselben Codebasis arbeiten.
- Legen Sie für alle problematischen Typen den Wert
any
fest. Dadurch würde TypeScript Fehler im Grunde unterdrücken. Wir haben diese Option nicht ausgewählt, da unser Ziel bei der Migration die korrekte Typzuweisung war, die durch die Unterdrückung beeinträchtigt würde. - Alle TypeScript-Fehler manuell beheben Das würde bedeuten, dass Tausende von Fehlern behoben werden müssten, was sehr zeitaufwendig ist.
Trotz des hohen erwarteten Aufwands haben wir uns für Option 3 entschieden. Es gab noch weitere Gründe, warum wir uns für diese Option entschieden haben: So konnten wir beispielsweise den gesamten Code prüfen und alle Funktionen, einschließlich ihrer Implementierung, einmal in zehn Jahren überprüfen. Aus geschäftlicher Sicht boten wir keinen Mehrwert, sondern hielten den Status quo aufrecht. Das machte es schwieriger, Option 3 als die richtige Wahl zu rechtfertigen.
Wir waren jedoch der Meinung, dass wir mit TypeScript zukünftige Probleme, insbesondere in Bezug auf Regressionen, verhindern könnten. Daher ging es weniger darum, dass wir einen neuen Geschäftswert schaffen, sondern darum, dass wir den bereits erzielten Geschäftswert nicht verlieren.
JavaScript-Unterstützung des TypeScript-Compilers
Nachdem wir die Zustimmung eingeholt und einen Plan entwickelt hatten, um sowohl den Closure- als auch den TypeScript-Compiler auf demselben JavaScript-Code auszuführen, begannen wir mit einigen kleinen Dateien. Unser Ansatz war größtenteils Bottom-Up: Wir haben mit dem Kerncode begonnen und uns dann durch die Architektur bis zu den High-Level-Bereichen gearbeitet.
Wir konnten unsere Arbeit parallelisieren, indem wir jeder einzelnen Datei in DevTools @ts-nocheck
hinzugefügt haben. Um TypeScript zu korrigieren, müssten Sie die Anmerkung @ts-nocheck
entfernen und alle Fehler beheben, die TypeScript findet. So konnten wir sicher sein, dass jede Datei geprüft und so viele Probleme mit dem Dateityp wie möglich behoben wurden.
Im Allgemeinen funktionierte dieser Ansatz mit wenigen Problemen. Wir sind auf mehrere Fehler im TypeScript-Compiler gestoßen, die meisten davon waren jedoch schwer zu finden:
- Ein optionaler Parameter mit einem Funktionstyp, der
any
zurückgibt, wird als erforderlich behandelt: #38551 - Eine Attributzuweisung an eine statische Methode einer Klasse bricht die Deklaration: #38553
- Bei der Deklaration einer Unterklasse mit einem Konstruktor ohne Argumente und einer übergeordneten Klasse mit einem Konstruktor mit Argumenten wird der untergeordnete Konstruktor weggelassen: #41397
Diese Fehler zeigen, dass der TypeScript-Compiler in 99% der Fälle eine solide Grundlage ist. Ja, diese schwer zu fassenden Fehler führten manchmal zu Problemen mit DevTools, aber in den meisten Fällen waren sie so schwer zu fassen, dass wir sie leicht umgehen konnten.
Das einzige Problem, das etwas Verwirrung gestiftet hat, war die nicht deterministische Ausgabe von .tsbuildinfo
-Dateien: #37156.
Bei Chromium ist es erforderlich, dass zwei Builds desselben Chromium-Commits genau dieselbe Ausgabe liefern.
Leider haben unsere Chromium-Entwickler festgestellt, dass die .tsbuildinfo
-Ausgabe nicht deterministisch war: crbug.com/1054494.
Um dieses Problem zu umgehen, mussten wir die Datei .tsbuildinfo
(die im Wesentlichen JSON enthält) per Monkey-Patch bearbeiten und anschließend verarbeiten, um eine deterministische Ausgabe zu erhalten: https://crrev.com/c/2091448
Glücklicherweise konnte das TypeScript-Team das Upstream-Problem beheben und wir konnten unsere Problemumgehung bald entfernen. Vielen Dank an das TypeScript-Team, dass es Fehlerberichte entgegennimmt und diese Probleme zeitnah behebt.
Insgesamt sind wir mit der (Typ-)Richtigkeit des TypeScript-Compilers zufrieden. Wir hoffen, dass Devtools als großes Open-Source-JavaScript-Projekt dazu beigetragen hat, die JavaScript-Unterstützung in TypeScript zu verbessern.
Die Folgen analysieren
Wir konnten gute Fortschritte bei der Behebung dieser Typfehler erzielen und die Menge des von TypeScript geprüften Codes langsam erhöhen. Im August 2020, also neun Monate nach Beginn der Migration, haben wir jedoch festgestellt, dass wir mit unserem aktuellen Tempo den Termin nicht einhalten würden. Einer unserer Entwickler hat ein Analysediagramm erstellt, um den Fortschritt der „TypeScriptifizierung“ (so haben wir diese Migration genannt) zu veranschaulichen.
Fortschritt der TypeScript-Migration – verbleibende Codezeilen, die migriert werden müssen
Die Schätzungen, wann wir die Nulllinie erreichen würden, lagen zwischen Juli 2021 und Dezember 2021, also fast ein Jahr nach unserem Termin. Nach Gesprächen mit der Geschäftsführung und anderen Entwicklern haben wir uns darauf geeinigt, die Anzahl der Entwickler zu erhöhen, die an der Migration zur TypeScript-Compilerunterstützung arbeiten. Das war möglich, weil wir die Migration so konzipiert haben, dass mehrere Entwickler, die an mehreren verschiedenen Dateien arbeiten, nicht in Konflikt miteinander geraten.
An diesem Punkt wurde die Umstellung auf TypeScript zu einer teamweiten Aufgabe. Mit dieser zusätzlichen Unterstützung konnten wir die Migration Ende November 2020 abschließen, also 13 Monate nach Beginn und über ein Jahr früher als in unserer ursprünglichen Schätzung prognostiziert.
Insgesamt wurden 771 Änderungslisten (ähnlich einem Pull-Request) von 18 Entwicklern eingereicht. Unser Tracking-Bug (https://crbug.com/1011811) hat über 1.200 Kommentare, fast alle davon automatisierte Beiträge aus Änderungslisten. Unsere Tracking-Liste enthielt über 500 Zeilen für alle zu TypeScript zu konvertierenden Dateien, ihre zugewiesenen Personen und die Änderungsliste, in der sie „in TypeScript konvertiert“ wurden.
Auswirkungen auf die Leistung des TypeScript-Compilers abmildern
Das größte Problem, mit dem wir uns derzeit befassen, ist die geringe Leistung des TypeScript-Compilers. Angesichts der Anzahl der Entwickler, die Chromium und DevTools entwickeln, ist dieser Engpass kostspielig. Leider konnten wir dieses Risiko vor der Migration nicht erkennen. Erst als wir den Großteil der Dateien zu TypeScript migriert hatten, stellten wir einen deutlichen Anstieg der Zeit fest, die für Chromium-Builds benötigt wurde: https://crbug.com/1139220
Wir haben dieses Problem an das TypeScript-Compilerteam von Microsoft weitergeleitet. Leider wurde es als beabsichtigt eingestuft. Wir hoffen, dass sie diese Entscheidung noch einmal überdenken. In der Zwischenzeit arbeiten wir daran, die Leistungseinbußen auf Chromium-Seite so weit wie möglich zu minimieren.
Leider sind die derzeit verfügbaren Lösungen nicht immer für Mitwirkende geeignet, die nicht zu Google gehören. Da die Open-Source-Beiträge zu Chromium sehr wichtig sind (insbesondere die des Microsoft Edge-Teams), suchen wir aktiv nach Alternativen, die für alle Mitwirkenden funktionieren. Wir haben jedoch derzeit keine geeignete alternative Lösung gefunden.
Aktueller Status von TypeScript in den Entwicklertools
Wir haben den Typprüfer des Closure Compilers aus unserer Codebasis entfernt und verwenden jetzt nur noch den TypeScript-Compiler. Wir können TypeScript-Dateien schreiben und TypeScript-spezifische Funktionen wie Schnittstellen und generische Typen verwenden, was uns im Alltag hilft. Wir sind jetzt zuversichtlicher, dass der TypeScript-Compiler Typfehler und Rückfälle erkennt. Das war auch unser Ziel, als wir mit der Migration begonnen haben. Diese Migration war wie so viele andere langsam, nuanciert und oft herausfordernd. Da wir aber die Vorteile nutzen, glauben wir, dass es sich gelohnt hat.
Vorschaukanäle herunterladen
Verwenden Sie als Standard-Entwicklungsbrowser Chrome Canary, Chrome Dev oder Chrome Beta. Diese Vorabversionen bieten Zugriff auf die neuesten DevTools-Funktionen, ermöglichen den Test moderner Webplattform-APIs und helfen Ihnen, Probleme auf Ihrer Website zu finden, bevor Ihre Nutzer sie bemerken.
Chrome-Entwicklertools-Team kontaktieren
Mit den folgenden Optionen können Sie über neue Funktionen, Updates oder andere Themen im Zusammenhang mit den DevTools sprechen.
- Senden Sie uns Feedback und Funktionsanfragen unter crbug.com.
- Melden Sie ein DevTools-Problem über das Dreipunkt-Menü Weitere Optionen > Hilfe > DevTools-Problem melden.
- Tweeten Sie an @ChromeDevTools.
- Hinterlassen Sie Kommentare unter den YouTube-Videos zu den Neuigkeiten in den DevTools oder den YouTube-Videos mit Tipps zu den DevTools.