Мы в команде инструментов разработчика очень любим TypeScript. Настолько, что пишем на нем новый код и сейчас переводим на него всю базу кода инструментов разработчика. Подробнее об этом переходе можно узнать из нашего выступления на Chrome Dev Summit 2020. Поэтому мы решили перевести базу кода Puppeteer на TypeScript.
Планирование переноса
При планировании миграции мы хотели иметь возможность продвигаться небольшими шагами. Это позволяет снизить затраты на перенос, поскольку вы работаете только с небольшой частью кода, а также уменьшить риски. Если что-то пойдет не так, вы сможете легко отменить изменения. У Puppeteer много пользователей, и выпуск неработающей версии мог бы вызвать проблемы у многих из них, поэтому было важно свести к минимуму риск внесения критических изменений.
Нам также повезло, что у Puppeteer есть надежный набор модульных тестов, охватывающих все его функциональные возможности. Это позволило нам быть уверенными в том, что мы не нарушим работу кода при переносе и не внесем изменения в API. Цель заключалась в том, чтобы пользователи Puppeteer даже не заметили перехода. Тесты были важной частью этой стратегии. Если бы у нас не было хорошего покрытия тестами, мы бы добавили его, прежде чем продолжить перенос.
Вносить изменения в код без тестирования рискованно, но особенно опасно, когда вы работаете с целыми файлами или всей базой кода. При внесении механических изменений легко пропустить какой-нибудь шаг, и в нескольких случаях тесты выявили проблему, которую не заметили ни разработчик, ни проверяющий.
Мы потратили время на настройку непрерывной интеграции (CI). Мы заметили, что непрерывная интеграция в pull-запросах работала нестабильно и часто завершалась сбоем. Это происходило так часто, что мы привыкли игнорировать непрерывную интеграцию и все равно объединять pull-запросы, предполагая, что сбой был разовой проблемой непрерывной интеграции, а не проблемой в Puppeteer.
После того как мы провели общее обслуживание и уделили время устранению неполадок, тесты стали проходить гораздо чаще, и мы смогли доверять CI, зная, что сбой указывает на реальную проблему. Это не самая интересная работа, и наблюдать за бесконечными запусками непрерывной интеграции может быть утомительно, но надежная работа набора тестов была крайне важна, учитывая количество pull-запросов, которые поступали в ходе переноса.
Как выбрать и загрузить один файл
К этому моменту мы подготовили все для переноса и создали надежный CI-сервер с множеством тестов. Вместо того чтобы погружаться в произвольный файл, мы намеренно выбрали небольшой файл для переноса. Это полезное упражнение, поскольку оно позволяет проверить запланированный процесс, который вы собираетесь выполнить. Если это сработает для этого файла, ваш подход верен; если нет, вы можете вернуться к чертежной доске.
Кроме того, пофайловый подход (и регулярные выпуски Puppeteer, благодаря которым не все изменения поставлялись в одной и той же версии npm) снизил риск. Мы выбрали файл DeviceDescriptors.js в качестве первого, поскольку он был одним из самых простых в кодовой базе. Может показаться, что все эти подготовительные действия не стоят такого незначительного изменения, но цель состоит не в том, чтобы сразу внести большие изменения, а в том, чтобы действовать осторожно и методично, файл за файлом. Время, потраченное на проверку подхода, определенно сэкономит время позже при переносе, когда вы столкнетесь с более сложными файлами.
Докажите закономерность и повторите действия
К счастью, изменение DeviceDescriptors.js было успешно добавлено в кодовую базу, и план сработал так, как мы и надеялись! В этот момент вы готовы засучить рукава и приступить к делу, что мы и сделали. Использование ярлыка GitHub – это отличный способ сгруппировать все pull-запросы. Мы обнаружили, что это полезно для отслеживания прогресса.
Перенести и улучшить позже
Для каждого отдельного файла JavaScript мы выполняли следующие действия:
- Переименуйте файл
.jsв.ts. - Запустите компилятор TypeScript.
- Устраните проблемы.
- Создайте pull-запрос.
В большинстве первых pull-запросов требовалось извлечь интерфейсы TypeScript для существующих структур данных. В случае с первым pull-запросом, который перенес DeviceDescriptors.js, о котором мы говорили ранее, код изменился следующим образом:
module.exports = [
{
name: 'Pixel 4',
… // Other fields omitted to save space
},
…
]
И стал:
interface Device {
name: string,
…
}
const devices: Device[] = [{name: 'Pixel 4', …}, …]
module.exports = devices;
В рамках этого процесса мы проверили каждую строку базы кода на наличие проблем. Как и в любой базе кода, которая существует несколько лет и постоянно развивается, есть возможности для рефакторинга кода и улучшения ситуации. В частности, при переходе на TypeScript мы обнаружили, что небольшая реструктуризация кода позволит нам больше полагаться на компилятор и повысить безопасность типов.
Как ни странно, очень важно не вносить изменения сразу. Цель переноса – перевести базу кода на TypeScript. Во время этого процесса всегда нужно помнить о риске поломки программного обеспечения и неудобств для пользователей. Поскольку мы внесли минимальные изменения, риск был низким. После объединения файла и его переноса в TypeScript мы смогли внести последующие изменения, чтобы улучшить код и использовать систему типов. Установите строгие границы для переноса и постарайтесь не выходить за них.
Перенос тестов для проверки определений типов
После того как мы перенесли весь исходный код в TypeScript, мы смогли сосредоточиться на тестировании. Наши тесты были очень подробными, но все они были написаны на JavaScript. Это означало, что они не тестировали наши определения типов. Одна из долгосрочных целей проекта (над которой мы все ещё работаем) – поставлять высококачественные определения типов вместе с Puppeteer, но в нашем коде не было никаких проверок определений типов.
Перенеся тесты в TypeScript (по тому же процессу, файл за файлом), мы обнаружили проблемы с TypeScript, которые в противном случае пришлось бы искать пользователям. Теперь наши тесты не только охватывают все функции, но и проверяют качество кода TypeScript.
Мы уже получили огромную пользу от TypeScript как разработчики, работающие над базой кода Puppeteer. В сочетании с улучшенной средой непрерывной интеграции это позволило нам повысить продуктивность при работе над Puppeteer и выявлять с помощью TypeScript ошибки, которые в противном случае попали бы в выпуск npm. Мы рады, что теперь у нас есть высококачественные определения TypeScript, которые могут использовать все разработчики, работающие с Puppeteer.
Как скачать предварительные версии
Рекомендуем использовать в качестве браузера для разработки Chrome Canary, Dev или Beta. Эти каналы предварительной версии дают вам доступ к новейшим функциям DevTools, позволяют тестировать передовые API веб-платформ и помогают находить проблемы на вашем сайте раньше, чем это сделают пользователи.
Как связаться с командой инструментов разработчика Chrome
Чтобы обсудить новые функции, обновления или другие темы, связанные с инструментами разработчика, воспользуйтесь следующими вариантами:
- Отправить отзыв или запрос на добавление функции можно на сайте crbug.com.
- Сообщить о проблеме с инструментами разработчика можно, нажав на значок Другие инструменты > Справка > Сообщить о проблеме с инструментами разработчика.
- Напишите нам в Твиттере: @ChromeDevTools.
- Оставляйте комментарии к видео о новых функциях инструментов разработчика на YouTube или видео с советами по использованию инструментов разработчика на YouTube.