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

Взаимодействовать с ними просто и легко с помощью нескольких команд CLI. То, что они делают, довольно просто, загружает пакеты для вас в соответствии с конкретной версией. Левый логотип принадлежит bower, а правый, конечно, npm.

Опыт Bower и npm

Пользуюсь bower уже довольно давно (более 1 года), так как работаю с фронтендом и опыт работы с ним не совсем приятный. На самом деле я думаю, что ненавижу это. С другой стороны, у нас есть npm менеджер пакетов, который в основном используется разработчиками node.js (бэкэнд/внешний интерфейс). Я широко использовал оба, и мне не нравятся оба из них, но я думаю, что NPM хуже, чем Bower.

Более или менее мое взаимодействие с этими пакетными менеджерами одинаково, у них даже есть одни и те же команды, которые иногда приводят к разным результатам. Например, установка пакета (наиболее часто используемая функция) в Bower устанавливается в bower_components (по умолчанию), а в npm — в node_modules. Хорошо, другое имя, кого это волнует. Ну, хитрость внутри каждого каталога. Bower учитывает все зависимости с плоской структурой, а npm, напротив, учитывает все зависимости с иерархической структурой. Это означает, что если вам нужна одна зависимость, в каталоге вашего проекта node_module у вас будет только эта зависимость, но внутри него может быть другой каталог node_modules, который содержит зависимости вашей зависимости, и так далее… Возможно, это неплохая идея, так как вы изолируете зависимости, и это спасает вас от неприятностей. Ну, это до узла 4.x.x, начиная с версии 5.x.x они также используют плоскую структуру.

Будьте осторожны, вложенные каталоги пользователей Windows болезненны…

Версии

В проекте, над которым я работал, предполагалось использовать семантическое версионирование, но не нормальное, а такое, которое используется при разработке, т.е. 0.Y.Z. Однако мы используем это X.Y.beta-Z, где Z автоматически увеличивается при выпуске. В SemVer указано, что библиотека должна иметь общедоступный API, который является интерфейсом библиотеки. Это не должно меняться так часто, верно??

И bower, и npm имеют одни и те же странные правила для разрешения версий, что позволяет разными способами определять ваши зависимости. Я действительно не знаю, почему кому-то может понадобиться больше, чем 1 или 2 способа разрешения версий зависимостей проекта, но неважно.

Две самые нелепые версии: ^ и ~

Сложно, вам не кажется? Жди следующего…

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

Проблема

Если вы работаете над небольшим проектом, вероятно, вы не столкнетесь с какими-либо проблемами при использовании приведенных выше правил разрешения версий, но если вы работаете над очень большим проектом с большим количеством зависимостей между несколькими командами, все становится сложнее. Вас всегда интересуют версии, которые вы используете. Это x.y.z или x.y1.z2? Вам нужно выполнить поиск внутри каждого пакета, чтобы убедиться, что вы используете правильную версию, а не просто искать единственное место, где должна быть правда, т.е. bower.json или package.json.

По сути, вы поддерживаете непоследовательную систему

Под непоследовательной системой я подразумеваю то, что зависимости могут меняться изо дня в день без вашего ведома. Это очень вероятно, когда у вас есть автоматические сборки CI. Итак, вы добровольно создаете новую систему каждый раз, когда ваши зависимости обновляются, даже не подозревая об этом. В каком-то смысле вы создали живую систему, которая развивается автоматически. Я не знаю, хорошо ли это в конце концов.

Пример

У вас есть система, которая содержит:

  • lib-A.js
  • lib-B.js
  • ext-lib-C.js
  • ext-lib-D.js

Вы поддерживаете lib-A.js и lib-B.js во всех своих командах, и они получают обновляется постоянно, т.е. пару минорных версий в неделю. ext-lib-C и D являются внешними и обновляются не так часто. Затем вы используете A и B в своем продукте, используя семантику xy*, таким образом, вы устанавливаете незначительные изменения (которые в этом случае может включать новые функции).

Итак, у вас есть система, включающая библиотеки, в которых неясно, что они содержат, и неясно, работают ли они правильно или нет. JavaScript — это динамический язык, и трудно понять, что происходит, если вы не запустите свой код. Добавьте к этому отсутствие интеграционных тестов, функциональных тестов (или даже E2E-тестов), и вы получите идеальный рецепт несогласованности и хрупкости системы.

Заслуга (и)

  • Вам не нужно заботиться об обновлениях, они выполняются «автоматически».

Других заслуг я себе не представляю...

Проблемы

  • Вы не знаете, что установлено, поэтому неясно, что проверять.
  • У вас могут быть проблемы, которые трудно найти, поскольку у вас нет хорошей ссылки на то, что установлено.

Вывод

Переменное управление версиями пакетов затрудняет поддержку и разработку системы, поскольку она может сломаться в любой момент без вашего ведома. Чем больше становится проект, тем сложнее будет поддерживать его и быть уверенным, что что-то не сломается. В общем, будьте проще, используйте определенные версии пакетов, и все станет намного проще.