Оптимизация сборки Webpack: разбор инцидента в CI
Сборка фронтенда в CI выросла с 8 до 31 минуты после безобидного изменения конфигурации. Разбираем, как найти узкое место в Webpack 5, что измерять и почему включение кеша не всегда спасает пайплайн.

Рассмотрим воспроизводимый сценарий инцидента: проект на Webpack 5 и TypeScript сначала собирался в CI за 8 минут, а после добавления полных source map и очистки рабочего каталога перед каждым запуском стал занимать 31 минуту. В локальной разработке проблема почти не проявлялась. Цена сбоя — длиннее обратная связь для разработчика, занятые CI-раннеры и риск не успеть выпустить исправление. Ниже — не набор флагов, а порядок расследования, который помогает отделить медленную компиляцию от медленного окружения.
Что именно сломалось
В Webpack 5 итоговое время состоит не только из работы загрузчиков. В него входят чтение исходников, построение графа зависимостей, трансформация TypeScript и JavaScript, генерация source map, минификация, запись файлов и запуск проверок вокруг сборки. Если измерять только длительность всей команды, причина останется скрыта.
В нашем сценарии изменение затронуло два слоя. Во-первых, режим source map стал тяжелее: сборщик начал хранить больше информации о соответствии итогового кода исходникам. Во-вторых, CI перестал использовать файловый кеш между запусками. Каждый коммит снова проходил полный путь, хотя большая часть зависимостей не менялась.
Быстрая сборка начинается не с увеличения числа ядер, а с ответа на вопрос: какой этап действительно съедает время и повторяется без необходимости.
Какие данные собрать до правок
Зафиксируйте одну и ту же ветку, commit, версию Node.js, lock-файл, тип CI-раннера и объём доступной памяти. Для этой статьи ориентир — Node.js 20 LTS, Webpack 5.x и TypeScript 5.x. Если проект использует другие версии, результаты нельзя сравнивать напрямую: поведение загрузчиков, минификатора и кеша могло измениться.
- Запишите время полной сборки и время повторной сборки без изменения исходников.
- Сохраните размер каталога сборки и число файлов в нём.
- Проверьте, сколько памяти доступно процессу и не завершался ли он с ошибкой нехватки памяти.
- Сравните локальный запуск и CI на одном commit, но не смешивайте их в одну цифру.
- Отдельно измерьте этапы проверки типов, тестов, минификации и упаковки артефактов.
Кеш сборки — сохранённые результаты промежуточной обработки, которые можно повторно использовать при следующем запуске. Он ускоряет повторные сборки, но не делает первый запуск короче и не заменяет корректную инвалидацию.
Как читать результаты профилирования
Для Webpack 5 полезно включить профиль сборки и сохранить отчёт, а затем сравнить два запуска: холодный, когда промежуточных результатов нет, и тёплый, когда кеш доступен. Ищите не только самый длинный загрузчик. Важнее понять, сколько файлов он обрабатывает и меняется ли это число между коммитами.
Признаки разных узких мест
- Долго работает TypeScript. Проверьте, не запускает ли сборщик проверку типов для всех пакетов при каждом изменении. Трансформация кода и полная проверка типов — разные задачи, их не всегда стоит выполнять в одном последовательном этапе.
- Большую часть времени занимает минификация. Сравните объём JavaScript до и после tree shaking и число выходных чанков. Минификатор получает уже собранный граф, поэтому кеш загрузчиков здесь не устранит его работу.
- Растёт время генерации source map. В production выберите режим, соответствующий задаче отладки, а не автоматически самый подробный. Полная карта исходников увеличивает время и размер артефактов.
- Сборка почти не использует процессор. Причина может быть не в Webpack: проверьте чтение файлов, сетевой диск, распаковку зависимостей, лимит памяти и очередь на CI-раннере.
- Холодный запуск нормальный, повторный не быстрее. Значит, кеш не сохраняется между job или постоянно инвалидируется. Проверьте ключ кеша, путь к нему и то, что зависимости действительно восстанавливаются до запуска сборки.
Три подхода к ускорению
Оптимизировать всё сразу опасно. Вы не поймёте, какая правка помогла, а изменение source map или минификации может ухудшить диагностику production-сбоя.
| Подход | Что ускоряет | Цена и риск | Когда применять |
|---|---|---|---|
| Файловый кеш Webpack 5 | Повторную обработку неизменившихся модулей | Нужны место в CI и корректная инвалидация | Сборки повторяются на том же runner или кеш можно восстановить |
| Разделение задач | Цикл разработки и отдельные проверки типов | Появляется несколько этапов и правила публикации | Разработчику не нужна полная production-проверка на каждый локальный запуск |
| Сужение графа | Число модулей, проходящих через загрузчики | Нужно проверять зависимости и исключения, легко скрыть проблему | Загрузчики обрабатывают внешние пакеты или лишние каталоги |
Что обычно даёт самый безопасный результат
Начните с файлового кеша Webpack 5. Он не меняет итоговый JavaScript, поэтому риск отличается от смены минификатора или source map. Кеш должен жить там, где CI сможет восстановить его до сборки; каталог, который удаляется в начале каждой job, для этой цели бесполезен.
Затем проверьте область применения загрузчиков. TypeScript-загрузчик не должен без причины получать файлы из каталогов зависимостей. Исключение должно соответствовать структуре конкретного проекта, а не копироваться из чужой конфигурации: монорепозиторий, workspace и внешние пакеты меняют границы графа.
Третий шаг — развести режимы. Для локального цикла нужен быстрый результат и удобная отладка. Для production важны воспроизводимость, размер артефакта и диагностируемость. Один профиль, пытающийся одновременно решить все задачи, часто оказывается самым медленным компромиссом.
Где подход ломается первым
Кеш перестаёт помогать после смены версии Node.js, Webpack, загрузчика, lock-файла или параметров сборки. Это нормальная инвалидация: старый результат нельзя безопасно считать совместимым с новым окружением. Проблема начинается, когда ключ кеша не учитывает эти изменения и выдаёт старые артефакты.
В монорепозитории частый крайний случай — изменение общего пакета. Формально вы меняете один файл, но его импортируют десятки приложений. Поэтому смотреть только на размер diff недостаточно: отслеживайте граф зависимостей и число реально пересобранных модулей.
Ещё один риск — memory limit. Параллельная обработка может сократить CPU-время, но одновременно увеличить пик памяти. Если CI завершает процесс без понятного отчёта, сначала найдите ограничение раннера, а не добавляйте ещё больше параллелизма.
Что проверять после каждой правки
- Запустите холодную сборку три раза и сравните не только среднее время, но и разброс.
- Запустите тёплую сборку после изменения одного исходного модуля.
- Сверьте хеши или содержимое production-артефактов с контрольной сборкой.
- Проверьте source map, размер бандлов, предупреждения и exit code.
- Повторите тест на том же типе CI-раннера, где возникла проблема.
Один удачный запуск не доказывает улучшение. CI зависит от соседних job, состояния кеша и нагрузки на runner, поэтому сравнивать нужно несколько одинаковых прогонов.
Как ревьюить оптимизацию
Хорошее замечание в ревью привязано к измерению. Вместо фразы «добавьте кеш» спросите, какой именно этап он сокращает, где хранится кеш и что входит в его ключ. Вместо «уберите source map» уточните, какой режим нужен для расследования production-ошибок и чем его заменит команда.
Отдельно проверьте воспроизводимость. Сборка не должна зависеть от случайного содержимого каталога на runner или от пакетов, которые не зафиксированы lock-файлом. Ускорение, после которого разные job публикуют разные артефакты, — не оптимизация, а новый инцидент.
Что делать дальше
Добавьте в CI два измерения: длительность холодной и тёплой сборки, а также размер восстановленного кеша. Сохраняйте их по commit и версии окружения. Через несколько запусков станет видно, действительно ли ускорение держится или результат получен из-за удачного состояния runner.
Следующим шагом вынесите в отдельные метрики время загрузчиков, минификации, проверки типов и упаковки артефакта. Если после этого самым дорогим этапом окажется не Webpack, продолжать менять его конфигурацию бессмысленно: проблема находится в CI, файловой системе или соседней проверке.
Аналитик звонков: Разбор записи звонка по этапам сделки: где сорвалось, что упущено, три исправляемые рекомендации с цитатами.
Спросить за 90 ₽Источники: Webpack: сборка и кеширование, Webpack: руководство по производительности
Частые вопросы
Нужно ли включать кеш Webpack 5 в production-сборке?
Почему после включения кеша первая сборка не стала быстрее?
Что делать, если после оптимизации артефакт изменился?
А если у меня другая версия Webpack или Node.js?
Что делать, если сборка иногда ускоряется, а иногда снова становится долгой?
Какие подводные камни важны в production?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.