Package Managers и Bundlers
Экспресс-шпаргалка 20/20
- npm/pnpm/yarn различаются стратегией хранения зависимостей, скоростью установки и удобством в монорепах.
- Lockfile фиксирует точное дерево зависимостей и обеспечивает воспроизводимые сборки.
npm ciставит строго по lockfile, быстрее и стабильнее для CI, чемnpm install.- Semver определяет совместимость версий, но диапазоны могут неожиданно подтянуть breaking changes.
- peerDependencies декларируют совместимость с хост-пакетом; конфликты требуют выравнивания версий.
- Workspaces позволяют централизованно управлять зависимостями и пакетами в монорепозитории.
- Обновление зависимостей делается поэтапно с changelog-review, тестами и rollback планом.
- Vite обычно быстрее в dev, Webpack часто гибче в сложных enterprise-конфигах.
- Tree shaking работает при ESM, корректных imports и правильном
sideEffects. - Code splitting разбивает бандл на чанки и снижает стартовую загрузку JS.
- Анализ бандла делают через visualizer/stats, чтобы найти тяжёлые или дублирующиеся зависимости.
- HMR может ломаться из-за stateful side effects и нестабильных boundaries.
- Env-переменные в бандле нужно разделять на публичные и приватные, чтобы не утекали секреты.
- Source maps нужны для прод-диагностики, но доступ к ним надо ограничивать.
- CI ускоряют кэшированием, параллелизацией и уменьшением лишних шагов pipeline.
- Кэширование ассетов строится на content hash + immutable cache headers.
- Release pipeline включает quality gates: lint, typecheck, tests, build, smoke.
- Vulnerability management: регулярный аудит, patch policy и контроль транзитивных пакетов.
- Anti-patternы конфига: неуправляемые плагины, хаос alias/loaders и отсутствие budget.
- Миграция Webpack -> Vite делается поэтапно с проверкой build parity и метрик.
1. В чем разница npm, pnpm и yarn на практике?
Теги: tooling, build, package-manager
Сложность: Middle/Senior
Короткий ответ
npm, pnpm и Yarn решают одну задачу, но по-разному строят dependency tree, lockfile workflow, workspaces и node_modules. На практике выбор влияет на воспроизводимость установок, скорость CI, совместимость tooling, строгость к undeclared dependencies и удобство монорепозитория.
Что сказать на интервью (30-60 секунд)
Я бы не выбирал package manager по вкусу. npm - дефолт экосистемы и самый простой baseline. pnpm экономит диск и ускоряет installs за счет content-addressable store, hard links и более строгой структуры зависимостей. Yarn modern дает Plug'n'Play, constraints и сильную диагностику ghost dependencies, но может требовать настройки IDE/tooling. На интервью важно назвать миграционный риск: lockfile меняется, CI cache меняется, и часть пакетов может опираться на неявно доступные зависимости.
Мини-пример
// package.json
{
"packageManager": "pnpm@10.12.1",
"workspaces": ["apps/*", "packages/*"]
}
Углубление (2-3 минуты)
| Критерий | npm | pnpm | Yarn modern |
|---|---|---|---|
| Adoption baseline | самый простой старт | нужен team agreement | нужна настройка версии/режима |
| Install strategy | hoisted node_modules | content-addressable store + links | PnP или node_modules linker |
| Ghost dependencies | легче случайно не заметить | строже из-за layout | PnP явно ловит undeclared deps |
| Monorepo | workspaces есть | сильный workspace workflow | workspaces + constraints |
| Migration risk | минимальный | tooling может не любить symlinks | PnP может требовать IDE/tool fixes |
Кейс: монорепа с 20 пакетами долго ставит зависимости и периодически ловит "works on my machine", потому что пакет импортирует transitive dependency. Хороший ответ: сравнить package managers на branch, зафиксировать packageManager, lockfile, CI cache key, install command и проверить ghost dependency failures до миграции.
Практика
- Составьте package manager decision table для single app, monorepo, legacy enterprise app.
- Для каждого варианта укажите install command, lockfile, CI cache key и главный migration risk.
- Найдите один пример ghost dependency: импорт пакета, которого нет в
dependenciesтекущего workspace.
Типичные ошибки
- Смешивают npm/pnpm/yarn lockfiles в одном репозитории.
- Не фиксируют
packageManagerи получают разные версии CLI у команды. - Мигрируют package manager вместе с большим dependency upgrade.
- Игнорируют tooling, который не поддерживает symlinks или PnP.
Follow-up вопросы
- Что такое ghost dependency?
- Почему pnpm может сломать код, который работал на hoisted
node_modules? - Когда Yarn PnP не стоит включать сразу?
- Что должно попасть в migration checklist package manager?
Что повторить
- npm, pnpm, Yarn install strategies.
packageManager, Corepack, workspaces, lockfiles.- Ghost dependencies, hoisting, symlinks, PnP.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
2. Зачем нужен lockfile и почему его нельзя игнорировать?
Теги: tooling, build, package-manager
Сложность: Middle/Senior
Короткий ответ
Lockfile фиксирует точное дерево зависимостей и обеспечивает воспроизводимые сборки.
Что сказать на интервью (30-60 секунд)
Lockfile - это не мусорный generated-файл, а контракт установки. package.json говорит "какие диапазоны разрешены", а lockfile фиксирует конкретные resolved versions, integrity hashes и структуру dependency tree. Если lockfile не коммитить или обновлять случайно, две машины могут поставить разные transitive versions, даже когда package.json не менялся.
Мини-пример
package.json: "react": "^18.2.0"
package-lock.json: react@18.3.1 + integrity sha512-...
pnpm-lock.yaml: exact package graph + peer resolution context
Углубление (2-3 минуты)
| Ситуация | Что должен сделать кандидат | Почему |
|---|---|---|
Changed package.json, lockfile unchanged | обновить lockfile осознанно | иначе CI/install drift |
Changed lockfile, package.json unchanged | проверить transitive update | возможен security или breaking drift |
| Несколько lockfiles | оставить один manager | разные installers читают разные truth |
| Merge conflict в lockfile | regenerate clean install | ручной merge легко ломает graph |
| CI использует frozen install | fail при mismatch | защита reproducibility |
Кейс: локально тесты проходят, CI падает после fresh install. В PR изменили package.json, но не lockfile. Сильный ответ: CI должен запускать frozen/clean install, падать на mismatch, а разработчик должен пересоздать lockfile тем же package manager и той же CLI-версией.
Практика
- Сделайте lockfile drift drill: изменили dependency range, lockfile не обновили, CI запускает frozen install.
- Опишите, какой command должен упасть:
npm ci,pnpm install --frozen-lockfileилиyarn install --immutable. - Составьте checklist ревью lockfile diff: direct dependency, transitive dependency, integrity, peer resolution.
Типичные ошибки
- Добавляют lockfile в
.gitignoreдля application repo. - Ручками правят lockfile вместо regenerate через package manager.
- Не замечают, что lockfile diff обновил сотни transitive packages.
- Используют
npm installв CI и случайно переписывают lockfile.
Follow-up вопросы
- Чем
package.jsonотличается от lockfile? - Почему lockfile надо коммитить для приложения?
- Как разбирать lockfile merge conflict?
- Почему один repo не должен держать несколько lockfiles без причины?
Что повторить
package-lock.json,pnpm-lock.yaml,yarn.lock.- Integrity hashes, resolved versions, peer resolution.
- Frozen/immutable installs in CI.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
3. Разница npm install и npm ci?
Теги: tooling, build, package-manager
Сложность: Middle/Senior
Короткий ответ
npm ci ставит строго по lockfile, быстрее и стабильнее для CI, чем npm install.
Что сказать на интервью (30-60 секунд)
npm install - рабочая команда для разработки: она может обновить package-lock.json, добавить пакет и привести lockfile к package.json. npm ci - clean install для автоматизированной среды: требует существующий lockfile, удаляет node_modules, не пишет в package.json/lockfile и падает, если lockfile не совпадает с manifest.
Мини-пример
# CI
steps:
- run: npm ci
- run: npm test
- run: npm run build
Углубление (2-3 минуты)
| Поведение | npm install | npm ci |
|---|---|---|
| Требует lockfile | нет | да |
| Может менять lockfile | да | нет |
Удаляет node_modules | нет автоматически | да |
| Добавляет отдельный пакет | да | нет |
| Подходит для CI | хуже | да |
| Падает при lockfile mismatch | не обязан | да |
Кейс: разработчик добавил dependency в package.json, но забыл lockfile. npm install в CI может поправить дерево и скрыть проблему, а npm ci упадет и заставит принести reproducible diff. Хороший ответ также отмечает, что flags вроде legacy-peer-deps должны совпадать между lockfile generation и npm ci.
Практика
- Разберите CI install failure:
package.jsonиpackage-lock.jsonрасходятся. - Назовите правильный fix: локально
npm install, commit lockfile, сноваnpm ci. - Проверьте
.npmrc: есть ли flags, которые меняют форму dependency tree.
Типичные ошибки
- Используют
npm installв CI и получают непредсказуемый lockfile drift. - Запускают
npm ciбез закоммиченногоpackage-lock.json. - Не коммитят
.npmrc, хотя lockfile создан с важными install flags. - Лечат peer conflict через
--legacy-peer-depsбез понимания причины.
Follow-up вопросы
- Почему
npm ciудаляетnode_modules? - Что произойдет, если lockfile не совпадает с
package.json? - Когда локально нужен
npm install, а неnpm ci? - Почему install flags должны быть одинаковыми?
Что повторить
npm ci,npm install, lockfile mismatch..npmrc, install flags,legacy-peer-deps,strict-peer-deps.- CI cache: npm cache vs
node_modulescache.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
4. Как работает semver и риски диапазонов версий?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
Semver определяет совместимость версий, но диапазоны могут неожиданно подтянуть breaking changes.
Что сказать на интервью (30-60 секунд)
SemVer - это MAJOR.MINOR.PATCH: major для breaking API, minor для backward-compatible features, patch для backward-compatible fixes. Но dependency ranges - это отдельный слой риска: ^1.2.3 может подтянуть новый minor, ~1.2.3 обычно держит в пределах patch, а 0.x опаснее, потому что SemVer считает major zero нестабильной фазой.
Мини-пример
// package.json
{
"dependencies": {
"lib-a": "^1.2.3",
"lib-b": "~1.2.3",
"lib-c": "1.2.3"
}
}
Углубление (2-3 минуты)
| Range | Что разрешает | Риск |
|---|---|---|
1.2.3 | только exact version | мало автоматических fixes |
~1.2.3 | patch updates | меньше риск, но fixes ограничены |
^1.2.3 | minor + patch в рамках major | minor может сломать на практике |
^0.2.3 | обычно patch внутри 0.2.x | 0.x API нестабилен |
* / latest | слишком широко | почти нет reproducibility без lockfile |
Кейс: библиотека выпустила minor, формально backward-compatible, но поменяла поведение edge-case. При range ^ новый install без lockfile подтянул эту версию и сломал сборку. Хороший ответ: lockfile для приложений, Renovate/Dependabot PR с changelog и тестами, exact pin для критичных пакетов при необходимости.
Практика
- Составьте semver range risk matrix для
^,~, exact,0.x,latest. - Для каждого range предскажите максимальную версию, которую может поставить resolver.
- Опишите dependency update policy: кто ревьюит changelog, какие тесты обязательны, когда pin оправдан.
Типичные ошибки
- Думают, что SemVer гарантирует отсутствие багов.
- Не учитывают, что
0.y.zможет менять API агрессивнее. - Используют
latestв production dependencies. - Обновляют большой набор packages одним PR без понятного rollback.
Follow-up вопросы
- Чем
^1.2.3отличается от~1.2.3? - Почему lockfile не отменяет need review dependency updates?
- Что особенного в
0.xверсиях? - Когда стоит pin exact version?
Что повторить
- SemVer major/minor/patch and public API.
- npm version ranges: exact,
~,^,x,latest. - Dependency update policy and changelog review.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
5. Что такое peerDependencies и как решать конфликты?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
peerDependencies декларируют совместимость с хост-пакетом; конфликты требуют выравнивания версий.
Что сказать на интервью (30-60 секунд)
peerDependencies нужны библиотекам и плагинам, которые должны работать с host package, но не должны приносить свою отдельную копию host. Типичный пример: React component library требует react как peer, чтобы приложение использовало одну совместимую копию React. Конфликт решается не "заглушить warning", а понять, кто требует какой host range, и выровнять версии.
Мини-пример
// package.json
{
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
}
Углубление (2-3 минуты)
| Сценарий | Что означает | Правильное действие |
|---|---|---|
Plugin wants webpack@^5 | нужен host webpack 5 | обновить host или plugin |
Component lib wants react@^18 | должна быть одна React copy | выровнять app React |
| Peer range слишком узкий | package может быть совместим шире | проверить changelog/issues, обновить package |
| Optional peer | интеграция не обязательна | не ставить без нужной feature |
--legacy-peer-deps | npm игнорирует peer resolution | временный workaround, не fix |
Кейс: после обновления UI-kit install падает: ui-kit требует react@^18, приложение уже на React 19. Сильный ответ: проверить, есть ли версия UI-kit с peer range для React 19, не ставить вторую копию React, не делать force install без runtime test, а если это внутренняя библиотека - расширить peer range после совместимости.
Практика
- Разберите peer dependency conflict walkthrough: app, plugin, host package, requested peer ranges.
- Выпишите три решения: upgrade host, upgrade plugin, replace package; отметьте риск каждого.
- Сформулируйте, когда
peerDependenciesMeta.optionalуместен.
Типичные ошибки
- Лечат конфликт флагом
--forceили--legacy-peer-depsбез проверки runtime. - Дублируют host dependency внутри библиотеки как обычную
dependency. - Слишком узко фиксируют peer range на patch version.
- Не понимают, кто является host package в конфликте.
Follow-up вопросы
- Почему React часто должен быть peer dependency в UI libraries?
- Чем peer dependency отличается от dependency?
- Что изменилось в npm v7+ для peerDependencies?
- Когда конфликт peer range лучше решать upgrade, а не override?
Что повторить
peerDependencies,peerDependenciesMeta, host package.- npm peer resolution,
strict-peer-deps,legacy-peer-deps. - Dependency graph diagnosis:
npm explain,npm ls, package-manager warnings.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
6. Когда применять workspaces в монорепозитории?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
Workspaces нужны, когда в одном репозитории живут несколько связанных пакетов: приложения, shared UI, utils, configs, SDK. Они дают локальное связывание packages, единый install/lockfile workflow и возможность запускать команды по конкретным workspace, но требуют строгих границ зависимостей и понятной ownership-модели.
Что сказать на интервью (30-60 секунд)
Я бы применял workspaces, если пакеты реально развиваются вместе: например, apps/web, packages/ui, packages/eslint-config. Плюс - локальные packages подключаются без ручного npm link, dependency graph виден package manager-у, а CI может запускать команды точечно. Риск - превратить monorepo в клубок: package A импортирует private internals package B, версии shared пакетов не синхронизированы, а root scripts скрывают, что реально изменилось.
Мини-пример
{
"private": true,
"workspaces": ["apps/*", "packages/*"]
}
Углубление (2-3 минуты)
| Сценарий | Workspaces подходят? | Почему |
|---|---|---|
| App + shared UI + eslint config | да | общий цикл разработки |
| Два независимых продукта | не обязательно | лишняя связанность |
| Library published отдельно | да, если нужен local integration | нужен release/versioning plan |
| Microfrontends с разными командами | осторожно | ownership и release cadence |
| Только один app | обычно нет | complexity без выгоды |
Кейс: команда правит UI-kit и приложение в одном PR. Без workspaces приходится публиковать preview package или использовать npm link. С workspaces app видит локальный @org/ui, но хороший ответ должен включать boundary rules: публичные exports, no deep imports, affected tests, versioning/publishing strategy.
Практика
- Составьте workspace boundary map:
apps/web,packages/ui,packages/config,packages/api-client. - Для каждого пакета укажите owner, public API, allowed imports и affected tests.
- Опишите CI: какие команды запускать для changed workspace и его dependents.
Типичные ошибки
- Делают deep imports из соседнего пакета вместо публичного entrypoint.
- Хранят dependency в root, хотя она нужна только одному workspace.
- Не проверяют dependents после изменения shared package.
- Не имеют стратегии versioning/publish для packages, которые уходят наружу.
Follow-up вопросы
- Чем workspace отличается от
npm link? - Что такое affected tests в monorepo?
- Почему deep imports опасны для shared package?
- Когда monorepo усложнит, а не ускорит delivery?
Что повторить
- npm/pnpm/Yarn workspaces, workspace filtering.
- Public package API,
exports, dependency ownership. - Affected graph, local linking, publish/versioning plan.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
7. Как безопасно обновлять зависимости в большом проекте?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
Обновление зависимостей делается поэтапно с changelog-review, тестами и rollback планом.
Что сказать на интервью (30-60 секунд)
Я бы разделил dependency update на обычный patch/minor и risky major/security update. Безопасный процесс: маленькие PR, changelog diff, lockfile review, focused tests, smoke build и отдельный rollback plan. Самый частый риск - обновить много пакетов одним PR, получить поломку и не понять, какая transitive dependency изменила поведение.
Мини-пример
1. Update one package group
2. Review changelog + lockfile diff
3. Run focused tests + build
4. Ship behind normal release gates
5. Revert package + lockfile together if needed
Углубление (2-3 минуты)
| Update type | Как выпускать | На что смотреть |
|---|---|---|
| Patch | обычно small PR | changelog, tests, lockfile size |
| Minor | grouped by ecosystem | deprecations, peer ranges |
| Major | отдельный migration PR | breaking changes, codemods |
| Security fix | priority path | exploitability, prod usage |
| Transitive override | temporary | owner issue, removal date |
| Tooling update | staged | CI time, build output parity |
Кейс: обновили eslint, typescript, bundler plugins и testing library одним PR. CI прошел, но локальный dev server стал падать. Сильный ответ: разделить update groups, сначала tooling baseline, затем framework packages, записать lockfile diff, проверить dev/build/test отдельно и иметь быстрый revert именно package+lockfile pair.
Практика
- Составьте dependency update rollout plan для React ecosystem, bundler plugins и security patch.
- Для каждого update укажите affected area, smoke test, rollback command и risk level.
- Разберите lockfile diff: direct update или transitive cascade.
Типичные ошибки
- Обновляют "все сразу" и теряют причинность поломки.
- Ревертят только
package.json, забывая lockfile. - Игнорируют peer dependency warnings после update.
- Делают permanent override без задачи на удаление.
Follow-up вопросы
- Как группировать dependency update PR?
- Чем direct dependency update отличается от transitive update?
- Когда override оправдан?
- Какие тесты нужны для major tooling update?
Что повторить
- Changelog review, lockfile diff, dependency grouping.
- Renovate/Dependabot style workflows.
- Overrides/resolutions, peer conflicts, rollback of package+lockfile.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
8. Как выбирать между Vite и Webpack?
Теги: tooling, build, bundlers
Сложность: Middle/Senior
Короткий ответ
Vite обычно быстрее в dev, Webpack часто гибче в сложных enterprise-конфигах.
Что сказать на интервью (30-60 секунд)
Я бы выбирал не "модный bundler", а toolchain под constraints. Vite силен в dev experience: native ESM, быстрый cold start, HMR, pre-bundling dependencies, простой config. Webpack часто остается оправданным в сложных legacy/enterprise проектах: кастомные loaders/plugins, Module Federation, нестандартные asset pipelines, тонкая настройка SplitChunks. Решение надо проверять build parity: env, aliases, CSS, assets, tests, source maps, output chunks.
Мини-пример
Decision criteria:
- dev server startup and HMR latency
- plugin/loader compatibility
- production output parity
- SSR/library/microfrontend requirements
- team migration cost
Углубление (2-3 минуты)
| Критерий | Vite | Webpack |
|---|---|---|
| Dev startup/HMR | обычно сильная сторона | зависит от config/cache |
| Legacy loaders | может потребовать замены | зрелая экосистема loaders |
| Module Federation | не core story | зрелая поддержка |
| Production bundling | Rollup/Rolldown path | webpack optimizer |
| Config complexity | ниже для modern apps | выше, но гибче |
| Migration risk | parity gaps | сохранить legacy проще |
Кейс: SPA на Webpack стартует 40 секунд, но использует custom loader для CMS imports и Module Federation. Сильный ответ: не переписывать все сразу, а сделать proof-of-concept на одном entrypoint, измерить dev/build, проверить output parity и список несовместимых plugins.
Практика
- Составьте Vite/Webpack decision matrix для new SPA, legacy app, microfrontend shell, component library.
- Для миграции Webpack -> Vite запишите parity checklist: env, aliases, SVG/CSS, dynamic imports, source maps.
- Назовите rollback strategy, если production bundle изменил поведение.
Типичные ошибки
- Сравнивают только dev server speed и не проверяют production output.
- Переносят Webpack aliases/loaders "как есть" без parity tests.
- Игнорируют SSR/library/microfrontend requirements.
- Меняют bundler и одновременно обновляют framework/dependencies.
Follow-up вопросы
- Почему Vite быстрее в dev на больших проектах?
- Какие Webpack features сложнее заменить?
- Что такое build parity?
- Как измерить успешность bundler migration?
Что повторить
- Vite native ESM, dependency pre-bundling, production build.
- Webpack loaders/plugins, SplitChunks, Module Federation.
- Migration parity checklist and rollback plan.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
9. От чего зависит tree shaking?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
Tree shaking работает при ESM, корректных imports и правильном sideEffects.
Что сказать на интервью (30-60 секунд)
Tree shaking - это удаление неиспользуемого кода из production bundle. Оно лучше работает, когда код представлен как ESM с static imports/exports, а package правильно сообщает, какие файлы имеют side effects. На интервью важно назвать не только import { debounce }, но и риски: CommonJS хуже анализируется, barrel files могут протащить side effects, а sideEffects: false может случайно выбросить CSS/polyfills.
Мини-пример
// less effective
import _ from 'lodash';
// better for bundle size
import debounce from 'lodash/debounce';
Углубление (2-3 минуты)
| Фактор | Помогает | Ломает/мешает |
|---|---|---|
| Module format | ESM static exports | CommonJS dynamic require |
| Imports | named/direct imports | namespace import whole library |
sideEffects | точный список CSS/polyfills | false при наличии CSS imports |
| Barrel files | re-export only | initialization side effects |
| Minifier | removes unused code | cannot prove purity |
| Build mode | production optimization | dev mode не показатель |
Кейс: UI library поставила "sideEffects": false, а CSS импортировался внутри компонентов. Production bundle выбросил CSS, компоненты стали без стилей. Сильный ответ: проверить bundle analyzer, production build, sideEffects list вроде ["**/*.css"], ESM entrypoints и direct imports.
Практика
- Составьте tree-shaking diagnosis checklist: module format, import style, sideEffects, analyzer diff, production-only bug.
- Для UI package решите, можно ли ставить
"sideEffects": falseили нужен список CSS files. - Сравните bundle до/после direct import и объясните, какой модуль исчез.
Типичные ошибки
- Проверяют tree shaking в dev build.
- Ставят
sideEffects: falseи ломают CSS/polyfills. - Импортируют весь namespace большой библиотеки без необходимости.
- Думают, что bundler удалит любой unused code в CommonJS.
Follow-up вопросы
- Почему ESM важен для tree shaking?
- Чем
sideEffectsотличается отusedExports? - Почему CSS import - side effect?
- Как проверить, что optimization реально сработала?
Что повторить
- ESM, CommonJS, static analysis.
sideEffects, CSS imports, pure annotations.- Bundle analyzer and production build comparison.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
10. Как организовать code splitting и chunk strategy?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
Code splitting разбивает бандл на чанки и снижает стартовую загрузку JS.
Что сказать на интервью (30-60 секунд)
Code splitting нужен не чтобы "сделать много чанков", а чтобы пользователь не скачивал код, который не нужен для первого сценария. Основные инструменты: route-level split, dynamic import для тяжелых экранов, vendor/common chunks, preload/prefetch. Trade-off: меньше initial JS, но больше network requests, риск ChunkLoadError, waterfall и cache invalidation проблем после deploy.
Мини-пример
const AdminPanel = lazy(() => import('./AdminPanel'));
// грузим admin-код только когда пользователь открыл admin route
Углубление (2-3 минуты)
| Split boundary | Когда подходит | Риск |
|---|---|---|
| Route-level | разные страницы/разделы | слишком поздняя загрузка route |
| Component-level | тяжелый rarely-used widget | UX fallback/jank |
| Vendor chunk | стабильные dependencies | один огромный vendor |
| Common chunk | shared code | extra roundtrip/waterfall |
| Manual chunks | контроль cache/load order | brittle config |
| Prefetch/preload | вероятный next action | лишний traffic |
Кейс: dashboard грузит charting library на главной странице, хотя графики видит 10% пользователей. Хороший ответ: dynamic import chart widget, skeleton/loading state, bundle analyzer diff, LCP/INP check, обработка failed chunk load и проверка, не появился ли общий chunk, который все равно грузится на старте.
Практика
- Сделайте chunk strategy review для routes: home, dashboard, admin, editor.
- Для каждого route укажите initial chunks, async chunks, shared vendor и loading fallback.
- Опишите проверку после deploy: bundle analyzer, network waterfall,
ChunkLoadError, LCP/INP.
Типичные ошибки
- Делят код слишком мелко и получают waterfall из десятков requests.
- Выносят весь vendor в огромный initial chunk.
- Не обрабатывают failed async chunk после deploy.
- Добавляют lazy loading без нормального loading/error state.
Follow-up вопросы
- Чем route-level split отличается от component-level split?
- Почему слишком много чанков может ухудшить performance?
- Что такое
ChunkLoadErrorи как его пережить? - Когда manual chunks полезны, а когда вредны?
Что повторить
- Dynamic import, route splitting, vendor/common chunks.
- Webpack SplitChunks, Vite/Rollup manual chunks.
- Network waterfall, preload/prefetch, chunk-load failure handling.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
11. Как анализировать размер бандла и находить причины роста?
Теги: tooling, build, bundlers
Сложность: Middle/Senior
Короткий ответ
Размер бандла анализируют не по одному числу dist, а по входным чанкам, async-чанкам, gzip/brotli-размеру, дубликатам зависимостей и влиянию на первый экран. Главная цель - найти конкретную причину роста: новая тяжёлая библиотека, случайный импорт всего пакета, дубль версии, локали, polyfill, неверный split или регрессия tree shaking.
Что сказать на интервью (30-60 секунд)
Я начинаю с вопроса: вырос initial bundle или lazy chunk. Потом сравниваю два production build-а через analyzer/stats, смотрю gzip/brotli, composition по чанкам и network waterfall. Дальше ищу не "самую большую библиотеку вообще", а изменение: кто добавил dependency, почему она попала в initial path, нет ли дубля версий или импорта lodash/иконок/локалей целиком. Исправление проверяю бюджетом размера и пользовательской метрикой, например LCP/TTI на целевом сценарии.
Мини-пример
npm run build
# Сравните до/после:
# - initial JS/CSS chunks
# - async route chunks
# - gzip/brotli size
# - duplicated packages and unexpected modules
Углубление (2-3 минуты)
- Разделяйте
raw, gzip/brotli и реально загружаемый critical path. Большой async-чунк админки не равен регрессии первого экрана. - Смотрите composition по чанкам: route code, vendor, shared, CSS, assets. Проблема часто не в размере библиотеки, а в том, что она попала в общий chunk.
- Проверяйте дубли: две версии одной зависимости, ESM+CJS вариант одного пакета, повторные runtime helpers.
- Проверяйте импорт:
import * as icons,momentс локалями, barrel-файлы, side effects в пакетах, неработающий tree shaking. - Фиксируйте bundle budget в CI, но не превращайте его в слепой запрет: иногда рост оправдан фичей, тогда нужен осознанный budget change.
Практика
Возьмите PR, где добавили rich text editor на одну страницу. Объясните, почему его нельзя тащить в общий vendor, как выделить lazy chunk, какие метрики сравнить до/после и где поставить бюджет: на весь build или на initial route.
Типичные ошибки
- Смотрят только общий размер
distи не отделяют initial load от lazy routes. - Лечат проблему manual chunks без понимания network waterfall и cache hit rate.
- Удаляют одну большую библиотеку, но оставляют дубль версии или импорт всего пакета через barrel.
- Сравнивают non-production build, где sourcemaps/dev helpers искажают картину.
Follow-up вопросы
- Как отличить допустимый рост bundle от регрессии?
- Почему manual chunks иногда ухудшают performance?
- Как найти зависимость, которая попала в initial bundle случайно?
- Что будете делать, если analyzer показывает дубль одной библиотеки?
Что повторить
- Bundle analyzer/stats, gzip/brotli, initial vs async chunks.
- Tree shaking, side effects, ESM/CJS, dynamic import.
- Webpack
SplitChunks, Vite/RollupmanualChunks, route-based splitting.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
12. Как работает HMR и почему иногда ломается?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
HMR заменяет изменённый модуль без полной перезагрузки страницы, если bundler может найти безопасную update boundary. Он ломается, когда изменение нельзя применить локально: модуль держит глобальный side effect, не очищает подписки, меняет форму exports, нарушает framework-boundary или требует пересоздать состояние приложения.
Что сказать на интервью (30-60 секунд)
HMR - это dev-time механизм обратной связи, а не production feature. Bundler получает изменение файла, пересобирает affected modules и пытается применить update через boundary, например компонент, который умеет принять замену. Если boundary не найден или модуль оставил side effects, происходит full reload или stale state. Поэтому при отладке я смотрю не только на bundler, но и на форму модуля: есть ли module-level singleton, подписки, таймеры, CSS side effects, barrel exports и корректная cleanup-логика.
Мини-пример
export function createFeatureStore() {
return { ready: true };
}
if (import.meta.hot) {
import.meta.hot.accept((nextModule) => {
if (!nextModule) return;
// Re-read exports or invalidate if state shape changed.
});
}
Углубление (2-3 минуты)
- Boundary - это модуль, который может принять обновление сам или от прямой зависимости. Если его нет, update поднимается выше и часто заканчивается reload.
- Stateful side effects должны очищаться: WebSocket, timers, event listeners, global stores, mock servers. Иначе после нескольких hot updates появляются двойные подписки и stale state.
- В React/Vue/Svelte большую часть HMR берёт на себя интеграция framework, но она ожидает стабильную форму компонента. Смена hook order, default/named exports или смешивание runtime logic с module init может ломать refresh.
- HMR не доказывает корректность production build. Ошибка может быть только в dev-server path или только в production chunking/minification.
Практика
Представьте, что после изменения файла формы страница не перезагружается, но обработчик submit срабатывает два раза. Разберите причину: где создан listener, кто должен вызывать cleanup, когда лучше сделать invalidate, а когда исправить boundary.
Типичные ошибки
- Считать HMR обычной перезагрузкой и не думать про старое состояние модуля.
- Держать side effects на верхнем уровне файла без cleanup.
- Делать общий barrel, из-за которого непонятно, какой модуль должен принять update.
- Диагностировать production-баг только через dev-server/HMR.
Follow-up вопросы
- Что такое HMR boundary?
- Почему иногда нужен full reload вместо hot update?
- Как HMR связан с module-level side effects?
- Почему HMR может работать в dev, но production build всё равно падает?
Что повторить
import.meta.hot.accept,dispose,invalidate.- Framework Fast Refresh/HMR boundaries.
- Side effects, module cache, full reload fallback.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
13. Как правильно работать с env переменными в сборке?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
Env в frontend-сборке надо разделять на public build-time values и server/runtime secrets. Всё, что попало в клиентский bundle, считается публичным: API base URL, feature flag, release id. Секреты, токены и пароли должны оставаться на backend/serverless/edge стороне, а frontend получает только безопасные публичные значения.
Что сказать на интервью (30-60 секунд)
Я всегда проговариваю границу: frontend env - это не защищённое хранилище. Например в Vite клиенту доступны только переменные с публичным префиксом вроде VITE_, и они подставляются в bundle на этапе build. Поэтому я валидирую обязательные env до сборки, явно типизирую их, не храню секреты в VITE_*, разделяю .env.production/.env.staging и проверяю итоговый bundle на случайные утечки.
Мини-пример
const apiUrl = import.meta.env.VITE_API_URL;
if (!apiUrl) {
throw new Error('VITE_API_URL is required');
}
Углубление (2-3 минуты)
- Build-time env подставляется при сборке. Если один и тот же artifact нужно деплоить в разные окружения, лучше отдавать runtime config отдельным JSON/endpoint, а не пересобирать bundle.
- Public prefix - это не защита, а фильтр экспорта в клиент. Значение
VITE_PUBLIC_KEYвсё равно видно пользователю. - Все значения приходят строками: boolean/number нужно явно парсить и валидировать, иначе
VITE_FLAG=falseможет работать как truthy string. .env.localи.env.*.localдолжны быть локальными и не попадать в репозиторий.- Env policy должна быть одинаковой для локальной разработки, preview и production, иначе появляются "у меня работает" из-за разных mode/NODE_ENV.
Практика
Разберите PR, где разработчик добавил VITE_BACKEND_TOKEN для обращения к стороннему API из браузера. Объясните, почему это утечка, куда перенести вызов, как назвать публичные переменные и какой guard добавить в CI.
Типичные ошибки
- Класть секреты в переменные с public-префиксом.
- Думать, что
.envв frontend защищён от пользователя. - Путать
modebundler-а иNODE_ENV. - Не валидировать env до build и получать белый экран уже после деплоя.
Follow-up вопросы
- Почему frontend env нельзя использовать для секретов?
- Когда нужен runtime config вместо build-time env?
- Чем отличается
modeотNODE_ENV? - Как проверить, что секрет не попал в bundle?
Что повторить
- Vite
import.meta.env, public prefixes,.env.[mode]. - Runtime config vs build-time replacement.
- Secret management, CI env validation, typed config.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
14. Что такое source maps и как использовать их в проде?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
Source maps связывают minified production stack trace с исходным кодом. В проде они полезны для error tracking, но их нельзя бездумно публиковать рядом с bundle: обычные maps могут раскрыть исходники, имена файлов, структуру проекта и комментарии. Чаще их загружают в error tracker или private storage и привязывают к release id.
Что сказать на интервью (30-60 секунд)
Я объясняю source maps как компромисс между observability и раскрытием деталей реализации. Для production я выбираю стратегию: не генерировать maps, генерировать hidden maps для error tracker или nosources maps, если нужна ограниченная диагностика. После build maps должны соответствовать конкретному release artifact, иначе stack trace будет мапиться на неправильную версию кода.
Мини-пример
// vite.config.js
export default {
build: {
sourcemap: 'hidden',
},
};
Углубление (2-3 минуты)
source-mapобычно создаёт отдельный.mapи ссылку из bundle; браузер может скачать map, если сервер её отдаёт.hidden-source-map/hiddenполезен, когда maps нужны error reporting tooling, но не browser devtools.nosourcesубирает исходный текст, но всё ещё может раскрывать имена файлов и структуру.- Maps должны загружаться в error tracker в той же release job, где публикуется artifact; иначе диагностика станет недостоверной.
- Source maps не должны попадать в CDN публично без явной политики доступа.
Практика
Опишите release pipeline для SPA: сборка создаёт assets, загружает hidden sourcemaps в error tracker, публикует bundle на CDN и сохраняет release id. Что проверите, если ошибка в проде всё равно показывает minified stack?
Типичные ошибки
- Публиковать
.mapв CDN без ограничения доступа. - Не связывать maps с release/version, из-за чего stack trace мапится на старый код.
- Включать inline maps в production bundle.
- Думать, что
nosourcesполностью скрывает структуру приложения.
Follow-up вопросы
- Чем отличаются
source-map,hidden-source-mapиnosources-source-map? - Почему source maps могут быть security risk?
- Как связать sourcemaps с релизом?
- Что делать, если error tracker показывает неправильные строки?
Что повторить
- Vite
build.sourcemap, Webpackdevtool. hidden,nosources, inline/source-map trade-offs.- Error tracking release artifacts and private sourcemap upload.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
15. Как ускорять CI сборки frontend-проекта?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
CI ускоряют не одним "кэшем", а разбором critical path: install, typecheck, lint, unit, build, e2e, upload artifacts. Дальше убирают лишнюю работу, кэшируют правильные слои, параллелят независимые проверки, запускают affected-only там, где это безопасно, и не жертвуют доверительным quality signal.
Что сказать на интервью (30-60 секунд)
Я сначала строю таймлайн pipeline и смотрю, что реально блокирует merge. Потом фиксирую deterministic install (npm ci/lockfile), кэширую package-manager store и build cache, разделяю independent jobs, убираю повторный build в разных шагах, а тяжёлые e2e выношу в отдельный gate или запускаю по affected areas. Ускорение принимаю только если flaky rate и defect escape не выросли.
Мини-пример
checks:
- install once with lockfile
- run lint, typecheck and unit tests in parallel
- build once and reuse artifact
- run e2e only for affected flows or before release
Углубление (2-3 минуты)
- Кэш должен быть привязан к lockfile, версии Node/package manager и config. Иначе можно получить быстрый, но недостоверный CI.
- Кэшировать
node_modulesчасто хуже, чем store package manager-а: он тяжелее, зависит от OS и сложнее инвалидируется. - Параллелизация помогает только независимым шагам. Если каждый job заново делает install/build, выигрыш съедается overhead.
- Affected-only checks требуют надёжной dependency graph. Если graph неточный, нужен nightly/full gate.
- Production build, typecheck и unit tests обычно должны оставаться PR gate. E2E можно делить на smoke для PR и полный набор для release/nightly.
Практика
В pipeline PR: install 4 минуты, lint 2, typecheck 5, unit 6, build 7, e2e 18. Предложите план ускорения до 10-12 минут без потери доверия: что параллелить, что кэшировать, какие артефакты переиспользовать и какой full gate оставить.
Типичные ошибки
- Ускорять CI отключением тестов вместо сегментации gate-ов.
- Кэшировать без ключей по lockfile/config и получать случайные сборки.
- Параллелить всё подряд, дублируя install/build в каждом job.
- Игнорировать flaky tests: быстрый CI без доверия всё равно тормозит команду.
Follow-up вопросы
- Что должно входить в PR gate, а что можно вынести в release/nightly?
- Почему кэш может сделать CI недостоверным?
- Как выбирать cache key для frontend pipeline?
- Как измерить, что ускорение не ухудшило качество релизов?
Что повторить
- CI critical path, cache key design, artifact reuse.
- Deterministic installs, lockfile policy, package-manager store cache.
- PR/release/nightly gates, flaky rate, affected-only checks.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
16. Как проектировать кэширование статических ассетов?
Теги: tooling, build, cache
Сложность: Middle/Senior
Короткий ответ
Статические ассеты кэшируют через разные политики для разных типов файлов: hashed JS/CSS/images можно отдавать с долгим max-age и immutable, а HTML/manifest/runtime config должны быстро revalidate или не кэшироваться надолго. Ключевой принцип: если URL меняется при изменении содержимого, файл можно кэшировать агрессивно; если URL стабильный, нужна осторожная инвалидация.
Что сказать на интервью (30-60 секунд)
Я разделяю assets на immutable и mutable. Всё, что собрал bundler с content hash, например app.8f3a.js, можно держать в CDN/browser cache долго. Но index.html, service worker, runtime config и preload manifest должны обновляться контролируемо, иначе пользователь получит новый HTML со старым JS или наоборот. Проверяю это через headers, deploy order, rollback-сценарий и smoke: после релиза hard refresh не должен быть единственным способом получить рабочую версию.
Мини-пример
# /assets/app.8f3a2c.js
Cache-Control: public, max-age=31536000, immutable
# /index.html
Cache-Control: no-cache
Углубление (2-3 минуты)
- Content hash решает инвалидацию: новый контент получает новый URL, старый файл может жить в cache до истечения TTL.
- HTML нельзя кэшировать как hashed asset, потому что именно он связывает актуальные chunk names, preload hints и runtime config.
- Public assets без hash требуют отдельной политики: либо короткий TTL, либо versioned path, либо ручная инвалидация CDN.
- Service worker добавляет второй слой cache; его update flow надо проверять отдельно, иначе stale shell переживёт несколько релизов.
- Rollback должен учитывать assets: старый HTML должен ссылаться на ещё доступные старые chunks, поэтому нельзя удалять previous release files мгновенно.
Практика
Составьте cache policy table для SPA: index.html, assets/*.js, assets/*.css, images, fonts, runtime-config.json, service worker. Для каждого укажите header, TTL, кто инвалидирует и что проверяется после деплоя.
Типичные ошибки
- Ставить
immutableнаindex.htmlили runtime config. - Долго кэшировать файлы из
public/без hash/version в URL. - Удалять старые chunks сразу после деплоя и получать
ChunkLoadErrorу пользователей со старым HTML. - Проверять только локальный build, не проверяя реальные response headers CDN.
Follow-up вопросы
- Почему hashed asset можно кэшировать дольше, чем HTML?
- Что сломается, если удалить старые chunks сразу после релиза?
- Чем
no-cacheотличается отno-store? - Как service worker влияет на стратегию кэширования?
Что повторить
Cache-Control,max-age,immutable,ETag.- Content hash, CDN invalidation, deploy order.
- Vite static assets,
publicdirectory,basepath.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
17. Как организовать release pipeline с quality gates?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
Release pipeline должен отделять PR confidence от production release safety. Минимальный набор gates: deterministic install, lint/typecheck, unit/integration tests, production build, bundle/security checks, smoke на preview/staging, artifact immutability, deploy/rollback criteria и наблюдаемость после выката.
Что сказать на интервью (30-60 секунд)
Я строю pipeline от риска, а не от списка модных проверок. PR gate должен быстро ловить ошибки разработчика: types, lint, unit, build. Release gate должен доказывать, что конкретный artifact можно выкатить: lockfile не менялся после проверки, assets собраны один раз, smoke проходит на preview, sourcemaps и release id привязаны, есть rollback на предыдущий artifact. После выката смотрю error rate, frontend exceptions, failed chunk loads и key user flows.
Мини-пример
release-gates:
pr: [install, lint, typecheck, unit, build]
predeploy: [audit-policy, bundle-budget, preview-smoke]
postdeploy: [error-rate, chunk-load-errors, key-flow-smoke]
Углубление (2-3 минуты)
- Artifact должен быть immutable: нельзя собирать заново на каждом окружении и надеяться, что получится тот же результат.
- Quality gates должны иметь owner и stop condition: что именно блокирует merge/release, а что уходит в warning с ticket.
- Smoke проверяет не "сайт открылся", а критический route, загрузку чанков, env config и базовую интеграцию.
- Rollback лучше тестировать до инцидента: предыдущий artifact, headers, sourcemaps и monitoring должны совпадать с release id.
- Слишком тяжёлые gates убивают lead time, поэтому полный e2e можно вынести в nightly/release, оставив PR smoke.
Практика
Нарисуйте gate matrix для frontend релиза: PR, merge to main, preview, production, postdeploy 30 min. Для каждого gate укажите проверку, threshold, кто принимает исключение и что является rollback trigger.
Типичные ошибки
- Пересобирать artifact перед production deploy вместо promotion уже проверенной сборки.
- Добавлять десятки checks без ясного stop condition и owner.
- Считать зелёный build достаточным, не проверяя preview/staging env и CDN headers.
- Не связывать release id, sourcemaps, monitoring и rollback.
Follow-up вопросы
- Какие gates должны блокировать PR, а какие production deploy?
- Почему immutable artifact важнее повторной сборки на каждом окружении?
- Какой frontend-сигнал может стать rollback trigger?
- Как ускорить pipeline, не потеряв confidence?
Что повторить
- PR/release/postdeploy gates, smoke tests, artifact promotion.
- Bundle budget, sourcemap upload, release id.
- Rollback criteria, error rate, chunk-load monitoring.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
18. Как обрабатывать security vulnerabilities в пакетах?
Теги: tooling, build
Сложность: Middle/Senior
Короткий ответ
Уязвимости в пакетах обрабатывают как risk triage, а не как слепой audit fix --force. Нужно понять severity, exploitability, direct/transitive путь, prod/dev impact, доступность patch, совместимость SemVer, временный mitigation и срок закрытия. Автоматический фикс полезен, но major update, override или fork должны проходить тесты и владельца риска.
Что сказать на интервью (30-60 секунд)
Я не отвечаю "просто запускаю audit fix". Сначала классифицирую: пакет попадает в production bundle или только в dev toolchain, есть ли реальный reachable path, какой advisory, какая версия исправляет, нужен ли breaking change. Потом выбираю действие: compatible update, direct dependency bump, overrides для transitive dependency, замена пакета, временное исключение с expiry или срочный release. После этого прогоняю lockfile diff, tests, build и smoke.
Мини-пример
{
"overrides": {
"vulnerable-transitive-package": "1.2.3"
}
}
Углубление (2-3 минуты)
npm auditможет блокировать CI поaudit-level, но policy должна различать prod dependency, dev dependency и false positive/неэксплуатируемый путь.audit fix --forceможет поставить SemVer-major и сломать приложение; его нельзя делать без changelog, tests и rollback.overridesпомогает зафиксировать transitive dependency, но это временный контроль, который надо удалить после upstream fix.- Важно читать lockfile diff: иногда "фикс" заменяет большой участок дерева зависимостей.
- Для critical/high нужен SLA и owner; для low/dev-only может быть ticket с дедлайном, если риск принят явно.
Практика
Разберите advisory на transitive dependency в dev server. Составьте triage note: package path, affected versions, prod/dev impact, reachable path, proposed fix, tests, rollback и срок удаления override.
Типичные ошибки
- Делать
audit fix --forceбез понимания breaking changes. - Блокировать релизы на low/dev-only advisory без triage policy.
- Игнорировать transitive path и не понимать, кто тянет уязвимый пакет.
- Оставлять
overridesнавсегда без owner и expiry.
Follow-up вопросы
- Чем отличается direct vulnerability от transitive vulnerability?
- Когда допустим
override, а когда лучше заменить пакет? - Почему severity без exploitability не всегда достаточна для блокировки?
- Как проверить, что security update не сломал production bundle?
Что повторить
npm audit,audit-level,npm audit fix --dry-run.overrides, lockfile diff, transitive dependency path.- Severity, exploitability, SLA, waiver/expiry policy.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
19. Какие anti-patterns бывают в конфигурации bundler?
Теги: tooling, build, bundlers
Сложность: Middle/Senior
Короткий ответ
Плохая bundler-конфигурация обычно маскирует архитектурные проблемы: хаотичные aliases, дублирующие loaders/plugins, разный module resolution в app/test/storybook, ручные chunks без измерений, широкая транспиляция node_modules, магические defines, скрытые polyfills и сильное расхождение dev/prod behavior.
Что сказать на интервью (30-60 секунд)
Я называю bundler config production-кодом: его надо ревьюить, тестировать и держать минимальным. Красные флаги - когда alias есть в Webpack/Vite, но нет в TypeScript и тестах; когда loader order непонятен; когда plugin добавлен "потому что помогло"; когда dev build и prod build используют разные env/polyfills; когда manual chunking меняют без bundle report. Хорошая правка начинается с измерения: build time, bundle composition, HMR behavior, sourcemaps и smoke.
Мини-пример
// smell: alias works in bundler, but TypeScript/tests may resolve differently
resolve: {
alias: {
'@': './src',
'@shared': '../shared/src',
},
}
Углубление (2-3 минуты)
- Alias должен быть синхронизирован между bundler,
tsconfig, test runner, lint/import resolver и IDE, иначе CI и local dev видят разные модули. - Loader/plugin order влияет на результат. Если команда не может объяснить порядок transforms, config стал риском.
- Ручной split chunks/manual chunks без bundle report может ухудшить cache и network waterfall.
- Широкая transpilation rule по всему
node_modulesзамедляет build; исключения должны быть точечными и документированными. define/env replacements могут случайно раскрыть секреты или сломать dead-code elimination.
Практика
Проведите smell review bundler config: выпишите aliases, plugins/loaders, env/define, manual chunks, sourcemap policy и exceptions для dependencies. Для каждого пункта укажите, зачем он нужен, чем валидируется и какой risk будет при удалении.
Типичные ошибки
- Добавлять plugin без ссылки на проблему, owner и проверку эффекта.
- Держать разные aliases в app, tests и Storybook.
- Оптимизировать chunks на глаз, без analyzer и real route metrics.
- Прятать compatibility hacks без TODO/expiry и regression test.
Follow-up вопросы
- Почему bundler alias должен совпадать с
tsconfig paths? - Как проверить, что plugin действительно нужен?
- Чем опасны manual chunks без измерений?
- Как найти расхождение dev/prod config?
Что повторить
- Module resolution, aliases, loaders/plugins order.
- Vite/Rollup plugins, Webpack rules,
define. - Bundle analyzer, build time, HMR and sourcemap checks.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
20. Как строить миграцию Webpack -> Vite без остановки разработки?
Теги: tooling, build, bundlers
Сложность: Middle/Senior
Короткий ответ
Миграцию Webpack -> Vite безопаснее строить как parity-проект: сначала описать, что именно делает текущий Webpack, затем поднять Vite рядом, сравнить dev/build/test/storybook/assets/env/CSS/SVG/workers/sourcemaps, прогнать один вертикальный slice и только после этого переключать команды. Big-bang миграция опасна, потому что bundler затрагивает почти все frontend workflows.
Что сказать на интервью (30-60 секунд)
Я начинаю с inventory: entry points, aliases, env, loaders, CSS modules, SVG handling, public path, dev proxy, HMR, tests, Storybook, sourcemaps, legacy browser support. Потом делаю Vite config рядом и добиваюсь build parity: одинаковые routes открываются, assets резолвятся, env не утекает, typecheck вынесен отдельным шагом, bundle report и smoke не хуже. Переключение делаю через короткий rollout с fallback на старый pipeline.
Мини-пример
Migration checklist:
1. inventory current Webpack responsibilities
2. create parallel Vite build
3. compare output, routes, assets, env, sourcemaps
4. migrate one vertical slice
5. switch CI only after smoke and rollback are proven
Углубление (2-3 минуты)
- Vite dev server работает иначе: source code обслуживается через native ESM/on-demand transforms, а production всё равно требует optimized bundle.
- Vite не делает TypeScript type checking внутри transform pipeline, поэтому
tsc --noEmitдолжен остаться отдельным gate. - Webpack loaders часто надо заменить на Vite/Rollup plugin или встроенную возможность; не всё переносится 1:1.
- Особое внимание:
process.envvsimport.meta.env,publicPathvsbase, SVG loaders, CSS preprocessors, monorepo aliases и legacy browser support. - Успешная миграция измеряется не только faster dev server, но и стабильностью CI, размером bundle, HMR behavior, sourcemap quality и числом rollback-free релизов.
Типичные ошибки
- Мигрировать "конфиг в конфиг", не описав реальные responsibilities старого Webpack.
- Забыть, что Vite build и dev имеют разные pipelines и edge cases.
- Потерять typecheck, потому что Vite быстро транспилирует TypeScript.
- Не проверить assets/base path и получить broken links только после деплоя.
- Удалить старый pipeline до доказанного rollback.
Follow-up вопросы
- Какие части Webpack config нельзя переносить механически?
- Почему typecheck должен остаться отдельным gate?
- Как проверить parity между Webpack и Vite build?
- Когда миграцию лучше остановить или откатить?
Что повторить
- Vite dev/build model, dependency pre-bundling, HMR.
- Webpack loaders/plugins, aliases, public path.
- Typecheck gate, assets/env parity, rollout/rollback plan.
Связанные модули и карта
- Обучение: Package Managers и Bundlers
- Обучение: Tooling и Delivery
- Карта подготовки: Package Managers
Куда дальше
- Вернитесь в модуль: Package Managers и Bundlers.
- Сверьтесь с картой темы: Package Managers.
- Продолжайте по маршруту: Middle трек.
- Закрепите один ответ на практике в Песочнице.