Aggiornamento dell'architettura DevTools: migrazione di DevTools a TypeScript

Tim van der Lippe
Tim van der Lippe

Questo post fa parte di una serie di post del blog che descrivono le modifiche che stiamo apportando all'architettura di DevTools e il modo in cui viene creata.

Facendo seguito alla migrazione ai moduli JavaScript e alla migrazione ai componenti web, oggi continuiamo la nostra serie di post del blog sulle modifiche che stiamo apportando all'architettura di DevTools e sulla sua creazione. Se non l'hai ancora fatto, abbiamo pubblicato un video sul nostro lavoro di upgrade dell'architettura di DevTools al web moderno, con 14 suggerimenti su come migliorare i tuoi progetti web.

In questo post, descriveremo il nostro percorso di 13 mesi dal controllo dei tipi del compilatore di chiusura a TypeScript.

Introduzione

Data la dimensione del codebase DevTools e la necessità di fornire sicurezza agli ingegneri che lo lavorano, è necessario utilizzare un controllo dei tipi. A questo scopo, DevTools ha adottato il Closure Compiler nel 2013. L'adozione di Closure ha permesso agli ingegneri DevTools di apportare modifiche in tutta sicurezza; il compilatore Closure avrebbe eseguito controlli dei tipi per garantire che tutte le integrazioni di sistema fossero digitate correttamente.

Tuttavia, con il passare del tempo, gli strumenti di controllo dei caratteri alternativi sono diventati popolari nello sviluppo web moderno. Due esempi degni di nota sono TypeScript e Flow. Inoltre, TypeScript è diventato un linguaggio di programmazione ufficiale di Google. Anche se questi nuovi strumenti di controllo del tipo sono aumentati di popolarità, abbiamo anche notato che generavamo regressioni che avrebbero dovuto essere rilevate da un tipo di controllo. Abbiamo quindi deciso di rivalutare la nostra scelta di tipo di controllo e definire i passaggi successivi per lo sviluppo su DevTools.

Valutazione dei controlli dei tipi

Poiché DevTools utilizzava già un controllo del tipo, la domanda a cui abbiamo risposto è stata:

Continuiamo a utilizzare Closure Compiler o ne eseguiremo la migrazione a un nuovo tipo di controllo?

Per rispondere a questa domanda, abbiamo dovuto valutare diverse caratteristiche dei controlli del tipo. Poiché l'uso della verifica del tipo è incentrato sulla fiducia degli ingegneri, l'aspetto più importante per noi è la correttezza del testo. In altre parole: quanto è affidabile un controllo dei tipi nel scoprire problemi reali?

La nostra valutazione si è concentrata sulle regressioni che avevamo proposto e sul determinare quali sarebbero state le loro cause principali. Dato che stavamo usando già il compilatore Closure, il presupposto è che Closure non avrebbe rilevato questi problemi. Pertanto, dobbiamo determinare se sarebbe stato possibile qualsiasi altro tipo di controllo.

Correttezza del testo in TypeScript

Poiché TypeScript era un linguaggio di programmazione supportato ufficialmente da Google e stava diventando sempre più popolare, abbiamo deciso di prendere in considerazione TypeScript prima. TypeScript è stata una scelta interessante, in quanto il team di TypeScript utilizza DevTools come uno dei suoi progetti di test per monitorare la compatibilità con il controllo dei tipi di JavaScript attivato. L'output del test di riferimento di riferimento aveva mostrato che TypeScript stava rilevando una grande quantità di problemi di tipo, che non erano necessariamente rilevati dal compilatore Closure. Molti di questi problemi erano probabilmente la causa principale delle regressioni che stavamo cercando; questo, a sua volta, ci ha fatto credere che TypeScript potesse essere un'opzione valida per DevTools.

Durante la migrazione ai moduli JavaScript, avevamo già scoperto che il compilatore Closure stava scoprendo più problemi rispetto a prima. Il passaggio a un formato di moduli standard ha aumentato la capacità di Closure di comprendere il nostro codebase e, di conseguenza, ha aumentato l'efficacia delle verifiche dei tipi. Tuttavia, il team di TypeScript utilizzava una versione di base di DevTools precedente alla migrazione dei moduli JavaScript. Pertanto, abbiamo dovuto capire se la migrazione ai moduli JavaScript aveva ridotto anche la quantità di errori rilevati dal compilatore TypeScript.

Valutazione di TypeScript

DevTools esiste da oltre un decennio, in cui è cresciuto fino a diventare un'applicazione web ricca di funzionalità e di dimensioni considerevoli. Al momento della stesura di questo post del blog, DevTools contiene circa 150.000 righe di codice JavaScript proprietario. Quando abbiamo eseguito il compilatore TypeScript sul nostro codice sorgente, l'enorme volume di errori era impressionante. Siamo stati in grado di capire che mentre il compilatore TypeScript emetteva meno errori relativi alla risoluzione del codice (circa 2.000 errori), c'erano ancora altri 6.000 errori presenti nel nostro codebase relativi alla compatibilità dei tipi.

Questo ha dimostrato che TypeScript era in grado di capire come risolvere i tipi, ma ha rilevato una quantità significativa di incompatibilità dei tipi nel nostro codebase. Un'analisi manuale di questi errori ha mostrato che TypeScript era (il più delle volte) corretto. Il motivo per cui TypeScript è stato in grado di rilevarli e Closure non lo è stato perché spesso il compilatore Closure deduce che un tipo è un Any, mentre TypeScript eseguiva l'inferenza del tipo in base alle assegnazioni e deduceva un tipo più preciso. Per questo motivo, TypeScript è riuscito a comprendere meglio la struttura dei nostri oggetti e ha scoperto utilizzi problematici.

Un aspetto importante è che l'utilizzo del compilatore Closure in DevTools ha incluso l'uso frequente di @unrestricted. L'annotazione di una classe con @unrestricted disattiva in modo efficace i rigorosi controlli delle proprietà del compilatore Closure per quella classe specifica, il che significa che uno sviluppatore può aumentare la definizione di una classe a proprio piacimento senza la sicurezza del tipo. Non è stato possibile trovare alcun contesto storico che spiegasse il motivo per cui l'utilizzo di @unrestricted era prevalente nel codebase DevTools, ma ha comportato l'esecuzione del compilatore Closure in una modalità operativa meno sicura per grandi parti del codebase.

Un'analisi incrociata delle nostre regressioni con gli errori di tipo rilevati da TypeScript ha mostrato anche una sovrapposizione, che ci ha portato a credere che TypeScript avrebbe potuto evitare questi problemi (a condizione che i tipi stessi fossero corretti).

Chiamata con any in corso...

A questo punto, abbiamo dovuto decidere se migliorare l'utilizzo del compilatore Closure o eseguire la migrazione a TypeScript. Poiché Flow non era supportato né in Google né in Chromium, abbiamo dovuto rinunciare a questa opzione. In base alle discussioni e ai consigli dei tecnici di Google che lavorano agli strumenti JavaScript/TypeScript, abbiamo scelto di scegliere il compilatore TypeScript. (Recentemente abbiamo anche pubblicato un post del blog sulla migrazione di Puppeteer a TypeScript).

I motivi principali alla base del compilatore TypeScript sono stati la maggiore correttezza del tipo, mentre altri vantaggi includevano il supporto dei team TypeScript interni di Google e le funzionalità del linguaggio TypeScript, ad esempio interfaces (invece di typedefs in JSDoc).

Scegliere il compilatore TypeScript ci ha costretto a investire in modo significativo nel codebase DevTools e nella sua architettura interna. Di conseguenza, abbiamo stimato che fosse necessario almeno un anno per la migrazione a TypeScript (l'obiettivo è il terzo trimestre del 2020).

Esecuzione della migrazione

La domanda più importante che è rimasta è: come facciamo a migrare a TypeScript? Abbiamo 150.000 righe di codice e non possiamo eseguirne la migrazione in una volta. Sapevamo anche che l'esecuzione di TypeScript sul nostro codebase avrebbe scoperto migliaia di errori.

Abbiamo valutato più opzioni:

  1. Ottieni tutti gli errori di TypeScript e confrontali con un output "dorato". Questo approccio sarebbe simile a quello del team di TypeScript. Il più grande svantaggio di questo approccio è l'elevato numero di conflitti di unione, poiché decine di ingegneri lavorano sullo stesso codebase.
  2. Imposta tutti i tipi di problemi su any. In questo modo TypeScript può eliminare gli errori. Non abbiamo scelto questa opzione, poiché il nostro obiettivo per la migrazione era la correttezza dei tipi, che la soppressione avrebbe minato.
  3. Correggi manualmente tutti gli errori di TypeScript. Ciò comporta la correzione di migliaia di errori, un'attività dispendiosa in termini di tempo.

Nonostante il grande impegno previsto, abbiamo optato per l'opzione 3. C'erano altri motivi per cui abbiamo scelto questa opzione: ad esempio, ci ha consentito di controllare tutto il codice ed eseguire una revisione una volta nel decennio di tutte le funzionalità, compresa la sua implementazione. Da un punto di vista aziendale, non abbiamo fornito nuovo valore, ma abbiamo mantenuto lo status quo. Di conseguenza, era più difficile giustificare l'opzione 3 come scelta corretta.

Tuttavia, adottando TypeScript, ritenevamo fermamente di poter prevenire problemi futuri, in particolare quelli relativi alle regressioni. Di conseguenza, l'argomento era meno"Stiamo aggiungendo nuovo valore aziendale " e più "ci stiamo assicurando di non perdere il valore aziendale ottenuto".

Supporto JavaScript del compilatore TypeScript

Dopo aver assicurato l'approvazione e sviluppato un piano per eseguire sia il compilatore Closure che quello TypeScript sullo stesso codice JavaScript, abbiamo iniziato con alcuni piccoli file. Il nostro approccio era per lo più dal basso verso l'alto: iniziamo con il codice di base per proseguire nell'architettura fino a raggiungere i riquadri di alto livello.

Siamo riusciti a caricare in contemporanea il nostro lavoro aggiungendo preventivamente @ts-nocheck a ogni singolo file in DevTools. Il processo di "correzione di TypeScript" prevede la rimozione dell'annotazione @ts-nocheck e la risoluzione di eventuali errori rilevati da TypeScript. In questo modo eravamo sicuri che ogni file fosse stato controllato e che il maggior numero possibile di problemi sia stato risolto.

In generale, questo approccio funzionava con pochi problemi. Ci siamo imbattuti in diversi bug nel compilatore TypeScript, ma la maggior parte erano oscuri:

  1. Un parametro facoltativo con un tipo di funzione che restituisce any viene considerato obbligatorio: #38551
  2. Un'assegnazione di proprietà a un metodo statico di una dichiarazione delle interruzioni di classe: #38553
  3. La dichiarazione di una sottoclasse con un costruttore no-args e una superclasse con un costruttore args omette il costruttore figlio: #41397

Questi bug evidenziano che, per il caso del 99%, il compilatore TypeScript è una solida base su cui sviluppare. Sì, questi bug ambigui a volte causavano problemi a DevTools, ma il più delle volte erano abbastanza oscuri da consentirci di evitarli facilmente.

L'unico problema che aveva creato confusione era l'output non deterministico dei file .tsbuildinfo: #37156. In Chromium è necessario che due build dello stesso commit di Chromium generino esattamente lo stesso output. Sfortunatamente, i nostri tecnici della build di Chromium hanno scoperto che l'output .tsbuildinfo non era deterministico: crbug.com/1054494. Per ovviare al problema, abbiamo dovuto eseguire una "monkey-patch" del file .tsbuildinfo (che contiene essenzialmente JSON) e post-elaborarlo per restituire un output deterministico: https://crrev.com/c/2091448 Fortunatamente, il team di TypeScript ha risolto il problema upstream e siamo stati presto in grado di rimuovere la nostra soluzione alternativa. Grazie al team di TypeScript per la sua pazienza nei confronti delle segnalazioni di bug e per la risoluzione tempestiva di questi problemi.

Nel complesso, siamo soddisfatti della correttezza (tipo) del compilatore TypeScript. Speriamo che Devtools, in quanto grande progetto JavaScript open source, abbia contribuito a consolidare il supporto JavaScript in TypeScript.

Analisi delle conseguenze

Siamo riusciti a fare ottimi progressi nella risoluzione di questi tipi di errori e ad aumentare lentamente la quantità di codice controllato da TypeScript. Tuttavia, nell'agosto 2020 (9 mesi dall'inizio della migrazione) abbiamo effettuato un controllo e abbiamo scoperto che non avremmo raggiunto la scadenza con il nostro ritmo attuale. Uno dei nostri ingegneri ha creato un grafico di analisi per mostrare l'avanzamento di "TypeScriptification" (il nome che abbiamo dato a questa migrazione).

Avanzamento della migrazione di TypeScript

Avanzamento della migrazione TypeScript - Righe di codice rimanenti che richiedono la migrazione

Le stime in cui avremmo raggiunto zero linee rimanenti andavano da luglio 2021 a dicembre 2021, quasi un anno dopo la scadenza. Dopo aver parlato con la dirigenza e con altri ingegneri, abbiamo acconsentito ad aumentare il numero di ingegneri impegnati nella migrazione all'assistenza per la compilazione di TypeScript. Questo è stato possibile poiché abbiamo progettato la migrazione in modo che fosse caricabile in contemporanea, in modo che più tecnici che lavorassero su più file diversi non fossero in conflitto tra loro.

A quel punto, il processo di TypeScriptification è diventato un lavoro che coinvolge tutto il team. Con l'aiuto aggiuntivo, siamo riusciti a terminare la nostra migrazione alla fine di novembre 2020, 13 mesi dopo l'inizio e oltre un anno prima della nostra stima iniziale.

In totale, sono stati inviati 771 elenchi di modifiche (simili a una richiesta di pull) da 18 tecnici. Il nostro bug di monitoraggio (https://crbug.com/1011811) ha oltre 1200 commenti (quasi tutti sono post automatici dagli elenchi di modifiche). Il nostro foglio di monitoraggio conteneva più di 500 righe per tutti i file da digitare, i relativi assegnatari e l'elenco di modifiche in cui erano "Typescriptified".

Mitigare l'impatto sulle prestazioni del compilatore TypeScript

Il problema più grande che abbiamo attualmente a che fare sono le prestazioni lente del compilatore TypeScript. Dato il numero di ingegneri che creano Chromium e DevTools, questo collo di bottiglia è costoso. Sfortunatamente, non siamo riusciti a identificare questo rischio prima della migrazione ed è stato solo al momento in cui abbiamo eseguito la migrazione della maggior parte dei file a TypeScript che abbiamo rilevato un notevole aumento del tempo trascorso nelle build di Chromium: https://crbug.com/1139220

Abbiamo segnalato questo problema a monte al team di compilazione di Microsoft TypeScript, ma purtroppo hanno stabilito che questo comportamento è intenzionale. Ci auguriamo che il problema venga preso nuovamente in considerazione, ma nel frattempo stiamo lavorando per ridurre il più possibile l'impatto della lentezza delle prestazioni sul lato Chromium.

Sfortunatamente, le soluzioni oggi a nostra disposizione non sono sempre adatte a collaboratori non Google. I contributi open source a Chromium sono molto importanti (in particolare quelli del team di Microsoft Edge), pertanto stiamo cercando attivamente alternative che funzionino per tutti i collaboratori. Tuttavia, al momento non abbiamo trovato una soluzione alternativa adatta.

Stato attuale di TypeScript in DevTools

Al momento abbiamo rimosso il controllo del tipo di compilatore Closure dal nostro codebase e ci basiamo esclusivamente sul compilatore TypeScript. Siamo in grado di scrivere file creati da TypeScript e di utilizzare funzioni specifiche di TypeScript (ad esempio interfacce, generiche ecc.), che ci aiutano quotidianamente. Abbiamo una maggiore sicurezza che il compilatore TypeScript rilevi gli errori di tipo e le regressioni, cosa che speravamo che sarebbe accaduto quando abbiamo iniziato a lavorare su questa migrazione. Questa migrazione, come tante, è stata lenta, articolata e spesso impegnativa, ma man mano che forniamo i benefici, riteniamo che ne sia valsa la pena.

Scarica i canali in anteprima

Prendi in considerazione l'utilizzo di Chrome Canary, Dev o beta come browser di sviluppo predefinito. Questi canali in anteprima ti consentono di accedere alle funzionalità di DevTools più recenti, di testare le API per piattaforme web all'avanguardia e di individuare eventuali problemi sul tuo sito prima che lo facciano gli utenti.

Contattare il team di Chrome DevTools

Utilizza le opzioni seguenti per discutere delle nuove funzionalità e delle modifiche nel post o di qualsiasi altra cosa relativa a DevTools.

  • Inviaci un suggerimento o un feedback tramite crbug.com.
  • Segnala un problema DevTools utilizzando Altre opzioni   Altre   > Guida > Segnala i problemi di DevTools in DevTools.
  • Tweet all'indirizzo @ChromeDevTools.
  • Lascia commenti sui video di YouTube o sui suggerimenti di DevTools in DevTools Video di YouTube.