Esta publicación forma parte de una serie de publicaciones de blog en las que se describen los cambios que estamos realizando en la arquitectura de DevTools y cómo se compila.
A raíz de nuestra migración a módulos de JavaScript y migración a componentes web, hoy continuamos con nuestra serie de entradas de blog sobre los cambios que estamos realizando en la arquitectura de Devtools y cómo se compila. (Si aún no lo has visto, publicamos un video sobre nuestro trabajo para actualizar la arquitectura de DevTools a la Web moderna, con 14 sugerencias para mejorar 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
Dado el tamaño de la base de código de DevTools y la necesidad de brindar confianza a los ingenieros que trabajan en ella, es necesario usar un verificador de tipos. Para ello, Herramientas para desarrolladores adoptó Closure Compiler en 2013. La adopción de Closure permitió que los ingenieros de DevTools realizaran cambios con confianza. El compilador de Closure realizaría 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 popularizaron en el desarrollo web moderno. Dos ejemplos notables son TypeScript y Flow. Además, TypeScript se convirtió en un lenguaje de programación oficial en Google. Si bien estos nuevos verificadores de tipos aumentaron en popularidad, también notamos que estábamos enviando regresiones que un verificador de tipos debería haber detectado. Por lo tanto, decidimos volver a evaluar nuestra elección de verificador de tipos y determinar los próximos pasos para el desarrollo en DevTools.
Cómo evaluar verificadores de tipos
Dado que DevTools ya usaba un verificador de tipos, la pregunta que debíamos responder era la siguiente:
¿Seguimos usando Closure Compiler o migramos a un nuevo verificador de tipos?
Para responder esta pregunta, tuvimos que evaluar los verificadores de tipos en varias características. Dado que el uso de un verificador de tipos se enfoca en la confianza de los ingenieros, el aspecto más importante para nosotros es la corrección del tipo. En otras palabras: ¿Qué tan confiable es un verificador de tipos para descubrir problemas reales?
Nuestra evaluación se enfocó en las regresiones que enviamos y en determinar cuáles serían las causas raíz. La suposición aquí es que, como ya usábamos el compilador de Closure, Closure no habría detectado estos problemas. Por lo tanto, tendríamos que determinar si algún otro verificador de tipos podría hacerlo.
Corrección de tipos en TypeScript
Dado que TypeScript era un lenguaje de programación compatible oficialmente en Google y su popularidad aumentaba rápidamente, decidimos evaluarlo primero. TypeScript fue una opción interesante, ya que el propio equipo de TypeScript usa DevTools como uno de sus proyectos de prueba para hacer un seguimiento de su compatibilidad con la comprobación de tipos de JavaScript. El resultado de la prueba de referencia del modelo de referencia mostró 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, lo que, a su vez, nos hizo creer que TypeScript podría ser una opción viable para DevTools.
Durante nuestra migración a los módulos de JavaScript, ya descubrimos 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 para comprender nuestra base de código y, por lo tanto, aumentó la eficacia de los verificadores de tipos. Sin embargo, el equipo de TypeScript usaba una versión de referencia de DevTools anterior a la migración de módulos de JavaScript. Por lo tanto, tuvimos que averiguar si la migración a los módulos de JavaScript también había reducido la cantidad de errores que detectaría el compilador de TypeScript.
Cómo evaluar TypeScript
DevTools existe desde hace más de una década, en la que se convirtió en una aplicación web de gran tamaño y con muchas funciones. En el momento de escribir esta entrada de blog, DevTools contiene aproximadamente 150,000 líneas de código JavaScript propio. Cuando ejecutamos el compilador de TypeScript en nuestro código fuente, la cantidad de errores fue abrumadora. Pudimos determinar que, si bien el compilador de TypeScript emitía menos errores relacionados con la resolución de código (~2,000 errores), aún había otros 6,000 errores presentes en nuestra base de código relacionados con la compatibilidad de tipos.
Esto demostró que, si bien TypeScript pudo comprender cómo resolver los tipos, encontró una cantidad significativa de incompatibilidades de tipos en nuestra base de código.
Un análisis manual de estos errores mostró que TypeScript era (la mayoría de las veces) correcto.
El motivo por el que TypeScript pudo detectarlos y Closure no fue porque, a menudo, el compilador de Closure deducía que un tipo era un Any
, mientras que TypeScript realizaba una inferencia de tipo basada en asignaciones y deducía un tipo más preciso.
Por lo tanto, TypeScript fue mejor para comprender la estructura de nuestros objetos y descubrió usos problemáticos.
Una observación importante al respecto es que el uso del compilador Closure en DevTools incluía el uso frecuente de @unrestricted
.
Anotatar una clase con @unrestricted
desactiva de manera eficaz las verificaciones de propiedades estrictas del compilador de Closure para esa clase específica, lo que significa que un desarrollador puede aumentar la definición de una clase a voluntad sin seguridad de tipos.
No pudimos encontrar ningún contexto histórico sobre por qué el uso de @unrestricted
era predominante en la base de código de DevTools, pero esto provocó que se ejecutara el 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 descubrió TypeScript también mostró una superposición, lo que nos llevó a creer que TypeScript podría haber evitado estos problemas (siempre que los tipos fueran correctos).
Cómo hacer una llamada a any
En este punto, tuvimos que decidir entre mejorar el uso de Closure Compiler o migrar a TypeScript. (Dado que Flow no era compatible con Google ni con Chromium, tuvimos que renunciar a esa opción). En función de las conversaciones y recomendaciones de los ingenieros de Google que trabajan en las herramientas de JavaScript/TypeScript, elegimos el compilador de TypeScript. (Hace poco, también publicamos una entrada de blog sobre la migración de Puppeteer a TypeScript).
Los principales motivos para usar el compilador de TypeScript fueron la corrección de tipos mejorada, mientras que otras ventajas incluían la asistencia de los equipos de TypeScript de forma interna en Google y las funciones del lenguaje TypeScript, como interfaces
(en contraposición a typedefs
en JSDoc).
Elegir el compilador de TypeScript significó que tuvimos que invertir significativamente en la base de código de DevTools y su arquitectura interna. Por lo tanto, estimamos que necesitábamos al menos un año para migrar a TypeScript (con el objetivo de hacerlo en el tercer trimestre de 2020).
Realiza la migración
La pregunta más importante que quedaba era: ¿cómo migraremos a TypeScript? Tenemos 150,000 líneas de código y no podemos migrarlas 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:
- Obtén todos los errores de TypeScript y compáralos con un resultado "estándar". Este enfoque sería similar al que tiene el equipo de TypeScript. La mayor desventaja de este enfoque es la alta frecuencia de conflictos de combinación, ya que decenas de ingenieros trabajan en la misma base de código.
- Establece todos los tipos problemáticos en
any
. Esto haría que TypeScript suprima los errores. No elegimos esta opción, ya que nuestro objetivo para la migración era la corrección de tipos, lo que la supresión socavaría. - Corregir todos los errores de TypeScript de forma manual Esto implicaría corregir miles de errores, lo que lleva tiempo.
A pesar del gran esfuerzo esperado, 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 hacer una revisión de todas las funciones, incluida su implementación, una vez cada diez años. Desde una perspectiva empresarial, no estábamos proporcionando un valor nuevo, sino que manteníamos el statu quo. Esto hizo que fuera más difícil justificar la opción 3 como la opción correcta.
Sin embargo, cuando adoptamos TypeScript, creíamos firmemente que podíamos evitar problemas futuros, en especial en relación con las regresiones. Por lo tanto, el argumento era menos "estamos agregando un nuevo valor comercial" y más "nos aseguramos de no perder el valor comercial obtenido".
Compatibilidad con JavaScript del compilador de TypeScript
Después de asegurarnos de que todos estuvieran de acuerdo y de desarrollar un plan para ejecutar el compilador de Closure y TypeScript en el mismo código JavaScript, comenzamos con algunos archivos pequeños. Nuestro enfoque fue principalmente ascendente: comenzamos con el código principal y avanzamos en la arquitectura hasta llegar a los paneles de alto nivel.
Pudimos paralelizar nuestro trabajo agregando @ts-nocheck
de forma preventiva a cada archivo de DevTools. El proceso para “corregir TypeScript” sería quitar la anotación @ts-nocheck
y resolver cualquier error que TypeScript encuentre. Esto significaba que teníamos la seguridad de que se había verificado cada archivo y que se habían resuelto tantos problemas de tipo como fuera posible.
En general, este enfoque funcionó con pocos problemas. Encontramos varios errores en el compilador de TypeScript, pero la mayoría eran poco claros:
- Un parámetro opcional con un tipo de función que muestra
any
se considera obligatorio: #38551 - Una asignación de propiedad a un método estático de una clase interrumpe la declaración: #38553
- La declaración de una subclase con un constructor sin argumentos y una superclase con un constructor de argumentos omite el constructor secundario: #41397
Estos errores destacan que, en el 99% de los casos, el compilador de TypeScript es una base sólida sobre la que se puede compilar. Sí, estos errores ocultos a veces causaban problemas en DevTools, pero la mayoría de las veces eran lo suficientemente ocultos como para que pudiéramos solucionarlos fácilmente.
El único problema que causó cierta confusión fue el resultado no determinista de los archivos .tsbuildinfo
: #37156.
En Chromium, exigimos que cualquier compilación de la misma confirmación de Chromium genere exactamente 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 al archivo .tsbuildinfo
(que, en esencia, contiene JSON) y procesarlo posteriormente para que devuelva un resultado determinista: https://crrev.com/c/2091448 Afortunadamente, el equipo de TypeScript resolvió el problema upstream y pronto pudimos quitar nuestra solución alternativa. Gracias al equipo de TypeScript por recibir los informes de errores y solucionarlos rápidamente.
En general, estamos conformes con la corrección (tipo) del compilador de TypeScript. Esperamos que Devtools, como un gran proyecto de JavaScript de código abierto, haya ayudado a consolidar la compatibilidad con JavaScript en TypeScript.
Analiza las consecuencias
Pudimos avanzar mucho en la resolución de estos errores de tipo y aumentar lentamente la cantidad de código que TypeScript verifica. Sin embargo, en agosto de 2020 (9 meses después de iniciar esta migración), hicimos un control y descubrimos que no cumplirí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 la "TypeScriptificación" (el nombre que le dimos a esta migración).
Progreso de la migración de TypeScript: Seguimiento de las líneas de código restantes que deben migrarse
Las estimaciones de cuándo llegaríamos a cero líneas restantes oscilaban entre julio de 2021 y diciembre de 2021, casi un año después de la fecha límite. Después de conversar con la administración y otros ingenieros, acordamos aumentar la cantidad de ingenieros que trabajan en la migración a la compatibilidad con el compilador de TypeScript. Esto fue posible porque diseñamos la migración para que sea paralelizable, de modo que varios ingenieros que trabajan en varios archivos diferentes no entren 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 terminar nuestra migración a fines de noviembre de 2020, 13 meses después de comenzar y más de un año antes de lo que preveía nuestra estimación inicial.
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 ellos publicaciones automatizadas de listas de cambios). Nuestra hoja de seguimiento tenía más de 500 filas para todos los archivos que se iban a convertir a TypeScript, su asignado y en qué lista de cambios se habían convertido.
Mitiga el impacto del rendimiento del compilador de TypeScript
El mayor problema que tenemos actualmente es el rendimiento lento del compilador de TypeScript. Dado la cantidad de ingenieros que compilan Chromium y DevTools, este cuello de botella es costoso. Lamentablemente, no pudimos identificar este riesgo antes de la migración y solo cuando migramos la mayoría de los archivos a TypeScript descubrimos un aumento notable en el tiempo dedicado a las compilaciones de Chromium: https://crbug.com/1139220
Informamos este problema al equipo del compilador de TypeScript de Microsoft, pero, lamentablemente, determinaron que este comportamiento es intencional. Esperamos que reconsideren este problema, pero, mientras tanto, estamos trabajando para mitigar el impacto del rendimiento lento en Chromium tanto como sea posible.
Lamentablemente, las soluciones que tenemos disponibles en la actualidad no siempre son adecuadas para los colaboradores que no son de Google. Dado que las contribuciones de código abierto a Chromium son muy importantes (en especial, las del equipo de Microsoft Edge), buscamos activamente alternativas que funcionen para todos los colaboradores. Sin embargo, en este momento, no hemos encontrado una solución alternativa adecuada.
Estado actual de TypeScript en DevTools
En este momento, quitamos el verificador de tipos del compilador Closure de nuestra base de código y solo dependemos del compilador de TypeScript. Podemos escribir archivos creados por TypeScript y usar funciones específicas de TypeScript (como interfaces, genéricos, etc.), lo que nos ayuda a diario. Tenemos más confianza en que el compilador de TypeScript detectará errores de tipo y regresiones, que es lo que esperábamos que sucediera cuando comenzamos a trabajar en esta migración. Esta migración, como muchas otras, fue lenta, detallada y, a menudo, desafiante, pero a medida que vemos los beneficios, creemos que valió la pena.
Descarga los canales de vista previa
Considera usar Chrome Canary, Dev o Beta como tu navegador de desarrollo predeterminado. Estos canales de vista previa te brindan acceso a las funciones más recientes de DevTools, te permiten probar las APIs de plataformas web de vanguardia y te ayudan a encontrar problemas en tu sitio antes de que lo hagan tus usuarios.
Comunícate con el equipo de Herramientas para desarrolladores de Chrome
Usa las siguientes opciones para hablar sobre las funciones nuevas, las actualizaciones o cualquier otro tema relacionado con DevTools.
- Envíanos tus comentarios y solicitudes de funciones a crbug.com.
- Informa un problema de DevTools con Más opciones > Ayuda > Informar un problema de DevTools en DevTools.
- Twittea a @ChromeDevTools.
- Deja comentarios en los videos de YouTube sobre las novedades de DevTools o en los videos de YouTube sobre sugerencias de DevTools.