Перейти к основному содержимому

Package Managers и Bundlers

Экспресс-шпаргалка 20/20

  1. npm/pnpm/yarn различаются стратегией хранения зависимостей, скоростью установки и удобством в монорепах.
  2. Lockfile фиксирует точное дерево зависимостей и обеспечивает воспроизводимые сборки.
  3. npm ci ставит строго по lockfile, быстрее и стабильнее для CI, чем npm install.
  4. Semver определяет совместимость версий, но диапазоны могут неожиданно подтянуть breaking changes.
  5. peerDependencies декларируют совместимость с хост-пакетом; конфликты требуют выравнивания версий.
  6. Workspaces позволяют централизованно управлять зависимостями и пакетами в монорепозитории.
  7. Обновление зависимостей делается поэтапно с changelog-review, тестами и rollback планом.
  8. Vite обычно быстрее в dev, Webpack часто гибче в сложных enterprise-конфигах.
  9. Tree shaking работает при ESM, корректных imports и правильном sideEffects.
  10. Code splitting разбивает бандл на чанки и снижает стартовую загрузку JS.
  11. Анализ бандла делают через visualizer/stats, чтобы найти тяжёлые или дублирующиеся зависимости.
  12. HMR может ломаться из-за stateful side effects и нестабильных boundaries.
  13. Env-переменные в бандле нужно разделять на публичные и приватные, чтобы не утекали секреты.
  14. Source maps нужны для прод-диагностики, но доступ к ним надо ограничивать.
  15. CI ускоряют кэшированием, параллелизацией и уменьшением лишних шагов pipeline.
  16. Кэширование ассетов строится на content hash + immutable cache headers.
  17. Release pipeline включает quality gates: lint, typecheck, tests, build, smoke.
  18. Vulnerability management: регулярный аудит, patch policy и контроль транзитивных пакетов.
  19. Anti-patternы конфига: неуправляемые плагины, хаос alias/loaders и отсутствие budget.
  20. Миграция 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 минуты)

КритерийnpmpnpmYarn modern
Adoption baselineсамый простой стартнужен team agreementнужна настройка версии/режима
Install strategyhoisted node_modulescontent-addressable store + linksPnP или node_modules linker
Ghost dependenciesлегче случайно не заметитьстроже из-за layoutPnP явно ловит undeclared deps
Monorepoworkspaces естьсильный workspace workflowworkspaces + constraints
Migration riskминимальныйtooling может не любить symlinksPnP может требовать IDE/tool fixes

Кейс: монорепа с 20 пакетами долго ставит зависимости и периодически ловит "works on my machine", потому что пакет импортирует transitive dependency. Хороший ответ: сравнить package managers на branch, зафиксировать packageManager, lockfile, CI cache key, install command и проверить ghost dependency failures до миграции.

Практика

  1. Составьте package manager decision table для single app, monorepo, legacy enterprise app.
  2. Для каждого варианта укажите install command, lockfile, CI cache key и главный migration risk.
  3. Найдите один пример ghost dependency: импорт пакета, которого нет в dependencies текущего workspace.

Типичные ошибки

  1. Смешивают npm/pnpm/yarn lockfiles в одном репозитории.
  2. Не фиксируют packageManager и получают разные версии CLI у команды.
  3. Мигрируют package manager вместе с большим dependency upgrade.
  4. Игнорируют tooling, который не поддерживает symlinks или PnP.

Follow-up вопросы

  1. Что такое ghost dependency?
  2. Почему pnpm может сломать код, который работал на hoisted node_modules?
  3. Когда Yarn PnP не стоит включать сразу?
  4. Что должно попасть в migration checklist package manager?

Что повторить

  1. npm, pnpm, Yarn install strategies.
  2. packageManager, Corepack, workspaces, lockfiles.
  3. Ghost dependencies, hoisting, symlinks, PnP.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 в lockfileregenerate clean installручной merge легко ломает graph
CI использует frozen installfail при mismatchзащита reproducibility

Кейс: локально тесты проходят, CI падает после fresh install. В PR изменили package.json, но не lockfile. Сильный ответ: CI должен запускать frozen/clean install, падать на mismatch, а разработчик должен пересоздать lockfile тем же package manager и той же CLI-версией.

Практика

  1. Сделайте lockfile drift drill: изменили dependency range, lockfile не обновили, CI запускает frozen install.
  2. Опишите, какой command должен упасть: npm ci, pnpm install --frozen-lockfile или yarn install --immutable.
  3. Составьте checklist ревью lockfile diff: direct dependency, transitive dependency, integrity, peer resolution.

Типичные ошибки

  1. Добавляют lockfile в .gitignore для application repo.
  2. Ручками правят lockfile вместо regenerate через package manager.
  3. Не замечают, что lockfile diff обновил сотни transitive packages.
  4. Используют npm install в CI и случайно переписывают lockfile.

Follow-up вопросы

  1. Чем package.json отличается от lockfile?
  2. Почему lockfile надо коммитить для приложения?
  3. Как разбирать lockfile merge conflict?
  4. Почему один repo не должен держать несколько lockfiles без причины?

Что повторить

  1. package-lock.json, pnpm-lock.yaml, yarn.lock.
  2. Integrity hashes, resolved versions, peer resolution.
  3. Frozen/immutable installs in CI.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 installnpm 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.

Практика

  1. Разберите CI install failure: package.json и package-lock.json расходятся.
  2. Назовите правильный fix: локально npm install, commit lockfile, снова npm ci.
  3. Проверьте .npmrc: есть ли flags, которые меняют форму dependency tree.

Типичные ошибки

  1. Используют npm install в CI и получают непредсказуемый lockfile drift.
  2. Запускают npm ci без закоммиченного package-lock.json.
  3. Не коммитят .npmrc, хотя lockfile создан с важными install flags.
  4. Лечат peer conflict через --legacy-peer-deps без понимания причины.

Follow-up вопросы

  1. Почему npm ci удаляет node_modules?
  2. Что произойдет, если lockfile не совпадает с package.json?
  3. Когда локально нужен npm install, а не npm ci?
  4. Почему install flags должны быть одинаковыми?

Что повторить

  1. npm ci, npm install, lockfile mismatch.
  2. .npmrc, install flags, legacy-peer-deps, strict-peer-deps.
  3. CI cache: npm cache vs node_modules cache.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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.3patch updatesменьше риск, но fixes ограничены
^1.2.3minor + patch в рамках majorminor может сломать на практике
^0.2.3обычно patch внутри 0.2.x0.x API нестабилен
* / latestслишком широкопочти нет reproducibility без lockfile

Кейс: библиотека выпустила minor, формально backward-compatible, но поменяла поведение edge-case. При range ^ новый install без lockfile подтянул эту версию и сломал сборку. Хороший ответ: lockfile для приложений, Renovate/Dependabot PR с changelog и тестами, exact pin для критичных пакетов при необходимости.

Практика

  1. Составьте semver range risk matrix для ^, ~, exact, 0.x, latest.
  2. Для каждого range предскажите максимальную версию, которую может поставить resolver.
  3. Опишите dependency update policy: кто ревьюит changelog, какие тесты обязательны, когда pin оправдан.

Типичные ошибки

  1. Думают, что SemVer гарантирует отсутствие багов.
  2. Не учитывают, что 0.y.z может менять API агрессивнее.
  3. Используют latest в production dependencies.
  4. Обновляют большой набор packages одним PR без понятного rollback.

Follow-up вопросы

  1. Чем ^1.2.3 отличается от ~1.2.3?
  2. Почему lockfile не отменяет need review dependency updates?
  3. Что особенного в 0.x версиях?
  4. Когда стоит pin exact version?

Что повторить

  1. SemVer major/minor/patch and public API.
  2. npm version ranges: exact, ~, ^, x, latest.
  3. Dependency update policy and changelog review.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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-depsnpm игнорирует 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 после совместимости.

Практика

  1. Разберите peer dependency conflict walkthrough: app, plugin, host package, requested peer ranges.
  2. Выпишите три решения: upgrade host, upgrade plugin, replace package; отметьте риск каждого.
  3. Сформулируйте, когда peerDependenciesMeta.optional уместен.

Типичные ошибки

  1. Лечат конфликт флагом --force или --legacy-peer-deps без проверки runtime.
  2. Дублируют host dependency внутри библиотеки как обычную dependency.
  3. Слишком узко фиксируют peer range на patch version.
  4. Не понимают, кто является host package в конфликте.

Follow-up вопросы

  1. Почему React часто должен быть peer dependency в UI libraries?
  2. Чем peer dependency отличается от dependency?
  3. Что изменилось в npm v7+ для peerDependencies?
  4. Когда конфликт peer range лучше решать upgrade, а не override?

Что повторить

  1. peerDependencies, peerDependenciesMeta, host package.
  2. npm peer resolution, strict-peer-deps, legacy-peer-deps.
  3. Dependency graph diagnosis: npm explain, npm ls, package-manager warnings.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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.

Практика

  1. Составьте workspace boundary map: apps/web, packages/ui, packages/config, packages/api-client.
  2. Для каждого пакета укажите owner, public API, allowed imports и affected tests.
  3. Опишите CI: какие команды запускать для changed workspace и его dependents.

Типичные ошибки

  1. Делают deep imports из соседнего пакета вместо публичного entrypoint.
  2. Хранят dependency в root, хотя она нужна только одному workspace.
  3. Не проверяют dependents после изменения shared package.
  4. Не имеют стратегии versioning/publish для packages, которые уходят наружу.

Follow-up вопросы

  1. Чем workspace отличается от npm link?
  2. Что такое affected tests в monorepo?
  3. Почему deep imports опасны для shared package?
  4. Когда monorepo усложнит, а не ускорит delivery?

Что повторить

  1. npm/pnpm/Yarn workspaces, workspace filtering.
  2. Public package API, exports, dependency ownership.
  3. Affected graph, local linking, publish/versioning plan.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 PRchangelog, tests, lockfile size
Minorgrouped by ecosystemdeprecations, peer ranges
Majorотдельный migration PRbreaking changes, codemods
Security fixpriority pathexploitability, prod usage
Transitive overridetemporaryowner issue, removal date
Tooling updatestagedCI 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.

Практика

  1. Составьте dependency update rollout plan для React ecosystem, bundler plugins и security patch.
  2. Для каждого update укажите affected area, smoke test, rollback command и risk level.
  3. Разберите lockfile diff: direct update или transitive cascade.

Типичные ошибки

  1. Обновляют "все сразу" и теряют причинность поломки.
  2. Ревертят только package.json, забывая lockfile.
  3. Игнорируют peer dependency warnings после update.
  4. Делают permanent override без задачи на удаление.

Follow-up вопросы

  1. Как группировать dependency update PR?
  2. Чем direct dependency update отличается от transitive update?
  3. Когда override оправдан?
  4. Какие тесты нужны для major tooling update?

Что повторить

  1. Changelog review, lockfile diff, dependency grouping.
  2. Renovate/Dependabot style workflows.
  3. Overrides/resolutions, peer conflicts, rollback of package+lockfile.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 минуты)

КритерийViteWebpack
Dev startup/HMRобычно сильная стороназависит от config/cache
Legacy loadersможет потребовать заменызрелая экосистема loaders
Module Federationне core storyзрелая поддержка
Production bundlingRollup/Rolldown pathwebpack optimizer
Config complexityниже для modern appsвыше, но гибче
Migration riskparity gapsсохранить legacy проще

Кейс: SPA на Webpack стартует 40 секунд, но использует custom loader для CMS imports и Module Federation. Сильный ответ: не переписывать все сразу, а сделать proof-of-concept на одном entrypoint, измерить dev/build, проверить output parity и список несовместимых plugins.

Практика

  1. Составьте Vite/Webpack decision matrix для new SPA, legacy app, microfrontend shell, component library.
  2. Для миграции Webpack -> Vite запишите parity checklist: env, aliases, SVG/CSS, dynamic imports, source maps.
  3. Назовите rollback strategy, если production bundle изменил поведение.

Типичные ошибки

  1. Сравнивают только dev server speed и не проверяют production output.
  2. Переносят Webpack aliases/loaders "как есть" без parity tests.
  3. Игнорируют SSR/library/microfrontend requirements.
  4. Меняют bundler и одновременно обновляют framework/dependencies.

Follow-up вопросы

  1. Почему Vite быстрее в dev на больших проектах?
  2. Какие Webpack features сложнее заменить?
  3. Что такое build parity?
  4. Как измерить успешность bundler migration?

Что повторить

  1. Vite native ESM, dependency pre-bundling, production build.
  2. Webpack loaders/plugins, SplitChunks, Module Federation.
  3. Migration parity checklist and rollback plan.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 formatESM static exportsCommonJS dynamic require
Importsnamed/direct importsnamespace import whole library
sideEffectsточный список CSS/polyfillsfalse при наличии CSS imports
Barrel filesre-export onlyinitialization side effects
Minifierremoves unused codecannot prove purity
Build modeproduction optimizationdev mode не показатель

Кейс: UI library поставила "sideEffects": false, а CSS импортировался внутри компонентов. Production bundle выбросил CSS, компоненты стали без стилей. Сильный ответ: проверить bundle analyzer, production build, sideEffects list вроде ["**/*.css"], ESM entrypoints и direct imports.

Практика

  1. Составьте tree-shaking diagnosis checklist: module format, import style, sideEffects, analyzer diff, production-only bug.
  2. Для UI package решите, можно ли ставить "sideEffects": false или нужен список CSS files.
  3. Сравните bundle до/после direct import и объясните, какой модуль исчез.

Типичные ошибки

  1. Проверяют tree shaking в dev build.
  2. Ставят sideEffects: false и ломают CSS/polyfills.
  3. Импортируют весь namespace большой библиотеки без необходимости.
  4. Думают, что bundler удалит любой unused code в CommonJS.

Follow-up вопросы

  1. Почему ESM важен для tree shaking?
  2. Чем sideEffects отличается от usedExports?
  3. Почему CSS import - side effect?
  4. Как проверить, что optimization реально сработала?

Что повторить

  1. ESM, CommonJS, static analysis.
  2. sideEffects, CSS imports, pure annotations.
  3. Bundle analyzer and production build comparison.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 widgetUX fallback/jank
Vendor chunkстабильные dependenciesодин огромный vendor
Common chunkshared codeextra roundtrip/waterfall
Manual chunksконтроль cache/load orderbrittle 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, который все равно грузится на старте.

Практика

  1. Сделайте chunk strategy review для routes: home, dashboard, admin, editor.
  2. Для каждого route укажите initial chunks, async chunks, shared vendor и loading fallback.
  3. Опишите проверку после deploy: bundle analyzer, network waterfall, ChunkLoadError, LCP/INP.

Типичные ошибки

  1. Делят код слишком мелко и получают waterfall из десятков requests.
  2. Выносят весь vendor в огромный initial chunk.
  3. Не обрабатывают failed async chunk после deploy.
  4. Добавляют lazy loading без нормального loading/error state.

Follow-up вопросы

  1. Чем route-level split отличается от component-level split?
  2. Почему слишком много чанков может ухудшить performance?
  3. Что такое ChunkLoadError и как его пережить?
  4. Когда manual chunks полезны, а когда вредны?

Что повторить

  1. Dynamic import, route splitting, vendor/common chunks.
  2. Webpack SplitChunks, Vite/Rollup manual chunks.
  3. Network waterfall, preload/prefetch, chunk-load failure handling.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 минуты)

  1. Разделяйте raw, gzip/brotli и реально загружаемый critical path. Большой async-чунк админки не равен регрессии первого экрана.
  2. Смотрите composition по чанкам: route code, vendor, shared, CSS, assets. Проблема часто не в размере библиотеки, а в том, что она попала в общий chunk.
  3. Проверяйте дубли: две версии одной зависимости, ESM+CJS вариант одного пакета, повторные runtime helpers.
  4. Проверяйте импорт: import * as icons, moment с локалями, barrel-файлы, side effects в пакетах, неработающий tree shaking.
  5. Фиксируйте bundle budget в CI, но не превращайте его в слепой запрет: иногда рост оправдан фичей, тогда нужен осознанный budget change.

Практика

Возьмите PR, где добавили rich text editor на одну страницу. Объясните, почему его нельзя тащить в общий vendor, как выделить lazy chunk, какие метрики сравнить до/после и где поставить бюджет: на весь build или на initial route.

Типичные ошибки

  1. Смотрят только общий размер dist и не отделяют initial load от lazy routes.
  2. Лечат проблему manual chunks без понимания network waterfall и cache hit rate.
  3. Удаляют одну большую библиотеку, но оставляют дубль версии или импорт всего пакета через barrel.
  4. Сравнивают non-production build, где sourcemaps/dev helpers искажают картину.

Follow-up вопросы

  1. Как отличить допустимый рост bundle от регрессии?
  2. Почему manual chunks иногда ухудшают performance?
  3. Как найти зависимость, которая попала в initial bundle случайно?
  4. Что будете делать, если analyzer показывает дубль одной библиотеки?

Что повторить

  1. Bundle analyzer/stats, gzip/brotli, initial vs async chunks.
  2. Tree shaking, side effects, ESM/CJS, dynamic import.
  3. Webpack SplitChunks, Vite/Rollup manualChunks, route-based splitting.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 минуты)

  1. Boundary - это модуль, который может принять обновление сам или от прямой зависимости. Если его нет, update поднимается выше и часто заканчивается reload.
  2. Stateful side effects должны очищаться: WebSocket, timers, event listeners, global stores, mock servers. Иначе после нескольких hot updates появляются двойные подписки и stale state.
  3. В React/Vue/Svelte большую часть HMR берёт на себя интеграция framework, но она ожидает стабильную форму компонента. Смена hook order, default/named exports или смешивание runtime logic с module init может ломать refresh.
  4. HMR не доказывает корректность production build. Ошибка может быть только в dev-server path или только в production chunking/minification.

Практика

Представьте, что после изменения файла формы страница не перезагружается, но обработчик submit срабатывает два раза. Разберите причину: где создан listener, кто должен вызывать cleanup, когда лучше сделать invalidate, а когда исправить boundary.

Типичные ошибки

  1. Считать HMR обычной перезагрузкой и не думать про старое состояние модуля.
  2. Держать side effects на верхнем уровне файла без cleanup.
  3. Делать общий barrel, из-за которого непонятно, какой модуль должен принять update.
  4. Диагностировать production-баг только через dev-server/HMR.

Follow-up вопросы

  1. Что такое HMR boundary?
  2. Почему иногда нужен full reload вместо hot update?
  3. Как HMR связан с module-level side effects?
  4. Почему HMR может работать в dev, но production build всё равно падает?

Что повторить

  1. import.meta.hot.accept, dispose, invalidate.
  2. Framework Fast Refresh/HMR boundaries.
  3. Side effects, module cache, full reload fallback.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 минуты)

  1. Build-time env подставляется при сборке. Если один и тот же artifact нужно деплоить в разные окружения, лучше отдавать runtime config отдельным JSON/endpoint, а не пересобирать bundle.
  2. Public prefix - это не защита, а фильтр экспорта в клиент. Значение VITE_PUBLIC_KEY всё равно видно пользователю.
  3. Все значения приходят строками: boolean/number нужно явно парсить и валидировать, иначе VITE_FLAG=false может работать как truthy string.
  4. .env.local и .env.*.local должны быть локальными и не попадать в репозиторий.
  5. Env policy должна быть одинаковой для локальной разработки, preview и production, иначе появляются "у меня работает" из-за разных mode/NODE_ENV.

Практика

Разберите PR, где разработчик добавил VITE_BACKEND_TOKEN для обращения к стороннему API из браузера. Объясните, почему это утечка, куда перенести вызов, как назвать публичные переменные и какой guard добавить в CI.

Типичные ошибки

  1. Класть секреты в переменные с public-префиксом.
  2. Думать, что .env в frontend защищён от пользователя.
  3. Путать mode bundler-а и NODE_ENV.
  4. Не валидировать env до build и получать белый экран уже после деплоя.

Follow-up вопросы

  1. Почему frontend env нельзя использовать для секретов?
  2. Когда нужен runtime config вместо build-time env?
  3. Чем отличается mode от NODE_ENV?
  4. Как проверить, что секрет не попал в bundle?

Что повторить

  1. Vite import.meta.env, public prefixes, .env.[mode].
  2. Runtime config vs build-time replacement.
  3. Secret management, CI env validation, typed config.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 минуты)

  1. source-map обычно создаёт отдельный .map и ссылку из bundle; браузер может скачать map, если сервер её отдаёт.
  2. hidden-source-map/hidden полезен, когда maps нужны error reporting tooling, но не browser devtools.
  3. nosources убирает исходный текст, но всё ещё может раскрывать имена файлов и структуру.
  4. Maps должны загружаться в error tracker в той же release job, где публикуется artifact; иначе диагностика станет недостоверной.
  5. Source maps не должны попадать в CDN публично без явной политики доступа.

Практика

Опишите release pipeline для SPA: сборка создаёт assets, загружает hidden sourcemaps в error tracker, публикует bundle на CDN и сохраняет release id. Что проверите, если ошибка в проде всё равно показывает minified stack?

Типичные ошибки

  1. Публиковать .map в CDN без ограничения доступа.
  2. Не связывать maps с release/version, из-за чего stack trace мапится на старый код.
  3. Включать inline maps в production bundle.
  4. Думать, что nosources полностью скрывает структуру приложения.

Follow-up вопросы

  1. Чем отличаются source-map, hidden-source-map и nosources-source-map?
  2. Почему source maps могут быть security risk?
  3. Как связать sourcemaps с релизом?
  4. Что делать, если error tracker показывает неправильные строки?

Что повторить

  1. Vite build.sourcemap, Webpack devtool.
  2. hidden, nosources, inline/source-map trade-offs.
  3. Error tracking release artifacts and private sourcemap upload.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 минуты)

  1. Кэш должен быть привязан к lockfile, версии Node/package manager и config. Иначе можно получить быстрый, но недостоверный CI.
  2. Кэшировать node_modules часто хуже, чем store package manager-а: он тяжелее, зависит от OS и сложнее инвалидируется.
  3. Параллелизация помогает только независимым шагам. Если каждый job заново делает install/build, выигрыш съедается overhead.
  4. Affected-only checks требуют надёжной dependency graph. Если graph неточный, нужен nightly/full gate.
  5. 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 оставить.

Типичные ошибки

  1. Ускорять CI отключением тестов вместо сегментации gate-ов.
  2. Кэшировать без ключей по lockfile/config и получать случайные сборки.
  3. Параллелить всё подряд, дублируя install/build в каждом job.
  4. Игнорировать flaky tests: быстрый CI без доверия всё равно тормозит команду.

Follow-up вопросы

  1. Что должно входить в PR gate, а что можно вынести в release/nightly?
  2. Почему кэш может сделать CI недостоверным?
  3. Как выбирать cache key для frontend pipeline?
  4. Как измерить, что ускорение не ухудшило качество релизов?

Что повторить

  1. CI critical path, cache key design, artifact reuse.
  2. Deterministic installs, lockfile policy, package-manager store cache.
  3. PR/release/nightly gates, flaky rate, affected-only checks.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 минуты)

  1. Content hash решает инвалидацию: новый контент получает новый URL, старый файл может жить в cache до истечения TTL.
  2. HTML нельзя кэшировать как hashed asset, потому что именно он связывает актуальные chunk names, preload hints и runtime config.
  3. Public assets без hash требуют отдельной политики: либо короткий TTL, либо versioned path, либо ручная инвалидация CDN.
  4. Service worker добавляет второй слой cache; его update flow надо проверять отдельно, иначе stale shell переживёт несколько релизов.
  5. 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, кто инвалидирует и что проверяется после деплоя.

Типичные ошибки

  1. Ставить immutable на index.html или runtime config.
  2. Долго кэшировать файлы из public/ без hash/version в URL.
  3. Удалять старые chunks сразу после деплоя и получать ChunkLoadError у пользователей со старым HTML.
  4. Проверять только локальный build, не проверяя реальные response headers CDN.

Follow-up вопросы

  1. Почему hashed asset можно кэшировать дольше, чем HTML?
  2. Что сломается, если удалить старые chunks сразу после релиза?
  3. Чем no-cache отличается от no-store?
  4. Как service worker влияет на стратегию кэширования?

Что повторить

  1. Cache-Control, max-age, immutable, ETag.
  2. Content hash, CDN invalidation, deploy order.
  3. Vite static assets, public directory, base path.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 минуты)

  1. Artifact должен быть immutable: нельзя собирать заново на каждом окружении и надеяться, что получится тот же результат.
  2. Quality gates должны иметь owner и stop condition: что именно блокирует merge/release, а что уходит в warning с ticket.
  3. Smoke проверяет не "сайт открылся", а критический route, загрузку чанков, env config и базовую интеграцию.
  4. Rollback лучше тестировать до инцидента: предыдущий artifact, headers, sourcemaps и monitoring должны совпадать с release id.
  5. Слишком тяжёлые gates убивают lead time, поэтому полный e2e можно вынести в nightly/release, оставив PR smoke.

Практика

Нарисуйте gate matrix для frontend релиза: PR, merge to main, preview, production, postdeploy 30 min. Для каждого gate укажите проверку, threshold, кто принимает исключение и что является rollback trigger.

Типичные ошибки

  1. Пересобирать artifact перед production deploy вместо promotion уже проверенной сборки.
  2. Добавлять десятки checks без ясного stop condition и owner.
  3. Считать зелёный build достаточным, не проверяя preview/staging env и CDN headers.
  4. Не связывать release id, sourcemaps, monitoring и rollback.

Follow-up вопросы

  1. Какие gates должны блокировать PR, а какие production deploy?
  2. Почему immutable artifact важнее повторной сборки на каждом окружении?
  3. Какой frontend-сигнал может стать rollback trigger?
  4. Как ускорить pipeline, не потеряв confidence?

Что повторить

  1. PR/release/postdeploy gates, smoke tests, artifact promotion.
  2. Bundle budget, sourcemap upload, release id.
  3. Rollback criteria, error rate, chunk-load monitoring.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 минуты)

  1. npm audit может блокировать CI по audit-level, но policy должна различать prod dependency, dev dependency и false positive/неэксплуатируемый путь.
  2. audit fix --force может поставить SemVer-major и сломать приложение; его нельзя делать без changelog, tests и rollback.
  3. overrides помогает зафиксировать transitive dependency, но это временный контроль, который надо удалить после upstream fix.
  4. Важно читать lockfile diff: иногда "фикс" заменяет большой участок дерева зависимостей.
  5. Для 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.

Типичные ошибки

  1. Делать audit fix --force без понимания breaking changes.
  2. Блокировать релизы на low/dev-only advisory без triage policy.
  3. Игнорировать transitive path и не понимать, кто тянет уязвимый пакет.
  4. Оставлять overrides навсегда без owner и expiry.

Follow-up вопросы

  1. Чем отличается direct vulnerability от transitive vulnerability?
  2. Когда допустим override, а когда лучше заменить пакет?
  3. Почему severity без exploitability не всегда достаточна для блокировки?
  4. Как проверить, что security update не сломал production bundle?

Что повторить

  1. npm audit, audit-level, npm audit fix --dry-run.
  2. overrides, lockfile diff, transitive dependency path.
  3. Severity, exploitability, SLA, waiver/expiry policy.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 минуты)

  1. Alias должен быть синхронизирован между bundler, tsconfig, test runner, lint/import resolver и IDE, иначе CI и local dev видят разные модули.
  2. Loader/plugin order влияет на результат. Если команда не может объяснить порядок transforms, config стал риском.
  3. Ручной split chunks/manual chunks без bundle report может ухудшить cache и network waterfall.
  4. Широкая transpilation rule по всему node_modules замедляет build; исключения должны быть точечными и документированными.
  5. define/env replacements могут случайно раскрыть секреты или сломать dead-code elimination.

Практика

Проведите smell review bundler config: выпишите aliases, plugins/loaders, env/define, manual chunks, sourcemap policy и exceptions для dependencies. Для каждого пункта укажите, зачем он нужен, чем валидируется и какой risk будет при удалении.

Типичные ошибки

  1. Добавлять plugin без ссылки на проблему, owner и проверку эффекта.
  2. Держать разные aliases в app, tests и Storybook.
  3. Оптимизировать chunks на глаз, без analyzer и real route metrics.
  4. Прятать compatibility hacks без TODO/expiry и regression test.

Follow-up вопросы

  1. Почему bundler alias должен совпадать с tsconfig paths?
  2. Как проверить, что plugin действительно нужен?
  3. Чем опасны manual chunks без измерений?
  4. Как найти расхождение dev/prod config?

Что повторить

  1. Module resolution, aliases, loaders/plugins order.
  2. Vite/Rollup plugins, Webpack rules, define.
  3. Bundle analyzer, build time, HMR and sourcemap checks.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: 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 минуты)

  1. Vite dev server работает иначе: source code обслуживается через native ESM/on-demand transforms, а production всё равно требует optimized bundle.
  2. Vite не делает TypeScript type checking внутри transform pipeline, поэтому tsc --noEmit должен остаться отдельным gate.
  3. Webpack loaders часто надо заменить на Vite/Rollup plugin или встроенную возможность; не всё переносится 1:1.
  4. Особое внимание: process.env vs import.meta.env, publicPath vs base, SVG loaders, CSS preprocessors, monorepo aliases и legacy browser support.
  5. Успешная миграция измеряется не только faster dev server, но и стабильностью CI, размером bundle, HMR behavior, sourcemap quality и числом rollback-free релизов.

Типичные ошибки

  1. Мигрировать "конфиг в конфиг", не описав реальные responsibilities старого Webpack.
  2. Забыть, что Vite build и dev имеют разные pipelines и edge cases.
  3. Потерять typecheck, потому что Vite быстро транспилирует TypeScript.
  4. Не проверить assets/base path и получить broken links только после деплоя.
  5. Удалить старый pipeline до доказанного rollback.

Follow-up вопросы

  1. Какие части Webpack config нельзя переносить механически?
  2. Почему typecheck должен остаться отдельным gate?
  3. Как проверить parity между Webpack и Vite build?
  4. Когда миграцию лучше остановить или откатить?

Что повторить

  1. Vite dev/build model, dependency pre-bundling, HMR.
  2. Webpack loaders/plugins, aliases, public path.
  3. Typecheck gate, assets/env parity, rollout/rollback plan.

Связанные модули и карта

  1. Обучение: Package Managers и Bundlers
  2. Обучение: Tooling и Delivery
  3. Карта подготовки: Package Managers

Куда дальше

  1. Вернитесь в модуль: Package Managers и Bundlers.
  2. Сверьтесь с картой темы: Package Managers.
  3. Продолжайте по маршруту: Middle трек.
  4. Закрепите один ответ на практике в Песочнице.