Frontend Meetup от OneTwoTrip: как мы билды разгоняли и DX улучшали

Продолжаем рассказывать подробности про наш офлайн-митап для фронтендеров, который прошёл в Москве 20 марта 2025. В этой серии — презентация Станислава Голышева. Он рассказал, как его команда ускорила сборки, улучшила developer experience и перешла на современные инструменты: pnpm, SWC и Rspack. Во время перехода, конечно, возникли некоторые трудности, и о них Стас тоже рассказал, а ещё поделился результатами, чтобы вы могли самостоятельно решить, стоит ли оно того.

Ссылка на выступление: https://vkvideo.ru/video-229335646_456239089

О спикере

Станислав Голышев — FE Platform TechLead. Стас занимается фронтенд-разработкой почти 5 лет и все их провёл в OneTwoTrip. Сейчас занимается разработкой UI-Kit, автоматизацией и вопросами FrontOps.

Станислав Голышев
Станислав Голышев

В чём была проблема

В 2024 году в OneTwoTrip на большинстве проектов ещё использовали Webpack и npm, из-за этого процесс сборки занимал довольно длительное время, а папка node_modules была сравнима с черной дырой, в которую затягивается вся свободная память машины. На 1:43 показаны примеры небольшого проекта, а на более крупных цифры могли быть в 5–6 раз больше. Поэтому было принято решение перейти на более современные инструменты — о них и поговорим далее.

Frontend Meetup от OneTwoTrip: как мы билды разгоняли и DX улучшали

Пакетные менеджеры: pnpm vs Bun

Pnpm, или Performant Node Package Manager, был одним из первых инструментов, на которые решили перейти в OneTwoTrip. Это один из популярных менеджеров пакетов JavaScript, первая стабильная версия которого вышла в 2017 году. Его используют многие компании, он активно развивается и поддерживается.

Главная фишка pnpm — жёсткие ссылки и глобальное хранилище. Что это значит? Пакет устанавливается в глобальное хранилище, а в локальном node_modules создаётся жёсткая ссылка. Таким образом, при повторной установке этого пакета в другом проекте или вертикали он будет взят из глобального хранилища. Это экономит время и память.

Кроме того, pnpm создаёт строгую структуру node_modules. Это помогает избежать проблем с транзитивными зависимостями, например, у вас в проекте используется библиотека неуказанная в package.json, при этом приложение как-то работает, но потом вы сносите какой-нибудь неиспользуемый пакет и приложение резко перестает работать, а оказывается что эта неуказанная зависимость бралась как раз из зависимостей удаленного пакета, pnpm не позволит такому случится, так как все зависимости в node_modules не будут выходить за область своего пакета, только если это не изменено настройкой node-linker в npmrc.

К плюсам pnpm можно отнести экономию памяти, высокую скорость установки пакетов, поддержку workspaces из коробки и полную совместимость с npm. Из минусов разве что возможные проблемы с миграцией legacy проектов из-за отличающейся работы с зависимостями.

Bun — это современный универсальный инструмент для улучшения работы с JavaScript и TypeScript. Он помогает быстрее запускать проекты и оптимизирует использование памяти. Первая стабильная версия bun вышла в 2023 году, сейчас инструмент активно развивается и поддерживается.

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

Bun, как и pnpm, использует глобальное хранилище и жёсткие ссылки. Для конфигурации использует toml файл, но недавно добавили также поддержку .npmrc. Оптимизирован для максимальной производительности благодаря низкоуровневой реализации на Zig.

К плюсам bun также можно отнести экономию памяти и очень высокую скорость установки пакетов. Также радует поддержка workspace из коробки и развитая экосистема.

Но есть и довольно весомые минусы. Здесь точно возникнут проблемы с миграцией legacy проектов. И наверняка понадобится время, чтобы освоить этот инструмент.

Что же выбрать?

Pnpm подойдёт для крупных и legacy проектов, где нам нужна полная совместимость с npm.

Bun можно выбрать для небольших, новых и экспериментальных проектов, где нужна максимальная скорость или необходимо упростить стек инструментов.

Мы в OneTwoTrip в итоге остановились на pnpm — и ни о чём не жалеем.

Транспайлеры: Babel vs SWC

Здесь у нас есть старый проверенный временем Babel, первая стабильная версия которого вышла в 2014 году, и более молодой SWC, появившийся в 2018. Не так давно появился и Oxc Transformer, который заявляет, что он в 4–5 раз быстрее SWC, но про него говорить пока рано, он ещё в альфе.

Babel в представлении не нуждается, все разработчики его знают и использовали. Это максимально распространённый инструмент. SWC же активно набирает популярность, развивается и поддерживается. Спойлер: мы выбрали именно его.

Чем же так хорош SWC? Он написан на Rust, делает акцент на многопоточности, из коробки умеет в минификацию, tree shaking и dead code elimination.

Если сравнивать, мы видим, что они полностью противоположны. Babel — очень медленный, много плагинов, сложная конфигурация, отличная документация, легко писать свои плагины и есть react-compiler. В SWC его нет, зато очень высокая скорость, мало плагинов (не потому, что он непопулярен, а потому что всё есть в коробочной версии) и лёгкая конфигурация. Зато плохая документация и сложно писать свои плагины (потому что нужно это делать на Rust).

Что же выбрать?

Мы считаем, что Babel стоит оставить только для legacy проектов, которые по какой-то причине нельзя перевести на SWC. А переехать может быть сложно из-за плохой документации, необходимости писать плагины на Rust (нам в этом помог ChatGPT, правда, всё было не так уж просто — подробности на 8:16) и адаптации некоторых участков кода (пример на 9:29).

Сборщики: Webpack, Vite или…

Сразу уточним, что у нас на проекте используется самописный сборщик на основе webpack, в котором очень много сложной логики. Так что переезд на любой сборщик, архитектурно от него отличающийся, был бы очень непростым.

Изначально мы считали, что у нас есть два варианта: старый Webpack и новый Vite. Но при попытках переехать на него мы сразу столкнулись с проблемами. Например, Vite обязывает в css модулях использовать суффикс module, а у нас по кодовой базе так не положено. Мы пробовали это исправить, но ничего не получилось.

Когда стали искать другой путь, нашли ещё четыре варианта: Rspack, Farm, Mako и уже знакомый нам Bun. Из всех этих вариантов нам больше всего подошёл Rspack.

Чем он так хорош? Rspack — это буквально Webpack, переписанный на Rust. Первая стабильная версия появилась только летом 2024, но он уже достиг 700 тысяч скачиваний в месяц (это на момент доклада, сейчас более 1.2 млн) и активно растёт.

В Rspack сделан акцент на многопоточность, предлагает drop-in замену Webpack и из коробки имеет множество оптимизаций. На 11:51 можно посмотреть, какое ускорение он предлагает.

А ещё у него целая экосистема: Rslib, Rsdoctor, Rspress, и так далее — полный перечень на 12:42.

Что мы получили в итоге?

Итого мы заменили Webpack на Rspack, Babel на SWC, Terser выкинули в пользу SWC минимайзера (только это сократило сборку на 10 секунд), CssMinimizer и PostCSS поменяли на LightningCssMinimizerRspackPlugin. А ещё заменили практически все Webpack плагины на встроенные в Rspack и поправили пути в некоторых самописных плагинах.

Ради чего же были все эти мучения? Установка зависимостей ускорилась в 4–5 раз, Dev Build — в 10 раз, Prod Build — в 12 раз, а HMR — в 4 раза.

Все эти результаты можно посмотреть на слайде на 14:49 и следующем, на 15:07. Не будет преувеличением сказать, что изменения колоссальные. А стоит ли вам пройти такой же путь, решайте сами.

1
Начать дискуссию