Обновление архитектуры DevTools: миграция DevTools на TypeScript

Тим ван дер Липпе
Tim van der Lippe

Этот пост является частью серии постов в блоге, описывающих изменения, которые мы вносим в архитектуру DevTools и то, как она построена.

Продолжая переход на модули JavaScript и переход на веб-компоненты , сегодня мы продолжаем серию публикаций в блоге об изменениях, которые мы вносим в архитектуру Devtools и о том, как она построена . (Если вы его еще не видели, мы опубликовали видео о нашей работе по обновлению архитектуры DevTools до современной сети с 14 советами о том, как улучшить ваши веб-проекты.)

В этом посте мы опишем наш 13-месячный путь перехода от проверки типов Closure Compiler к TypeScript .

Введение

Учитывая размер кодовой базы DevTools и необходимость обеспечить уверенность инженерам, работающим над ней, использование средства проверки типов является необходимостью . С этой целью DevTools внедрил компилятор Closure еще в 2013 году. Внедрение Closure позволило инженерам DevTools с уверенностью вносить изменения; Компилятор Closure будет выполнять проверки типов, чтобы гарантировать, что все системные интеграции правильно типизированы.

Однако со временем в современной веб-разработке стали популярны альтернативные средства проверки типов. Два ярких примера — TypeScript и Flow . Более того, TypeScript стал официальным языком программирования Google. Хотя популярность этих новых средств проверки типов росла, мы также заметили, что реализуем регрессии, которые должны были быть обнаружены средствами проверки типов. Поэтому мы решили пересмотреть свой выбор средства проверки типов и определить следующие шаги разработки с использованием DevTools.

Оценка средств проверки типов

Поскольку DevTools уже использовала средство проверки типов, нам нужно было ответить на следующий вопрос:

Продолжим ли мы использовать Closure Compiler или перейдем на новую программу проверки типов?

Чтобы ответить на этот вопрос, нам пришлось оценить типовые проверщики по нескольким характеристикам. Поскольку использование средства проверки типов направлено на уверенность инженеров, наиболее важным аспектом для нас является правильность типов. Другими словами: насколько надежна программа проверки типов при обнаружении реальных проблем?

Наша оценка была сосредоточена на реализованных нами регрессиях и определении их первопричин. Предполагается, что, поскольку мы уже использовали компилятор Closure, Closure не обнаружил бы этих проблем. Таким образом, нам нужно было бы определить, сможет ли это сделать какой-либо другой модуль проверки типов.

Корректность ввода в TypeScript

Поскольку TypeScript был официально поддерживаемым языком программирования в Google и его популярность быстро росла, мы решили сначала оценить TypeScript. TypeScript оказался интересным выбором, поскольку сама команда TypeScript использует DevTools в качестве одного из своих тестовых проектов для отслеживания их совместимости с включенной проверкой типов JavaScript. Результаты базового эталонного теста показали, что TypeScript обнаруживает большое количество проблем с типами — проблем, которые компилятор Closure не обязательно обнаруживает. Многие из этих проблем, вероятно, были основной причиной регрессий, которые мы реализовывали; это, в свою очередь, заставило нас поверить, что TypeScript может стать жизнеспособным вариантом для DevTools.

Во время перехода на модули JavaScript мы уже обнаружили, что Closure Compiler обнаруживает больше проблем, чем раньше. Переход к стандартному формату модулей увеличил способность Closure понимать нашу кодовую базу и, следовательно, повысил эффективность средств проверки типов. Однако команда TypeScript использовала базовую версию DevTools, предшествовавшую миграции модулей JavaScript. Поэтому нам нужно было выяснить, уменьшил ли переход на модули JavaScript количество ошибок, которые ловит компилятор TypeScript.

Оценка TypeScript

DevTools существует уже более десяти лет и за это время превратился в многофункциональное веб-приложение значительного размера. На момент написания этой статьи в блоге DevTools содержал около 150 000 строк собственного кода JavaScript. Когда мы запустили компилятор TypeScript для нашего исходного кода, количество ошибок было ошеломляющим. Нам удалось выяснить, что хотя компилятор TypeScript выдавал меньше ошибок, связанных с разрешением кода (около 2000 ошибок), в нашей кодовой базе все еще присутствовало еще 6000 ошибок, связанных с совместимостью типов.

Это показало, что хотя TypeScript и смог понять, как разрешать типы, он обнаружил значительное количество несовместимостей типов в нашей кодовой базе. Ручной анализ этих ошибок показал, что TypeScript (в большинстве случаев) был корректен. Причина, по которой TypeScript смог их обнаружить, а Closure — нет, заключалась в том, что часто компилятор Closure определял тип как Any , тогда как TypeScript выполнял вывод типа на основе присвоений и вывел более точный тип. Таким образом, TypeScript действительно лучше понимал структуру наших объектов и обнаруживал проблемные варианты использования .

Одним из важных нюансов является то, что использование компилятора Closure в DevTools включало частое использование @unrestricted . Аннотирование класса с помощью @unrestricted эффективно отключает строгие проверки свойств компилятора Closure для этого конкретного класса, а это означает, что разработчик может дополнять определение класса по своему желанию без безопасности типов. Нам не удалось найти никакого исторического контекста, объясняющего, почему использование @unrestricted было распространено в кодовой базе DevTools, но это привело к запуску компилятора Closure в менее безопасном режиме работы для больших частей кодовой базы.

Перекрестный анализ наших регрессий с ошибками типов, обнаруженными TypeScript, также показал совпадение, что заставило нас поверить, что TypeScript мог бы предотвратить эти проблемы (при условии, что сами типы были правильными).

Совершение any звонка

На этом этапе нам пришлось выбирать между улучшением использования компилятора Closure или переходом на TypeScript. (Поскольку Flow не поддерживался ни в Google, ни в Chromium, нам пришлось отказаться от этой опции.) Основываясь на обсуждениях и рекомендациях инженеров Google, работающих над инструментами JavaScript/TypeScript, мы решили выбрать компилятор TypeScript. (Недавно мы также опубликовали сообщение в блоге о переходе Puppeteer на TypeScript .)

Основными причинами использования компилятора TypeScript были улучшенная корректность типов, в то время как другие преимущества включали поддержку со стороны команд TypeScript внутри Google и особенности языка TypeScript, такие как interfaces (в отличие от typedefs в JSDoc).

Выбор компилятора TypeScript означал, что нам пришлось значительно инвестировать в кодовую базу DevTools и ее внутреннюю архитектуру. Таким образом, мы подсчитали, что нам понадобится как минимум один год для перехода на TypeScript (планируемый срок — третий квартал 2020 года).

Выполнение миграции

Самый большой вопрос, который остался: как мы собираемся перейти на TypeScript? У нас 150 000 строк кода, и мы не можем перенести их за один раз. Мы также знали, что запуск TypeScript в нашей кодовой базе обнаружит тысячи ошибок.

Мы оценили несколько вариантов:

  1. Получите все ошибки TypeScript и сравните их с «золотым» результатом . Этот подход будет похож на тот, который использует команда TypeScript. Самый большой недостаток этого подхода — частое возникновение конфликтов слияния, поскольку над одной базой кода работают десятки инженеров.
  2. Установите для всех проблемных типов any . По сути, это заставит TypeScript подавлять ошибки. Мы не выбрали этот вариант, поскольку нашей целью при миграции была корректность типов, подавление которой могло бы подорвать ее.
  3. Исправьте все ошибки TypeScript вручную. Это потребует исправления тысяч ошибок, а это отнимает много времени.

Несмотря на большие ожидаемые усилия, мы выбрали вариант 3. Были и дополнительные причины, по которым мы выбрали этот вариант: например, он позволял нам проверять весь код и раз в десять лет проверять всю функциональность, включая ее реализацию. . С точки зрения бизнеса мы не создавали новую ценность, а скорее поддерживали статус-кво. Это затруднило обоснование варианта 3 как правильного выбора.

Однако, приняв TypeScript, мы твердо верили, что сможем предотвратить будущие проблемы, особенно связанные с регрессиями. Таким образом, аргумент был не столько «мы добавляем новую ценность бизнеса», сколько «мы гарантируем, что не потеряем полученную ценность бизнеса».

Поддержка JavaScript компилятором TypeScript

Получив поддержку и разработав план запуска компилятора Closure и TypeScript с одним и тем же кодом JavaScript , мы начали с нескольких небольших файлов. Наш подход был в основном восходящим: начните с основного кода и продвигайтесь вверх по архитектуре, пока не дойдете до панелей высокого уровня.

Мы смогли распараллелить нашу работу, заранее добавив @ts-nocheck в каждый файл в DevTools. Процесс «исправления TypeScript» будет заключаться в удалении аннотации @ts-nocheck и устранении любых ошибок, которые обнаружит TypeScript. Это означало, что мы были уверены, что каждый файл был проверен и что как можно больше проблем с типами было решено.

В целом этот подход работал с небольшими проблемами. Мы столкнулись с несколькими ошибками в компиляторе TypeScript, но большинство из них были неясными:

  1. Необязательный параметр с типом функции, который возвращает any , рассматривается как обязательный: #38551
  2. Назначение свойства статическому методу объявления разрыва класса: #38553
  3. В объявлении подкласса с конструктором без аргументов и суперкласса с конструктором с аргументами дочерний конструктор опускается: #41397

Эти ошибки подчеркивают, что в 99% случаев компилятор TypeScript является прочной основой для дальнейшего развития. Да, эти малоизвестные ошибки иногда вызывали проблемы в DevTools, но в большинстве случаев они были достаточно неясными, чтобы мы могли легко их обойти.

Единственной проблемой, которая вызвала некоторую путаницу, был недетерминированный вывод файлов .tsbuildinfo : #37156 . В Chromium мы требуем, чтобы любые две сборки одного и того же коммита Chromium приводили к одному и тому же результату. К сожалению, наши инженеры по сборке Chromium обнаружили, что вывод .tsbuildinfo недетерминирован: crbug.com/1054494 . Чтобы обойти эту проблему, нам пришлось внести исправления в файл .tsbuildinfo (который по существу содержит JSON) и выполнить его постобработку, чтобы вернуть детерминированный результат: https://crrev.com/c/2091448 . К счастью, команда TypeScript устранила проблему. вышестоящей проблемы, и вскоре мы смогли удалить обходной путь. Спасибо команде TypeScript за то, что они восприимчивы к сообщениям об ошибках и оперативно устраняют эти проблемы!

В целом мы довольны корректностью (типирования) компилятора TypeScript. Мы надеемся, что Devtools как крупный проект JavaScript с открытым исходным кодом помог укрепить поддержку JavaScript в TypeScript.

Анализ последствий

Нам удалось добиться значительного прогресса в устранении этих ошибок типов и постепенно увеличивать объем кода, проверяемого TypeScript. Однако в августе 2020 года (9 месяцев после начала миграции) мы провели проверку и обнаружили, что не уложимся в сроки при нынешних темпах. Один из наших инженеров построил график анализа, чтобы показать ход «TypeScriptification» (название, которое мы дали этой миграции).

Ход миграции TypeScript

Ход миграции TypeScript — отслеживание оставшихся строк кода, которые необходимо перенести.

По оценкам, когда мы достигнем оставшейся нулевой отметки, варьировались от июля 2021 года до декабря 2021 года, то есть почти на год позже установленного срока. После обсуждений с руководством и другими инженерами мы согласились увеличить количество инженеров, работающих над переходом на поддержку компилятора TypeScript. Это стало возможным, поскольку мы разработали возможность распараллеливания миграции, чтобы несколько инженеров, работающих над несколькими разными файлами, не конфликтовали друг с другом.

На этом этапе процесс TypeScriptification стал коллективной работой. Благодаря дополнительной помощи мы смогли завершить миграцию в конце ноября 2020 года, через 13 месяцев после начала и более чем на год раньше, чем предполагалось по первоначальной оценке.

Всего 18 инженеров представили 771 список изменений (похожий на запрос на включение) . Наша ошибка отслеживания ( https://crbug.com/1011811 ) имеет более 1200 комментариев (почти все из них — автоматические публикации из списков изменений). В нашем листе отслеживания было более 500 строк для всех файлов, подлежащих машинописному написанию, их правопреемников и списка изменений, в которых они были «типизированы».

Уменьшение влияния производительности компилятора TypeScript

Самая большая проблема, с которой мы сегодня сталкиваемся, — это низкая производительность компилятора TypeScript. Учитывая количество инженеров, создающих Chromium и DevTools, это узкое место обходится дорого. К сожалению, мы не смогли выявить этот риск до начала миграции, и только в тот момент, когда мы перенесли большинство файлов на TypeScript, мы обнаружили заметное увеличение времени, затрачиваемого на сборки Chromium: https://crbug .com/1139220

Мы сообщили об этой проблеме команде компиляторов Microsoft TypeScript, но, к сожалению, они определили такое поведение как преднамеренное. Мы надеемся, что они пересмотрят эту проблему, но тем временем мы работаем над тем, чтобы максимально смягчить влияние низкой производительности на стороне Chromium.

К сожалению, решения, доступные нам сегодня, не всегда подходят для участников, не являющихся сотрудниками Google. Поскольку вклад открытого исходного кода в Chromium очень важен (особенно вклад команды Microsoft Edge), мы активно ищем альтернативы, которые будут работать для всех участников. Однако на данный момент мы не нашли подходящего альтернативного решения.

Текущее состояние TypeScript в DevTools

На данный момент мы удалили средство проверки типов компилятора Closure из нашей кодовой базы и полагаемся исключительно на компилятор TypeScript. Мы можем писать файлы, созданные на основе TypeScript, и использовать специфичные для TypeScript функции (такие как интерфейсы, дженерики и т. д.), которые помогают нам каждый день. У нас возросла уверенность в том, что компилятор TypeScript обнаружит ошибки типов и регрессии, на что мы и надеялись, когда впервые начали работу над этой миграцией. Эта миграция, как и многие другие, была медленной, сложной и зачастую сложной, но, учитывая преимущества, мы считаем, что оно того стоило.

Загрузите предварительный просмотр каналов

Рассмотрите возможность использования Chrome Canary , Dev или Beta в качестве браузера для разработки по умолчанию. Эти каналы предварительного просмотра дают вам доступ к новейшим функциям DevTools, тестируют передовые API-интерфейсы веб-платформы и находят проблемы на вашем сайте раньше, чем это сделают ваши пользователи!

Связь с командой Chrome DevTools

Используйте следующие параметры, чтобы обсудить новые функции и изменения в публикации или что-либо еще, связанное с DevTools.