Skip to content
EN

Сколько зависимостей у пакета и проекта на npm: четыре выборки и живые замеры

Кратко. От 49 до 56% пакетов npm не имеют зависимостей вообще, а медиана по реестру — ноль.

От 49 до 56% пакетов npm не имеют зависимостей вообще, а медиана по реестру — ноль. Так показали четыре независимые выборки, включая нашу контрольную. Среднее с апреля 2018 года не сдвинулось, хотя реестр вырос в 6,4 раза.

И вторая неожиданность: у typescript в дереве зависимостей 21 пакет, а устанавливается два. На этой разнице между «в замке́» и «на диске» построена половина пугающих цифр в теме.

Проверить заголовки сайта →

Половина пакетов npm не имеет зависимостей вообще

Это первое, что стоит знать, и это противоречит расхожему образу. Мы отобрали случайные пакеты через реплику реестра и посмотрели поле зависимостей у последней версии каждого. Четыре независимых выборки:

ВыборкаПакетовБез зависимостейСреднее прямыхМедиана
161549,1%3,011
293650,3%3,020
31 07752,3%2,530
4 (контрольная)4556%2,000

Четыре набора, взятые независимо, дают 49–56% пакетов вовсе без зависимостей и медиану ноль. Среднее около двух-трёх — но среднее здесь малоинформативно, потому что распределение крайне скошено: p90 равен семи, p99 — двадцати семи, а максимум в выборках доходил до 99 и 367.

И зависимостей не становится больше. В апрельском срезе 2018 года среднее число прямых зависимостей составляло 2,8. Реестр с тех пор вырос в 6,4 раза, а среднее осталось там же. Разработчики объявляют не больше, чем раньше.

Оговорки: выборка берётся по алфавитным префиксам, а не строго равновероятно, поэтому соседние по алфавиту имена — часто из одного спам-семейства — представлены избыточно. Считаются только dependencies, без разработческих и необязательных.

Данные исследования

Исходные данные всех таблиц этого отчёта доступны в виде открытого CSV-файла (UTF-8, первая строка — заголовки).

Скачать датасет (CSV)

Число в замке́ зависимостей завышает установку

Это ловушка, на которой строится половина страшных цифр. Мы разрешили деревья через npm install --package-lock-only и сравнили с тем, что реально попадает на диск:

ПакетСвоих прямыхВ деревеУстановленоНа диске
react 19.2011меньше 1 МБ
typescript 7.020 (все необязательные)21230 МБ
vite 8.25401628 МБ
next 16.365424327 МБ
express 5.22868683 МБ
jest 30.4432218243 МБ
react-scripts 5.0481 3111 292292 МБ

У TypeScript в дереве 21 пакет, а ставится 2 — завышение в десять с половиной раз. Причина: современные инструменты публикуют по одному бинарнику на платформу как необязательные зависимости, а материализуется ровно один. Утверждение «у TypeScript теперь двадцать зависимостей» неверно: на вашей машине у него одна.

И число пакетов не связано с местом на диске. next ставит 24 пакета и занимает 327 МБ; react-scripts ставит 1 292 и занимает 292 МБ. В пятьдесят четыре раза больше пакетов — и меньше места.

Сколько тянет настоящий каркас

Мы собрали типовые проекты с нуля. Числа зависят от выбранных ключей, и это само по себе показательно:

КаркасОбъявленоВ деревеУстановленоНа диске
Next.js + React + TS + ESLint + Tailwind10–11404348450 МБ
Тот же с другим набором ключей11423479 МБ
Vite + React + TS + ESLint8–913411075 МБ

Разброс между двумя сборками одного каркаса — 348 против 423 — напоминание, что «сколько зависимостей у Next.js» вопрос без единственного ответа: он зависит от того, какие опции вы выбрали при создании проекта.

Самое важное в этой таблице — не абсолютные числа, а состав. 87% дерева Next.js — это разработческие зависимости, то есть инструментарий сборки, который в продакшен не едет. Глубина распределена так: на первом уровне 10 пакетов, на втором 61, на третьем 118, на четвёртом 107, дальше по убывающей до девятого. Средняя глубина 3,78, медианная 4.

Академический замер по срезу сентября 2023 года независимо пришёл к тому же: уязвимые пакеты чаще всего лежат на четвёртом-шестом уровне.

Рассказ про растущее раздувание не выдерживает проверки

Мы разрешили деревья исторических версий тех же пакетов:

ПакетПикСейчасИзменение
webpack395 в версии 3.12 (май 2018)68−83%
react-scripts1 971 в версии 2.0 (сентябрь 2018)1 311−33%
express86 в версии 5.0 (сентябрь 2024)68−21%

Каждый достиг пика и с тех пор уменьшился. Современный каркас на Vite со своими 134 пакетами примерно вдесятеро легче сборки эпохи react-scripts.

Формулировка, которую данные поддерживают: «пик пройден в 2018–2020 годах, дальше консолидация на меньшем числе более крупных платформенных бинарников». Формулировку «раздувание всё хуже» данные не поддерживают.

Опубликованные цифры и их настоящие даты

ЦифраЗначениеДата среза
Среднее прямых зависимостей2,8апрель 2018
Среднее транзитивныхоколо 80апрель 2018
Медиана: прямых / транзитивных2 / 21январь 2017
Доля транзитивных рёбер графа94,7%январь 2020
Пакетов хотя бы с одной зависимостью61,3%сентябрь 2023

Самая свежая из них — трёхлетней давности. Ни одного исследования графа зависимостей на срезе новее сентября 2023 года не существует, при том что реестр с апреля 2018-го вырос в 6,4 раза.

И критическая оговорка к цифре 94,7%: она посчитана только по рантайм-зависимостям, авторы прямо пишут, что разработческие и необязательные исключены. Поскольку в реальном дереве приложения разработческие составляют 87%, все такие оценки занижают то, чему разработчик доверяет на самом деле, почти на порядок.

Три цифры, которые пересказывают неверно

«Средний проект npm тянет 79 транзитивных зависимостей». Здесь две ошибки сразу. Во-первых, это апрель 2018 года, а не 2025-й и не 2026-й. Во-вторых, 79 относится не к проекту, а к пакету: столько сторонних пакетов вы неявно начинаете доверять, устанавливая один средний пакет. Там же фигурирует и вторая величина, о которой почти не говорят, — 39 сопровождающих, то есть людей, которые могут этот код изменить. Её с 2018 года никто не перемерял.

«Проект на Next.js начинается с тысячи зависимостей». По нашему замеру — 404 в дереве и 348 установленных. Тысяча с лишним описывает эпоху react-scripts, снятого с поддержки в 2025 году.

«95% уязвимостей приходится на транзитивные зависимости». К npm это не прослеживается: ближайшее корректно измеренное — 94,7% рёбер графа зависимостей на срезе января 2020 года, а это совсем другая величина. Подробнее — в разборе цепочки поставок.

Что делать

  1. Считайте установленное, а не строки в замке́. Разница у TypeScript — в десять раз. npm ls --all показывает то, что реально на диске.
  2. Смотрите на разработческие зависимости отдельно. Они дают 87% дерева и ноль в продакшене. Установка без них меняет картину радикально — и именно эта величина важна для оценки поверхности атаки в работающем приложении.
  3. Считайте области имён, а не только пакеты. Число независимых сторон, которым вы доверяете, ближе к числу областей, чем к числу пакетов.
  4. Не переносите цифры между «объявлено», «в дереве» и «установлено». Это три разные величины, и подмена одной другой — источник большинства пугающих заголовков в этой теме.

Источники: Zimmermann и др., USENIX Security 2019, Zerouali и др., EMSE 2022, Robinson и др., ASIA CCS 2026, реплика реестра npm.

Больше по теме

Часто задаваемые вопросы

Почему медиана так высокая?

Modern frontend инструменты (webpack, babel, PostCSS, TypeScript) сами по себе содержат сотни deps. Plus React + routing + state + forms + i18n.

Что делает npm audit?

Checks lockfile versions против GitHub Advisory Database. Reports vulnerable packages + recommended upgrades.

pnpm/bun решают?

pnpm — content-addressable storage → shared across projects (save disk). Bun — Rust-based faster. Оба сохраняют npm semver.

Как reduce?

depcheck для unused. Замените небольшие deps на native API (fetch, Date). Consider Deno (std library > npm).

Запустить инструмент, который описан в этой статье

Бесплатный тариф — 10 мониторов, проверки каждые 5 мин, без карты. Платные тарифы — интервал от 1 минуты и проверки из нескольких регионов.