Dieser Post ist Teil einer Reihe von Blogposts, in denen die Änderungen beschrieben werden, die wir an der Architektur der Entwicklertools vornehmen.
Nach der Migration zu JavaScript-Modulen und der Migration zu Webkomponenten setzen wir heute unsere Blogpost-Reihe über Änderungen an der Architektur und deren Aufbau fort. Falls Sie es noch nicht gesehen haben, haben wir ein Video über unsere Arbeit Upgrade der DevTools-Architektur auf das moderne Web veröffentlicht. Es enthält 14 Tipps, wie Sie Ihre Webprojekte verbessern können.
In diesem Post beschreiben wir unseren 13-monatigen Weg vom Closure Compiler Type Checker zu TypeScript.
Einleitung
Angesichts der Größe der Entwicklertools-Codebasis und der Notwendigkeit, den Entwicklern, die daran arbeiten, Sicherheit zu bieten, ist die Verwendung eines Typprüfungsgeräts erforderlich. Aus diesem Grund haben die Entwicklertools 2013 den Closure Compiler eingeführt. Durch die Einführung von Closure konnten die Entwickler der Entwicklertools souverän Änderungen vornehmen. Der Closure-Compiler führte Typprüfungen durch, um sicherzustellen, dass alle Systemintegrationen korrekt geschrieben waren.
Mit der Zeit wurden jedoch alternative Typprüfungen in der modernen Webentwicklung immer beliebter. Zwei nennenswerte Beispiele sind TypeScript und Flow. Darüber hinaus wurde TypeScript eine offizielle Programmiersprache bei Google. Während diese neuen Typprüfungen an Beliebtheit gewonnen haben, haben wir auch festgestellt, dass es sich bei den Versandkosten um Regressionen handelt, die von einer Typprüfung hätte erkannt werden müssen. Daher haben wir uns entschlossen, unsere Auswahl des Typprüfungsgeräts noch einmal zu überprüfen und die nächsten Schritte für die Entwicklung mit den Entwicklertools zu ermitteln.
Typprüfungen auswerten
Da die Entwicklertools bereits einen Typprüfer verwendeten, lautete unsere Frage:
Verwenden wir weiterhin Closure Compiler oder migrieren wir zu einem neuen Typprüfer?
Um diese Frage zu beantworten, mussten wir die Typprüfer auf verschiedene Merkmale hin bewerten. Da bei der Verwendung einer Typprüfung das Vertrauen der Entwickler im Mittelpunkt steht, ist der wichtigste Aspekt für uns die Korrektheit der Schrift. Mit anderen Worten: Wie zuverlässig ist eine Typprüfung beim Auffinden echter Probleme?
Unsere Bewertung konzentrierte sich auf die von uns entwickelten Regressionen und die Ermittlung der Ursachen dafür. Da wir den Closure Compiler bereits verwendet haben, gehen wir davon aus, dass Closure diese Probleme nicht erfasst hätte. Daher müssten wir feststellen, ob eine andere Typprüfung möglich gewesen wäre.
Korrektheit der Eingabe in TypeScript
Da TypeScript bei Google eine offiziell unterstützte Programmiersprache war und immer beliebter wird, haben wir uns entschieden, TypeScript zuerst zu evaluieren. TypeScript war eine interessante Wahl, da das TypeScript-Team die Entwicklertools als eines seiner Testprojekte nutzt, um die Kompatibilität mit der JavaScript-Typprüfung zu prüfen. Die Testausgabe der Referenzreferenz hatte gezeigt, dass TypeScript viele Typprobleme erkannte – Probleme, die der Compiler Closure nicht unbedingt erkannt hatte. Viele dieser Probleme waren wahrscheinlich die Ursache für die Regressionen, die wir eingeführt haben. Dies wiederum brachte uns wiederum den Eindruck, dass TypeScript eine praktikable Option für die Entwicklertools sein könnte.
Bei unserer Migration zu JavaScript-Modulen haben wir bereits festgestellt, dass Closure Compiler mehr Probleme aufdeckt als zuvor. Durch die Umstellung auf ein Standardmodulformat gelang es Closure, unsere Codebasis besser zu verstehen und somit auch die Effektivität der Typprüfungen zu erhöhen. Das TypeScript-Team verwendete jedoch eine Basisversion der Entwicklertools, die vor der Migration der JavaScript-Module war. Daher mussten wir herausfinden, ob durch die Migration zu JavaScript-Modulen auch die Anzahl der Fehler reduziert wurde, die der TypeScript-Compiler erkennen würde.
TypeScript bewerten
DevTools gibt es seit über zehn Jahren, in denen sie sich zu einer beachtlichen Webanwendung mit vielen Funktionen entwickelt hat. Zum Zeitpunkt der Erstellung dieses Blogposts enthielten die Entwicklertools ca. 150.000 Zeilen eigenen JavaScript-Code. Als wir den TypeScript-Compiler in unserem Quellcode ausführten, war die schiere Anzahl der Fehler überwältigend. Wir konnten feststellen, dass der TypeScript-Compiler zwar weniger Fehler im Zusammenhang mit der Codeauflösung ausgab (etwa 2.000 Fehler), in unserer Codebasis aber noch weitere 6.000 Fehler in Bezug auf die Typkompatibilität vorhanden waren.
Dies zeigte, dass TypeScript zwar verstehen konnte, wie Typen aufgelöst werden konnten, jedoch eine erhebliche Anzahl von Typeninkompatibilitäten in unserer Codebasis erkannt wurden.
Eine manuelle Analyse dieser Fehler hat gezeigt, dass TypeScript (meist) korrekt war.
Der Grund, aus dem TypeScript diese erkennen konnte und Closure nicht, lag daran, dass der Closure-Compiler häufig einen Typ als Any
ableiten konnte, während TypeScript Typinferenzen basierend auf Zuweisungen durchführt und einen genaueren Typ ableiten konnte.
Dadurch konnte TypeScript die Struktur unserer Objekte viel besser verstehen und fand problematische Verwendungen.
Ein wichtiger Haken hierbei ist, dass bei der Verwendung des Compilers Closure in den Entwicklertools häufig @unrestricted
verwendet wurde.
Durch das Annotieren einer Klasse mit @unrestricted
werden die strengen Attributprüfungen des Closure-Compilers für diese bestimmte Klasse deaktiviert. Das bedeutet, dass ein Entwickler eine Klassendefinition bei Bedarf ohne Typsicherheit erweitern kann.
Wir konnten keinen historischen Kontext dazu finden, warum die Verwendung von @unrestricted
in der Codebasis der Entwicklertools vorherrschend war. Dies hatte jedoch dazu geführt, 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 gefundenen Typfehlern ergab ebenfalls eine Überschneidung. Daher glaubten wir, dass TypeScript diese Probleme hätte verhindern können, sofern die Typen selbst korrekt waren.
any
-Anruf wird getätigt
An diesem Punkt mussten wir uns entscheiden, ob wir die Nutzung unseres Closure Compilers verbessern oder zu TypeScript migrieren wollten. Da Flow weder von Google noch in Chromium unterstützt wurde, mussten wir auf diese Option verzichten. Auf Grundlage von Diskussionen 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, während andere Vorteile unter anderem der Support von TypeScript-Teams intern bei Google und die Funktionen der TypeScript-Sprache wie interfaces
(im Gegensatz zu typedefs
in JSDoc) waren.
Da wir uns für den TypeScript-Compiler entschieden haben, mussten wir beträchtlich in die DevTools-Codebasis und deren interne Architektur investieren. Daher gingen wir davon aus, dass wir mindestens ein Jahr für die Migration zu TypeScript benötigen (Ziel für das 3. Quartal 2020).
Migration durchführen
Die wichtigste Frage: Wie soll die Migration zu TypeScript erfolgen? Wir haben 150.000 Codezeilen, die wir nicht auf einmal migrieren können. Wir wussten auch, dass das Ausführen von TypeScript auf unserer Codebasis Tausende von Fehlern aufdecken würde.
Wir haben mehrere Optionen geprüft:
- Rufen Sie alle TypeScript-Fehler ab und vergleichen Sie sie mit einer goldenen Ausgabe. Dieser Ansatz ähnelt dem vom TypeScript-Team. Der größte Nachteil dieses Ansatzes ist das häufige Auftreten von Zusammenführungskonflikten, da Dutzende von Entwicklern mit derselben Codebasis arbeiten.
- Legen Sie alle problematischen Typen auf
any
fest. Dadurch würde TypeScript im Grunde genommen dazu führen, dass Fehler unterdrückt werden. Wir haben diese Option nicht ausgewählt, da unser Ziel der Migration darin bestand, dass die Art korrekt ist, was die Unterdrückung untergraben würde. - Beheben Sie alle TypeScript-Fehler manuell. Dies umfasst die Behebung Tausender Fehler, was zeitaufwendig ist.
Trotz des erwarteten Aufwands haben wir uns für Option 3 entschieden. Es gab weitere Gründe, warum wir uns für diese Option entschieden haben: So konnten wir beispielsweise den gesamten Code und alle Funktionen, einschließlich ihrer Implementierung, einmal in einem Jahrzehnt überprüfen. Aus geschäftlicher Sicht haben wir keinen neuen Mehrwert geschaffen, sondern den Status quo beibehalten. Dadurch ist es schwieriger geworden, Option 3 als richtige Wahl zu begründen.
Durch den Einsatz von TypeScript waren wir jedoch der festen Überzeugung, dass wir zukünftige Probleme vermeiden können, insbesondere in Bezug auf Regressionen. Daher lautete das Argument weniger: „Wir schaffen neuen Geschäftswert“, sondern vielmehr: „Wir stellen sicher, dass wir nicht gewonnenen Geschäftswert verlieren“.
JavaScript-Unterstützung des TypeScript-Compilers
Nachdem wir uns die Unterstützung gesichert und einen Plan entwickelt hatten, um den Compiler Closure und TypeScript auf demselben JavaScript-Code auszuführen, begannen wir mit einigen kleinen Dateien. Unser Ansatz war größtenteils Bottom-up: Beginnen Sie mit dem Kerncode und gehen Sie in der Architektur nach oben, bis wir zu den allgemeinen Panels gelangen.
Wir konnten unsere Arbeit parallelisieren, indem wir präventiv @ts-nocheck
jeder einzelnen Datei in den Entwicklertools hinzugefügt haben. Der Prozess der "Korrektur von TypeScript" würde darin bestehen, die Anmerkung @ts-nocheck
zu entfernen und alle Fehler zu beheben, die TypeScript finden würde. Das bedeutet, dass wir uns sicher waren, dass jede Datei überprüft wurde und so viele Arten von Problemen wie möglich behoben wurden.
Im Allgemeinen funktionierte dieser Ansatz mit wenigen Problemen. Im TypeScript-Compiler sind mehrere Fehler aufgetreten, von denen die meisten jedoch unklar waren:
- Ein optionaler Parameter mit einem Funktionstyp, der
any
zurückgibt, wird wie erforderlich behandelt: #38551. - Durch die Zuweisung einer Eigenschaft zu einer statischen Methode einer Klasse wird die Deklaration aufgehoben: #38553.
- Bei der Deklaration einer Unterklasse mit einem no-args-Konstruktor und einer Superklasse mit einem args-Konstruktor wird der untergeordnete Konstruktor weggelassen: #41397
Diese Fehler zeigen, dass der TypeScript-Compiler für 99 %-Fall eine solide Grundlage darstellt. Ja, diese unklaren Fehler verursachten manchmal Probleme in den Entwicklertools, aber meistens waren sie so unklar, dass wir sie problemlos umgehen konnten.
Das einzige Problem, das für Verwirrung sorgte, war die nicht deterministische Ausgabe von .tsbuildinfo
-Dateien: #37156.
Bei Chromium ist es erforderlich, dass zwei Builds desselben Chromium-Commit zu exakt derselben Ausgabe führen.
Leider haben unsere Chromium-Entwickler festgestellt, dass die .tsbuildinfo
-Ausgabe nicht deterministisch war: crbug.com/1054494.
Um dieses Problem zu umgehen, mussten wir die .tsbuildinfo
-Datei (die im Wesentlichen JSON enthält) aktualisieren und nachbearbeiten, um eine deterministische Ausgabe zurückzugeben: https://crrev.com/c/2091448. Glücklicherweise hat das TypeScript-Team das Upstream-Problem behoben und wir konnten unsere Problemumgehung bald entfernen. Vielen Dank an das TypeScript-Team für Ihre Annahmen zu Fehlerberichten und die schnelle Behebung dieser Probleme.
Wir sind insgesamt mit der Richtigkeit (Typ) des TypeScript-Compilers zufrieden. Wir hoffen, dass die Entwicklertools als großes Open-Source-JavaScript-Projekt dazu beigetragen haben, die JavaScript-Unterstützung in TypeScript zu festigen.
Folgen analysieren
Wir haben gute Fortschritte beim Beheben dieser Fehlertypen gemacht und die Menge des von TypeScript geprüften Codes langsam erhöht. Im August 2020 (9 Monate nach der Migration) stellten wir jedoch fest, dass wir mit unserem derzeitigen Tempo unsere Frist nicht einhalten konnten. Einer unserer Entwickler hat ein Analysediagramm erstellt, das den Fortschritt von „TypeScriptification“ (dem Namen, den wir dieser Migration gegeben haben) zeigt.
Fortschritt der TypeScript-Migration – Tracking von verbleibenden Codezeilen, die migriert werden müssen
Die Schätzungen für einen Zeitraum von Juli 2021 bis Dezember 2021, fast ein Jahr nach dem Stichtag, reichten aus, wenn der Schwellenwert auf null gesetzt wurde. Nach Gesprächen mit dem Management und anderen Entwicklern haben wir uns darauf geeinigt, die Anzahl der Entwickler zu erhöhen, die an der Migration zur TypeScript-Compiler-Unterstützung arbeiten. Das war möglich, als wir die Migration so konzipiert haben, dass sie parallelisierbar ist, sodass mehrere Entwickler, die an mehreren verschiedenen Dateien arbeiten, nicht miteinander in Konflikt stehen.
Der TypeScriptification-Prozess entwickelte sich zu einem teamweiten Prozess. Mit der zusätzlichen Hilfe konnten wir unsere Migration Ende November 2020 abschließen, 13 Monate nach Beginn und über ein Jahr vor unserer ursprünglichen Schätzung.
Insgesamt wurden 771 Änderungslisten (ähnlich einer Pull-Anfrage) von 18 Entwicklern eingereicht. Unser Tracking-Fehler (https://crbug.com/1011811) hat über 1.200 Kommentare (beinahe alle automatisierten Beiträge von Änderungslisten). Unser Tracking-Sheet hatte über 500 Zeilen für alle zu typisierenden Dateien, für die zuständige Person und in der Änderungsliste, in der sie typisiert wurden.
Reduzieren der Auswirkungen der TypeScript-Compiler-Leistung
Das größte Problem, mit dem wir es derzeit zu tun haben, ist die langsame Leistung des TypeScript-Compilers. Angesichts der Anzahl von Entwicklern, die Chromium und die Entwicklertools entwickeln, ist dieser Engpass kostspielig. Leider konnten wir dieses Risiko vor der Migration nicht identifizieren. Erst bei der Migration der meisten Dateien zu TypeScript stellten wir fest, dass der Zeitaufwand für Chromium-Builds deutlich zugenommen hat: https://crbug.com/1139220
Wir haben dem Microsoft TypeScript-Compiler-Team dieses Problem gemeldet. Leider hat das Team festgestellt, dass dieses Verhalten beabsichtigt war. Wir hoffen, dass wir das Problem überprüfen werden, aber wir arbeiten bereits daran, die langsamen Leistungseinbußen auf Chromium-Seite so weit wie möglich zu minimieren.
Leider sind die Lösungen, die uns heute zur Verfügung stehen, nicht immer für Beitragende geeignet, die nicht zu Google gehören. Da die Open-Source-Beiträge zu Chromium sehr wichtig sind (insbesondere die vom Microsoft Edge-Team), suchen wir aktiv nach Alternativen, die für alle Nutzer geeignet sind. Allerdings haben wir derzeit noch keine geeignete alternative Lösung gefunden.
Aktueller Status von TypeScript in den Entwicklertools
Derzeit haben wir den Compiler-Typprüfer Closure aus unserer Codebasis entfernt und verlassen uns ausschließlich auf den TypeScript-Compiler. Wir können mit TypeScript erstellte Dateien schreiben und uns mit TypeScript-spezifischen Funktionen (z. B. Schnittstellen, Generika usw.) beschäftigen, was uns täglich hilft. Wir sind jetzt sicherer, dass der TypeScript-Compiler Typfehler und Regressionen erkennt. Dies ging in unserer Hoffnung genauso vor, dass es zu Beginn der ersten Arbeit an der Migration der Fall wäre. Diese Migration war – wie so viele – langsam, differenziert und oft eine Herausforderung. Wir sind jedoch überzeugt, dass es sich gelohnt hat.
Vorschaukanäle herunterladen
Sie können Chrome Canary, Dev oder Beta als Standardbrowser für die Entwicklung verwenden. Über diese Vorschaukanäle erhältst du Zugriff auf die neuesten Entwicklertools-Funktionen, kannst neue Webplattform-APIs testen und Probleme auf deiner Website erkennen, bevor deine Nutzer es tun.
Chrome-Entwicklertools-Team kontaktieren
Verwende die folgenden Optionen, um die neuen Funktionen und Änderungen im Beitrag oder andere Themen im Zusammenhang mit den Entwicklertools zu besprechen.
- Sende uns über crbug.com Vorschläge oder Feedback.
- Wenn du ein Problem mit den Entwicklertools melden möchtest, klicke in den Entwicklertools auf Weitere Optionen > Hilfe > Probleme mit den Entwicklertools melden.
- Senden Sie einen Tweet an @ChromeDevTools.
- Hinterlasse Kommentare zu den Neuheiten in den Entwicklertools YouTube-Videos oder YouTube-Videos in den Entwicklertools-Tipps.