DEV Community

Даниил Пронин
Даниил Пронин

Posted on • Edited on

Почему не надо коммитить папку node_modules

English | Русский

Эта заметка — ответы на вопросы, которые могут возникнуть при чтении статьи Джека Франклина в переводе For Web.

Нет нужды в npm install

Если вы храните node_modules в git, вам не нужно отдельно устанавливать зависимости, чтобы запустить проект.

  1. git плохо работает с большим количеством файлов в репозитории. Поиск фразой “git performance many files” показывает много полезной информации об этом. Например, вот: Just as git does not scale well with large files, it can also become painful to work with when you have a large number of files

  2. Некоторые пакеты зависят от платформы. Например, инструменты для разработки, такие как dart-sass.

  3. Если вы закоммитили node_modules, это значит, что у любого разработчика теперь есть лёгкий способ что-либо исправить в любой из зависимостей (это называется “monkey patching”), и это определённо точно приведёт к тому, что после обновления этой “исправленной” зависимости прошлое исправление будет перетёрто, и с этой проблемой придётся как-то разбираться. Вы вообще не будете уверены в том, что зависимость определённой версии соответствует тому виду, в котором она была изначально получена.

Это полезно не только для локальной разработки, но и для ботов на CI-платформах: они могут полностью пропустить шаг установки зависимостей.

CI обычно настраивают так, чтобы зависимости кешировались, и не устанавливались каждый раз с нуля. Гуглить фразой “ci node_modules cache”.

Гарантированно воспроизводимые сборки

Хранение node_modules в git гарантирует, что два разработчика запускают один и тот же код с одним и тем же набором зависимостей.

Эту проблему решает “лок-файл” — специальный файл, который нужно коммитить, в который пакетный менеджер (NPM/PNPM/Yarn) записывает о каждой скачанной зависимости все необходимые данные для гарантии воспроизводимой сборки.

Если заглянуть в yarn.lock, можно увидеть следующее:

"@apideck/better-ajv-errors@^0.2.4":
  version "0.2.5"
  resolved "https://registry.yarnpkg.com/@apideck/better-ajv-errors/-/better-ajv-errors-0.2.5.tgz#b9c0092b7f7f23c356a0a31600334f7b8958458b"
  integrity sha512-Pm1fAqCT8OEfBVLddU3fWZ/URWpGGhkvlsBIgn9Y2jJlcNumo0gNzPsQswDJTiA8HcKpCjOhWQOgkA9kXR4Ghg==
  dependencies:
    json-schema "^0.3.0"
    jsonpointer "^4.1.0"
    leven "^3.1.0"
Enter fullscreen mode Exit fullscreen mode

Yarn заботливо записал, что он скачал пакет @apideck/better-ajv-errors:

  • версии 0.2.5
  • по адресу resolved (прямая ссылка на .tgz)
  • хеш-сумма файла была sha512-Pm1fAqCT8OE...
  • и у этого пакета было 3 зависимости

И так для каждой зависимости, размещённой в папке node_modules. И в следующий раз, когда в папке проекта будет запущена команда yarn install, зависимости будут скачаны не тех версий, что указаны в package.json, а те, что записаны в yarn.lock. Следовательно, у всей команды и на CI независимо от платформы (Linux/macOS/Windows) будет один и тот же набор файлов, один и тот же код, с одними и теми же хеш-суммами.

Да, этого можно добиться, используя локфайлы или другие инструменты, однако я встречал случаи, когда они не спасали от изменения минорной версии какой-либо зависимости.

Эту ошибку часто допускают, когда при развёртывании проекта у разработчика запускается npm install, который устанавливает пакеты по информации из package.json, а не package-lock.json. Чтобы установить пакеты из лок-файла, нужно запускать npm ci.

Лучшая осведомлённость о поставляемом коде

Я был удивлён тем, насколько я стал осведомлённее касательно зависимостей, когда стал видеть в диффе git весь код, попадающий в проект при их подключении. Это побудило нас поработать над инструментами, помогающими оценить влияние зависимостей на размер бандла и оптимизировать занимаемое на диске место.

При выборе зависимостей можно пользоваться специальными инструментами, а не просто читать километры кода.

  • Bundlephobia

    Покажет, сколько весит зависимость, сколько будет с GZIP, как долго она будет скачиваться по медленному 3G и по среднему 4G-интернету, покажет в процентном соотношении состав под-зависимостей, что зависимость экспортирует (если она написана на ES Modules), а также какие у неё есть альтернативы или соседние пакеты. Вот пример.

  • bundlejs.com

    Покажет, сколько точно в килобайтах будет добавлено кода при импорте вроде

    import { map } from "nanostores"
    

    Посмотрите это на примере nanostores.

  • npm.anvaka.com

    Покажет график всех зависимостей в виде 2D или 3D-графика. Посмотрите на примере Vue 3

Более вдумчивое добавление зависимостей, потому что они больше не скрыты

Я уже упоминал, что шум в диффах воспринимается людьми как недостаток хранения зависимостей в git, и я признаю, что это действительно может быть проблемой. Однако для меня этот шум оказался полезным знаком. Раньше я часто подключал новые зависимости, просто потому что не хотел сам писать несколько строчек кода. Теперь же я гораздо внимательнее отношусь к добавлению новых зависимостей, потому что я могу увидеть добавленный ими код и решить, стоит ли он того.

Вы можете прочитать код и до того, как добавить зависимость в проект. Например, зайдя в репозиторий на GitHub. Крайне советую хотя бы мельком смотреть зависимости, адекватность кода, количество открытых issue и дату последнего коммита.

Примечание: это не означает, что у нас нет зависимостей! Бывают ситуации, когда подключить зависимость выгодно, но видимость кода в системе контроля версий сделала меня более осознанным — цена добавления зависимостей больше не скрыта.

Она никогда и не была скрыта.

C большими диффами можно справиться

Нельзя игнорировать тот факт, что добавление или обновление зависимостей может вызвать много шума в диффе. Одна из наших зависимостей — TypeScript, и каждое его обновление приносит огромный дифф в git, который, честно говоря, не стоит просмотра (за пределами CHANGELOG). Мы пришли к правилу, которое помогает в таких ситуациях: изменения в node_modules должны быть отделены от любых изменений в основной кодовой базе. Так что если я обновлю node_modules/typescript до свежей версии, наш инструментарий предупредит меня при обнаружении изменений за пределами node_modules.

Вы пришли к костылю.

Это правило помогает нам в большинстве случаев, потому что любая задача, требующая добавления или обновления зависимостей, может быть разделена на две части:

  1. Обновление или добавление зависимости
  2. Использование зависимости в коде

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

И вот последствия хождения на костылях.

Защита от очередного left_pad

Печально известный инцидент с left_pad (популярный npm-пакет был внезапно удалён, ломая сборки проектов по всему миру) не затронул бы команду, которая хранит зависимости в git. В долгосрочной перспективе команде всё ещё пришлось бы разбираться с вопросом «что теперь делать с более не поддерживаемой зависимостью», но в краткосрочной перспективе их сборки бы не сломались, а релизы бы не были заблокированы.

Я помню тот день, когда left_pad был удалён с NPM. Я тогда работал в digital-агентстве на потоке сайтов, и, конечно же, во всех проектах, за которые я был ответственен, left_pad был под-зависимостью. Мы решили эту проблему тогда примерно за полчаса, когда CI показал 404 при попытке скачать пакет. Я уже не помню, что именно мы предприняли, но такая задача не должна быть вызовом и поводом городить костыли.

В конце концов, для защиты от именно таких проблем, вы можете поднять у себя прокси-реестр, например, с помощью Verdaccio. Он будет хранить у себя все копии всех скачанных пакетов.

Top comments (1)

Collapse
 
grawl profile image
Даниил Пронин

скопировал эту заметку из своего gist gist.github.com/Grawl/2bfce69521ae...