Стендап Сьогодні
📢 Канал в Telegram @stendap_sogodni
🦣 @stendap_sogodni@shevtsov.me в Федиверсі

🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!

22.05.2025

Чим система залежностей Go гірша за node_modules?

#Go

Тека з модулями Node.js - node_modules - є на цей час біблейським прикладом надмірної купи залежностей. Але я б не сказав, що ця проблема обмежується тільки Node.js чи тільки JS. Поки подивімося на Go.

В Go пакети завжди імпортуються за URL: import "github.com/leonid-shevtsov/omniwope". Що сидить за цим URL? Який формат файлу мають пакети Go? Ооо… Пакет Go завжди є репозиторієм системи контролю версій. (Звісно, найчастіше Git, але не обовʼязково.) Таким чином, мова Go не має ніякого “пакетного формату файлів”, а просто завантажує зміст, як це робить git clone. Та ще й перекладає питання версіювання на репозиторій.

Це все з одного боку спрощує життя. В Go немає проблеми сквотингу (хоча дехто відхопив собі смачне імʼя github.com/lib/pq, наприклад.) Немає централізованого сервісу — можна хоч на власному домені публікувати. Питання авторизації та приватних пакетів теж легко розвʼязується через репозиторій. Через все це я люблю Go modules.

(Ще цікавий момент: мажорна версія змінює URL пакету. Хоча це тільки домовленість, та я багато разів зустрічав несумісні зміни з тією ж мажорною версією.)

А тепер, величезний недолік. Змістом пакета є зміст репозиторію. Немає ніякої можливості сказати, що деякі файли не потрібні. Коли ти встановлюєш пакет Go, то тягнеш всю-всю документацію, ілюстрації, тестові файли — просто все. Наприклад: є github.com/pierrec/lz4/v4 із 40 Мб тестових архівів. І всі вони будуть сидіти в тебе на диску, в кеші CI тощо.

Те ж саме стосується й транзитивних залежностей. Та що мене особливо дратує — навіть якщо транзитивна залежність потрібна тільки для оголошення типу, все одно всі її файли будуть завантажені в Go modules. Ну а як інакше, коли тип є частиною коду? Цього можна частково уникнути, якщо оголошувати інтерфейси на боці споживача — але все одно вони часто спираються на конкретні типи. Або, пакети, потрібні для тестів транзитивних залежностей, теж завантажуються. Наприклад, пакет testfixtures підтримує БД Clickhouse. Мені вона зовсім не потрібна — та сам пакет не має на неї залежність. Але його тести — мають! Та оскільки тест сидить в тому ж самому репозиторії, то пакет Clickhouse стає транзитивною залежністю вже мого застосунку. І це тільки один приклад з безлічі.

Заради справедливості: цього напливу залежностей можна уникнути, якщо робити go mod vendor - тоді копіюється тільки код, потрібний для головного пакета. Але насправді це допомагає тільки якщо у вас цей vendor ще й доданий до Git. Бо інакше будете тримати локально як vendor, так і окремо всю купу — щоб його побудувати.