Фундаментальна проблема пакетних менеджерів для мов програмування

Чому існує так багато різних пакетних менеджерів? Їх можна зустріти як у багатьох операційних системах (apt, yum, pacman, Homebrew), так і працюючи з багатьма мовами програмування (Bundler, Cabal, Composer, CPAN, CRAN, CTAN, EasyInstall, Go Get, Maven, npm, NuGet, OPAM, PEAR, pip, RubyGems, і т. д. і т. п.). «Кожна мова програмування потребу у власному пакетному менеджері, це вже стало загальновизнаною істиною». Що за дивне тяжіння змушує мови програмування, один за іншим, скочуватися в цей обрив? Чому б нам просто не використовувати вже існуючі пакетні менеджери?


У вас, ймовірно, вже є деякі припущення, чому використання apt для управління пакетами Ruby є не найкращою ідеєю. «Системні менеджери пакетів і менеджери пакетів для мов програмування — абсолютно різні речі. Централізоване розповсюдження всіх пакетів це чудово, але абсолютно не підходять для більшості бібліотек, викладених на GitHub. Централізоване розповсюдження пакетів надто повільне. Всі різні мови програмування та їх ком'юніті ніяк не взаємодіють між собою. Такі пакетні менеджери встановлюють пакети глобально, а я хочу керувати версіями використовуваних бібліотек» Ці недоліки, безумовно, присутні в даному рішенні. Але в них втрачається сама суть всіх цих проблем.

Фундаментальна проблема полягає в тому, що пакетні менеджери для різних мов програмування є децентралізованими.

Ця децентралізація мається на увазі навіть в самому визначенні пакетного менеджера: це якась програма, що встановлює з віддалених джерел програми та бібліотеки, які не були доступні локально, на момент установки. Навіть якщо уявити ідеальний централізований пакетний менеджер, навіть там будуть існувати дві копії цієї бібліотеки: одна — десь на сервері, друга — розташована локально у програміста, який пише додаток, використовуючи цю бібліотеку. Однак, в реальності екосистема бібліотек сильно страждає від фрагментації — вона об'єднує безліч бібліотек, створених різними розробниками. Звичайно, всі бібліотеки можуть завантажуватися і індексуватися в одному місці, але це все одно не означає того, що автори бібліотек будуть знати про будь-яких інших випадках використання. І потім ми отримаємо те, що в світі Perl називають DarkPAN: незліченною кількість коду, яке, начебто існує, але про який ми не маємо ні найменшого уявлення, так як воно зашито де то в проприетарном коді або функціонує десь на корпоративних серверах. Обійти децентралізацію можна тільки коли ви контролюєте абсолютно весь код вашої програми. Але в цьому випадку вам навряд чи знадобиться пакетний менеджер, чи не так? (До речі, колеги розповідали мені, що подібне є обов'язковим для великих проектів, наприклад, таких як операційна система Windows або Google Chrome.)

Децентралізовані системи складні. Серйозно, дуже складні. Якщо ви ретельно не продумаєте архітектуру такої системи то вас неодмінно чекає dependency hell. Не існує одного «правильного» рішення цієї проблеми: я можу назвати, як мінімум, три різні підходи до вирішення даної проблеми, що застосовуються в різних поколіннях пакетних менеджерів, і кожен з них має свої плюси і мінуси.

Закріплюються версії. Мабуть, самим популярним, є думка, що розробник повинен суворо вказувати версію пакету. Цей підхід просувається такими менеджерами як Bundler для Ruby, Composer для PHP, pip в зв'язці з virtualenv для Python і будь-яким іншим, натхненним підходом Ruby/node.js (наприклад, Gradle для Java або Cargo для Rust). Воссоздаваемость збірок в них править балом — ці пакетні менеджери вирішують проблему децентралізованності, просто припускаючи, що вся екосистема пакетів перестає існувати, як тільки ви закріпили версії. Основною перевагою даного підходу є те, що ви можете вказувати версії бібліотек, які використовуєте в коді. Звичайно, це ж є і мінусом — вам завжди доведеться контролювати версії цих бібліотек. Зазвичай версії просто фіксують, благополучно забуваючи про них, навіть якщо виходить яке-небудь важливе оновлення безпеки. Щоб мати оновлені версії всіх залежностей необхідні цикли розробки, але цей час найчастіше витрачається на інші речі (наприклад на розробку нових фіч).

Стабільні версії. Якщо управління пакетами вимагає, щоб кожен індивідуальний розробник програми витрачав час і зусилля на підтримку всіх залежностей в актуальному стані і перевіряв щоб вони продовжували коректно працювати з додатком і один з одним, ми могли б задатися питанням — а чи існує спосіб централізувати цю роботу? Це приводить нас до ще одного підходу: створити централізований репозиторій з схваленими пакетами, робота яких перевірена, і випускати для них виправлення та оновлення безпеки, поки ми будемо підтримувати зворотний сумісність. Для різних мов програмування існують реалізації і таких пакетних менеджерів. Принаймні два, про яких я знаю, це Anaconda для Python і Stackage для Haskell. Але якщо придивитися, ми побачимо, що точно така ж модель використовується в пакетних менеджерах операційних систем. Як системний адміністратор, я часто рекомендую користувачам віддавати перевагу бібліотек, распостраняемым в репозиторіях операційних систем. Вони не зламають зворотну сумісність поки ми не перейдемо на нову релізну версію ОС, і в той же час, ви завжди будете використовувати останні виправлення й оновлення безпеки. (Так, ви не зможете скористатися фічами з нових версій, але, само по собі, це йде врозріз з поняттям стабільності.)

Розглядаючи децентралізацію. До цього пункту ми намагалися взагалі не розглядати децентралізацію як прийнятний підхід. Говорили про те, що необхідний центральний репозиторій і контроль за оновленнями з боку розробника. Але не выплескиваем ми дитину разом з водою? Головним мінусом централізованого підходу є величезна кількість роботи, яку необхідно провести для того, щоб домогтися стабільної роботи всіх пакетів і підтримки цих пакетів в актуальному стані. Крім того, ніхто не очікує, що абсолютно всі пакети будуть сумісні друг з іншому, але, тим не менш, це не заважає використовувати певні категорії пакетів разом з іншими. Ідеальна децентралізована система перекладає задачу визначення, які пакети можуть працювати разом на кожного, хто бере участь у цій системі, що знову повертає нас до фундаментального питання: Яким чином ми можемо створити екосистему децентралізованих пакетних менеджерів, яка буде працювати?

Ось ряд принципів, які можуть нам допомогти:

  1. Сувора інкапсуляція залежностей. Одна з причин, яка робить dependency hell такої підступної проблемою, полягає в тому, що залежно пакету часто є нерозривною частиною з його основним API: таким чином, вибір залежно більшою мірою є глобальним вибором, впливає на весь додаток. Якщо бібліотека використовує якісь залежності всередині, і цей вибір повністю обумовлений тільки деталями внутрішньої реалізації цієї бібліотеки не повинен призводити до якихось глобальних обмежень. NPM для NodeJS доводить цей принцип до логічного межі — за умовчанням він не обмежує дублювання залежностей, дозволяючи кожній бібліотеці завантажити свій власний примірник залежного пакета. Хоча я і сумніваюся що варто дублювати абсолютно всі пакети (це зустрічається в екосистемі Maven для Java), я, звичайно, згоден, що такий підхід підвищує компонуемость залежностей.
  2. Просування семантичного версионирования. В децентралізованих системах особливо важливо, щоб розробники бібліотек надавали якомога більш точну інформацію про бібліотеку, для того щоб користувачі та утиліти працюють з пакетами могли приймати обґрунтовані рішення. Різні формати версій і діапазонів версій лише ускладнюють і без того непросте завдання (як я вже писав в попередньому пості). Якщо у вас є можливість використовувати семантичні версії, або навіть краще, замість семантичних версій використовувати більш правильний підхід, вказуючи залежності на рівні типів у своїх інтерфейсах, наші утиліти зможуть зробити кращий вибір. «Золотий стандарт» інформації в децентралізованих системах це «Сумісний пакет А з пакетом Б», і ця інформація, найчастіше, дуже складна для аналізу (або неможлива, для систем з динамічною типізацією).
  3. Централізація для особливих випадків. Один з принципів децентралізованої системи полягає в тому, що кожен учасник може зібрати найбільш підходящі для себе оточення. Це передбачає певну свободу у виборі центрального джерела або ж створення і використання свого власного — централізація для особливих випадків. Якщо ми припускаємо, що користувачі будуть створювати свої власні репозиторії, в стилі використовуються в операційних системах, ми повинні надати їм інструменти за допомогою яких можна буде легко і безболісно створювати і використовувати ці репозиторії.


Протягом тривалого часу, екосистема управління вихідним кодом була повністю побудована навколо централізованих систем. Поширення таких систем контролю версій як Git докорінно змінило ситуацію: хоча Git і може здаватися більш складним ніж Subversion для освоєння далекими від технологій людьми, переваги децентралізації набагато масштабніше і різноманітніше. Але такий же Git для управління пакетами створити поки нікому не вдалося. Якщо хтось буде запевняти вас, що проблема управління пакетами вирішена і все просто вигадую заново Bundler, я прошу вас, подумайте про децентралізацію як слід.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.