Actualización de la arquitectura de Herramientas para desarrolladores: Migración de Herramientas para desarrolladores a TypeScript

Tim van der Lippe
Tim van der Lippe

Esta entrada forma parte de una serie de entradas de blog en las que se describen los cambios que hacemos en la arquitectura de Herramientas para desarrolladores y su compilación.

Para continuar con la migración a los módulos de JavaScript y la migración a los componentes web, hoy continuamos con nuestra serie de entradas de blog sobre los cambios que hacemos en la arquitectura de Devtools y cómo se crea. Si aún no lo viste, publicamos un video sobre nuestro trabajo de Cómo actualizar la arquitectura de Herramientas para desarrolladores a la Web moderna, con 14 sugerencias sobre cómo realizar mejoras en tus proyectos web.

En esta publicación, describiremos nuestro recorrido de 13 meses para pasar del verificador de tipos de Closure Compiler a TypeScript.

Introducción

Debido al tamaño de la base de código de Herramientas para desarrolladores y la necesidad de brindar confianza a los ingenieros que trabajan en ella, es necesario usar un verificador de tipo. Con ese fin, DevTools adoptó el Closure Compiler en 2013. La adopción de Closure permitió a los ingenieros de Herramientas para desarrolladores realizar cambios con confianza. El compilador de Closure realizaba verificaciones de tipo para garantizar que todas las integraciones del sistema estuvieran bien escritas.

Sin embargo, con el paso del tiempo, los verificadores de tipos alternativos se hicieron populares en el desarrollo web moderno. Dos ejemplos notables son TypeScript y Flow. Además, TypeScript se convirtió en un lenguaje de programación oficial de Google. Si bien estos nuevos verificadores de tipos aumentaron en popularidad, también notamos que enviábamos regresiones que deberían haber sido detectadas por un verificador de tipos. Por lo tanto, decidimos reevaluar nuestra elección del verificador de tipos y determinar los próximos pasos para el desarrollo en Herramientas para desarrolladores.

Evalúa los verificadores de tipo

Como Herramientas para desarrolladores ya usaba un corrector de tipo, la pregunta que debemos responder fue la siguiente:

¿Seguimos utilizando Closure Compiler o migramos a un nuevo verificador de tipo?

Para responder a esta pregunta, tuvimos que evaluar los verificadores de tipo en varias características. Dado que nuestro uso de un verificador de tipo se centra en la confianza del ingeniero, el aspecto más importante para nosotros es la corrección de tipo. En otras palabras: ¿Qué tan confiable es un verificador de tipo para descubrir problemas reales?

Nuestra evaluación se centró en las regresiones que habíamos enviado y en determinar cuáles serían las causas raíz. La suposición aquí es que, debido a que ya usábamos el Closure Compiler, Closure no habría detectado estos problemas. Por lo tanto, tendríamos que determinar si cualquier otro verificador de tipos hubiera podido hacerlo.

Corrección de tipo en TypeScript

Como TypeScript era un lenguaje de programación compatible oficialmente con Google y su popularidad aumentó rápidamente, decidimos evaluar TypeScript primero. TypeScript fue una elección interesante, ya que el propio equipo de TypeScript utiliza Herramientas para desarrolladores como uno de sus proyectos de prueba para realizar un seguimiento de su compatibilidad con la comprobación de tipo de JavaScript. El resultado de la prueba de referencia de referencia había demostrado que TypeScript detectaba una gran cantidad de problemas de tipo, que el compilador de Closure no detectaba necesariamente. Es probable que muchos de estos problemas sean la causa raíz de las regresiones que estábamos enviando; esto, a su vez, nos hizo creer que TypeScript podría ser una opción viable para Herramientas para desarrolladores.

Durante nuestra migración a módulos de JavaScript, ya habíamos descubierto que Closure Compiler estaba descubriendo más problemas que antes. El cambio a un formato de módulo estándar aumentó la capacidad de Closure de comprender nuestra base de código y, por lo tanto, aumentó la efectividad de los verificadores de tipo. Sin embargo, el equipo de TypeScript usaba una versión de referencia de Herramientas para desarrolladores anterior a la migración de los módulos de JavaScript. Por lo tanto, tuvimos que descubrir si la migración a módulos de JavaScript también había reducido la cantidad de errores que el compilador de TypeScript detectaría.

Cómo evaluar TypeScript

Herramientas para desarrolladores existe desde hace más de una década, en la que se ha convertido en una aplicación web de tamaño considerable y muchas funciones. Al momento de escribir esta entrada de blog, Herramientas para desarrolladores contiene aproximadamente 150,000 líneas de código JavaScript de origen. Cuando ejecutamos el compilador TypeScript en nuestro código fuente, la cantidad de errores era abrumadora. Pudimos descubrir que, mientras el compilador TypeScript generaba menos errores relacionados con la resolución del código (aproximadamente 2,000 errores), aún había otros 6,000 errores en nuestra base de código relacionados con la compatibilidad de tipos.

Esto demostró que, si bien TypeScript pudo comprender cómo resolver tipos, encontró una cantidad significativa de incompatibilidades de tipos en nuestra base de código. Un análisis manual de estos errores había demostrado que TypeScript era correcto (en la mayoría de los casos). La razón por la que TypeScript pudo detectar estos y Closure no fue porque, a menudo, el compilador de Closure deducía un tipo como Any, mientras que TypeScript realizaba inferencias de tipos en función de asignaciones e infiere un tipo más preciso. Por lo tanto, TypeScript pudo comprender mejor la estructura de nuestros objetos y descubrió usos problemáticos.

Un aspecto importante de esto es que el uso del compilador de Closure en Herramientas para desarrolladores incluyó el uso frecuente de @unrestricted. La anotación de una clase con @unrestricted desactiva efectivamente las verificaciones de propiedades estrictas del compilador de Closure para esa clase específica, lo que significa que un desarrollador puede aumentar una definición de clase a voluntad sin seguridad de tipos. No pudimos encontrar ningún contexto histórico sobre por qué el uso de @unrestricted prevalecía en la base de código de Herramientas para desarrolladores, pero esto generó la ejecución del compilador de Closure en un modo de operación menos seguro para grandes porciones de la base de código.

Un análisis cruzado de nuestras regresiones con los errores de tipo que TypeScript descubrió también mostró una superposición, lo que nos llevó a creer que TypeScript podría haber evitado estos problemas (siempre que los tipos en sí fueran correctos).

Realizando any llamada

En este punto, tuvimos que decidir entre mejorar nuestro uso del Closure Compiler o migrar a TypeScript. (Como Google y Chromium no admitían Flow, tuvimos que renunciar a esa opción). Basándonos en debates y recomendaciones de ingenieros de Google que trabajaban en las herramientas de JavaScript/TypeScript, elegimos el compilador TypeScript. (Recientemente, también publicamos una entrada de blog sobre cómo migrar Puppeteer a TypeScript).

Los motivos principales del compilador de TypeScript fueron la corrección de tipos mejorada, mientras que otras ventajas incluían la asistencia de los equipos de TypeScript internos de Google y las funciones del lenguaje TypeScript, como interfaces (a diferencia de typedefs en JSDoc).

La elección del compilador TypeScript significaba que tuvimos que realizar una inversión significativa en la base de código de Herramientas para desarrolladores y su arquitectura interna. Por lo tanto, estimamos que necesitábamos al menos un año para migrar a TypeScript (previsto para el tercer trimestre de 2020).

Realiza la migración

La mayor pregunta que nos quedó: ¿cómo vamos a migrar a TypeScript? Tenemos 150,000 líneas de código que no podemos migrar de una sola vez. También sabíamos que ejecutar TypeScript en nuestra base de código revelaría miles de errores.

Evaluamos varias opciones:

  1. Obtén todos los errores de TypeScript y compáralos con un resultado “dorado”. Este enfoque es similar al que usa el equipo de TypeScript. La mayor desventaja de este enfoque es la gran cantidad de conflictos de fusión, ya que decenas de ingenieros trabajan en la misma base de código.
  2. Establece todos los tipos problemáticos en any. Básicamente, esto generaría errores de supresión de TypeScript. No elegimos esta opción, ya que nuestro objetivo para la migración era la precisión del tipo, lo que socavaría la supresión.
  3. Corrige todos los errores de TypeScript manualmente. Esto implicaría corregir miles de errores, lo que lleva mucho tiempo.

A pesar del gran esfuerzo previsto, elegimos la opción 3. Hubo otros motivos por los que elegimos esta opción: por ejemplo, nos permitió auditar todo el código y realizar una revisión única por década de todas las funciones, incluida su implementación. Desde una perspectiva comercial, no proporcionamos valor nuevo, sino que mantuvimos el statu quo. Esto hizo que fuera más difícil justificar la opción 3 como la opción correcta.

Sin embargo, al adoptar TypeScript, creemos firmemente que podríamos evitar problemas futuros, en especial los relacionados con las regresiones. Por lo tanto, el argumento era menos "estamos agregando nuevo valor comercial" y más "nos aseguramos de no perder el valor comercial obtenido".

Compatibilidad con JavaScript con el compilador de TypeScript

Después de conseguir la aceptación y desarrollar un plan para ejecutar los compiladores de Closure y TypeScript en el mismo código JavaScript, comenzamos con algunos archivos pequeños. Nuestro enfoque era principalmente de abajo arriba: comenzar con el código principal y avanzar en la arquitectura hasta llegar a los paneles de alto nivel.

Pudimos paralelizar nuestro trabajo agregando de forma preventiva @ts-nocheck a cada archivo en Herramientas para desarrolladores. El proceso de "corregir TypeScript" consistía en quitar la anotación @ts-nocheck y resolver cualquier error que pudiera encontrar TypeScript. Esto significaba que estábamos seguros de que se había verificado cada archivo y de que se había resuelto la mayor cantidad de problemas de tipos posible.

En general, este enfoque funcionaba con pocos problemas. Nos encontramos con varios errores en el compilador de TypeScript, pero la mayoría eran desconocidos:

  1. Un parámetro opcional con un tipo de función que muestra any se trata como obligatorio: #38551
  2. Una asignación de propiedad a un método estático de una declaración de pausas de clase: #38553
  3. La declaración de una subclase con un constructor sin argumentos y una superclase con un constructor de args omite el constructor secundario: #41397

Estos errores resaltan que, para el caso del 99%, el compilador de TypeScript es una base sólida sobre la que basarse. Sí, estos errores desconocidos a veces causaban problemas a Herramientas para desarrolladores, pero la mayoría de las veces fueron lo suficientemente oscuros como para que pudiéramos solucionarlos con facilidad.

El único problema que causó cierta confusión fue el resultado no determinista de los archivos .tsbuildinfo: #37156. En Chromium, exigimos que las dos compilaciones de la misma confirmación de Chromium generen el mismo resultado. Lamentablemente, nuestros ingenieros de compilación de Chromium descubrieron que el resultado de .tsbuildinfo no era determinista: crbug.com/1054494. Para solucionar este problema, tuvimos que aplicar un parche en el archivo .tsbuildinfo (que básicamente contiene JSON) y realizar un procesamiento posterior para mostrar un resultado determinista: https://crrev.com/c/2091448. Por suerte, el equipo de TypeScript resolvió el problema anterior y pronto pudimos quitar nuestra solución alternativa. Agradecemos al equipo de TypeScript por ser receptivo a los informes de errores y por corregir estos problemas con rapidez.

En general, estamos satisfechos con la precisión (tipo) del compilador TypeScript. Esperamos que Devtools, como un gran proyecto de código abierto de JavaScript, haya ayudado a consolidar la compatibilidad con JavaScript en TypeScript.

Analizamos las secuelas

Pudimos realizar un buen progreso en la resolución de estos errores de tipo y aumentar lentamente la cantidad de código que verifica TypeScript. Sin embargo, en agosto de 2020 (9 meses después de la migración), hicimos una revisión y descubrimos que no cumplíamos con la fecha límite con nuestro ritmo actual. Uno de nuestros ingenieros creó un gráfico de análisis para mostrar el progreso de "TypeScriptification" (el nombre que le dimos a esta migración).

Progreso de la migración de TypeScript

Progreso de la migración de TypeScript: Se realiza un seguimiento de las líneas de código que deben migrarse

Las estimaciones en las que no deberíamos alcanzar ninguna línea restantes oscilaron entre julio y diciembre de 2021, casi un año después de nuestra fecha límite. Después de conversar con la gerencia y otros ingenieros, acordamos aumentar la cantidad de ingenieros que trabajaban en la migración para admitir el compilador TypeScript. Esto fue posible, ya que diseñamos la migración para que fuera paralelizable, de modo que varios ingenieros que trabajaban en varios archivos diferentes no entraran en conflicto entre sí.

En este punto, el proceso de TypeScriptification se convirtió en un esfuerzo de todo el equipo. Con la ayuda adicional, pudimos finalizar la migración a fines de noviembre de 2020, 13 meses después del comienzo y más de un año antes de la predicción inicial de nuestra estimación.

En total, 18 ingenieros enviaron 771 listas de cambios (similares a una solicitud de extracción). Nuestro error de seguimiento (https://crbug.com/1011811) tiene más de 1,200 comentarios (casi todos son publicaciones automáticas provenientes de listas de cambios). Nuestra hoja de seguimiento tenía más de 500 filas para todos los archivos a escribir, su destinatario y la lista de cambios en la que estaban "Typescriptificados".

Cómo mitigar el impacto del rendimiento del compilador de TypeScript

El mayor problema con el que nos enfrentamos actualmente es el rendimiento lento del compilador TypeScript. Dada la cantidad de ingenieros que compilan Chromium y Herramientas para desarrolladores, este cuello de botella es costoso. Por desgracia, no pudimos identificar este riesgo antes de la migración. Solo cuando migramos la mayoría de los archivos a TypeScript, notamos un notable aumento en el tiempo dedicado a las compilaciones de Chromium: https://crbug.com/1139220

Informamos este problema directamente al equipo de compiladores de Microsoft TypeScript, pero, lamentablemente, se determinó que este comportamiento es intencional. Esperamos que reconsideren este problema, pero, mientras tanto, estamos trabajando para mitigar el impacto de la lentitud en el rendimiento de Chromium tanto como sea posible.

Lamentablemente, las soluciones que tenemos actualmente disponibles no siempre son adecuadas para quienes no son Googlers. Dado que las contribuciones de código abierto a Chromium son muy importantes (especialmente las del equipo de Microsoft Edge), buscamos activamente alternativas que funcionen para todos los colaboradores. Sin embargo, en este momento no hemos determinado una solución alternativa adecuada.

Estado actual de TypeScript en Herramientas para desarrolladores

Por el momento, quitamos el verificador de tipos de compiladores de Closure de nuestra base de código y solo nos basamos en el compilador de TypeScript. Podemos escribir archivos creados por TypeScript y usar las funciones específicas de TypeScript (como interfaces, elementos genéricos, etc.), lo que nos ayuda a diario. Tenemos una mayor confianza de que el compilador de TypeScript detectará errores de tipo y regresiones, que es lo que esperamos que sucediera cuando empezamos a trabajar en esta migración. Esta migración, como muchas, fue lenta, tiene matices y, a menudo, es desafiante, pero a medida que obtenemos los beneficios, creemos que valió la pena.

Descarga los canales de vista previa

Considera usar Canary, Dev o Beta de Chrome como tu navegador de desarrollo predeterminado. Estos canales de vista previa te brindan acceso a las funciones más recientes de Herramientas para desarrolladores, prueba APIs de plataformas web de vanguardia y encuentra problemas en tu sitio antes que tus usuarios.

Cómo comunicarse con el equipo de Herramientas para desarrolladores de Chrome

Usa las siguientes opciones para analizar las nuevas funciones y los cambios en la publicación, o cualquier otra cosa relacionada con Herramientas para desarrolladores.

  • Envíanos tus sugerencias o comentarios a través de crbug.com.
  • Informa un problema en Herramientas para desarrolladores mediante Más opciones   Más   > Ayuda > Informar problemas con Herramientas para desarrolladores en Herramientas para desarrolladores.
  • Envía un tweet a @ChromeDevTools.
  • Deja comentarios en los videos de YouTube de las Novedades de las Herramientas para desarrolladores o en las sugerencias de Herramientas para desarrolladores (videos de YouTube).