Що знадобилося, щоб довіряти менеджеру пакетів, який написав Claude

Перекладено з англійської за допомогою ШІ. Читати оригінал

Я не написав і навіть не прочитав жодного рядка коду, поки робив jpm — менеджер пакетів для JavaScript, який працює в проєктах на npm, pnpm, yarn і bun. Ба більше, я попросив ШІ написати власні модулі TLS, криптографії та HTTP, щоб зробити його меншим. Спочатку це був справжнісінький вайб-кодинг. І готово все було за пів дня. Ні я, ні будь-хто інший не мав би довіряти йому в такому стані, але потім я взяв те, що знаю за 27+ років роботи професійним розробником, змусив його перевірити себе на наявних тестових наборах, а потім п’ять днів писати тести й жорстко тестувати себе, щоб зробити його надійнішим.

Про себе

За свою кар’єру я користувався багатьма мовами програмування, але здебільшого високорівневими, як-от C#, Elixir і JavaScript. Низькорівневу мову я не використовував більше ніж для кількох функцій відтоді, як малим вчив C і влітку допомагав батькові на його роботі з низькорівневими контролерами дисків. Тож освоїти Rust і написати на ньому проєкт на 60k+ рядків за короткий час я б не зміг. Натомість я добре вивчив, як працюють npm, pnpm і bun, бо професійно використовую Node.js ще з Node.js 0.8 (2012). А ще я знаю, скільки насправді треба тестувати софт і як важливо це автоматизувати, а не робити один раз. Усі ці agile-хлопці, які казали мені спершу писати тести, таки мали рацію — це окупилося!

Чому jpm

Я дивився подкаст Syntax на YouTube з ведучими Wes, Scott і CJ, і на початку випуску вони говорили про upm — швидкий і дуже малий менеджер пакетів, але написаний на JavaScript. Перше, що я подумав: щодо розміру — неправда, бо до нього треба додати середовище виконання Node.js чи якесь інше. Його написав Pooya, і CJ показував бенчмарки з проєкту, де видно, що він швидкий, але мені просто захотілося перевірити, чи зможемо ми зробити швидше. Pooya — добрий інженер, який робить багато хороших інструментів для екосистеми JavaScript, тож я знав, що це буде нелегко. І нарешті, я вже якийсь час користувався моделлю Opus 5.5, і вона мене просто вражала. От я й подумав: якщо вже стільки інших інструментів написано на Rust (у подкасті з цього навіть жартували), може, і я зможу зробити на Rust справді малий і швидкий менеджер пакетів.

У подкасті ведучі відповідали на запитання своєї аудиторії про ШІ — ті самі запитання, які зараз ставлять усі. Чи може він створювати справжній софт, чи лише демки? Інженерам варто хвилюватися чи радіти? Більшість вважає, що ШІ може зробити що завгодно з першої спроби. Спойлер: може. Просто результати так собі, якщо ним не керувати й не розуміти, як розробляти софт.

Пів дня

Тож того дня, коли я закінчував з основною роботою, я відкрив нову вкладку в терміналі й спитав Claude, що потрібно, щоб переписати upm як CLI на Rust. Я прочитав підсумок, дав добро, і він узявся до роботи. Здається, пішло хвилин 45, і в нього була робоча версія на купі сторонніх Rust-крейтів, через які вона була значно більшою, і протестована вона була лише на маленькому package.json. Щоб її зменшити, я попросив розібрати, що саме робить її такою великою, і до кінця того вечора в мене були власні модулі HTTP, JSON, криптографії та TLS і значно менший бінарний файл. Я сказав йому писати лише ті частини, які нам потрібні, але й жорстко їх тестувати — тестами інших проєктів, де вийде, або нашими власними, плюс трохи фазингу. Я хотів бути певним, що вони надійні.

П’ять днів

Ось цю частину всі, хто «вайб-кодить» проєкти, і пропускають. Так, у мене все працювало на маленькому package.json під Linux у WSL2 на Windows, але я не перевіряв ні Mac, ні Windows, ні справді великих проєктів. Тож я попросив його написати скрипт для зміцнення, який брав 100, а згодом 166 великих публічних проєктів і пробував їх встановити й зібрати. Потім встановити й зібрати їх тим менеджером пакетів, яким вони користуються, і порівняти. Разом із запуском їхніх тестів, де це було можливо, бо деяким тестам потрібні були секрети чи інше налаштування. І ще робити все це в командному рядку Windows і в PowerShell, а на своєму Mac я запустив агента, щоб він робив те саме на Mac. І за п’ять днів, ухваливши багато рішень про те, що підтримувати і як, ми мали 261 змерджений PR і надійний, добре протестований менеджер пакетів.

Як виглядало керівництво

Тож код я не писав, але керував і ухвалював купу дрібних рішень. Наприклад, Claude казав, що швидший формат lockfile зекономить лише кілька ms, але я все одно попросив це змінити. Ще Claude хотів зробити глобальне сховище опцією, яку треба вмикати вручну, але я спитав: навіщо, якщо ним можуть користуватися 99% пакетів? — і воно стало поведінкою за замовчуванням. Ще я не хотів, щоб він запускав скрипти, зокрема скрипти встановлення залежностей, якщо ви явно їх не дозволили. Логіка була така: зараз більшість роботи робить ШІ, тож перш ніж схвалити скрипти пакета, я можу просто попросити його спершу перевірити їх на проблеми.

Було й два випадки, коли Claude справді мені заперечував, але обидва рази я наполіг на своєму.

Перший — TLS. Я хотів зменшити бінарний файл, а крейти TLS і криптографії становили велику його частину. Claude сказав, що це саме той код, який ніколи не пишуть самі. Я відповів, що це старе інженерне мислення. Ми на Rust, тож помилки роботи з пам’яттю, які стояли за речами на кшталт Heartbleed, відпадають, і свою версію ми можемо тестувати так само жорстко, якщо не краще. Він погодився, що перебільшив ризик, і розписав, які тестові набори це доведуть (Wycheproof, вектори NIST, x509-limbo, BoGo). Я досі сумніваюся, добра це ідея чи ні, а Claude любить підлещуватися й казати те, що ви хочете почути, тож його слова про те, що він перебільшив ризик, цілком могли бути просто бажанням догодити. Він проходить купу тестів, зокрема тести, зроблені на основі реальних багів з інших проєктів. Я точно знаю, що тепер довіряв би йому значно більше, хоч би хто його написав.

Другий — HTTP/2. Мені хотілося більше швидкості, і я спитав про нього. Claude спершу перевірив його з curl і сказав, що робити його не варто, бо все встановлення jpm через HTTP/1.1 уже було швидшим, ніж саме лише завантаження через curl по HTTP/2. Я все одно натиснув. curl мусить обробляти всі випадки, а нам потрібно лише багато GET-запитів до одного реєстру. Щоб обігнати bun, треба мислити нестандартно. Він погодився, і приблизно через дві години в нього був робочий клієнт.

Потім ми це виміряли. На моєму домашньому інтернеті HTTP/2 був приблизно на 17% швидшим. На раннерах GitHub він ішов урівень або програвав, на холодному встановленні nuxt був до 2 разів повільнішим і на великих tarball-архівах споживав на 8–15% більше CPU. Саме в CI встановлення коштують реального часу й грошей, а ще він додавав до бінарного файлу 60 KB. Тож я попросив його видалити всі 3 671 рядок. Тут я помилявся, і цифри мені це показали.

Бувало й навпаки. jpm може сам встановити вам Node, якщо хочете, і я хотів сховати перевірку підпису Node за прапорцем, бо думав, що вона сповільнить встановлення. Claude наміряв від 15 до 19 ms і сказав, що прапорець означає лише одне: його ніхто не ввімкне. Тепер вона ввімкнена за замовчуванням, і я цьому радий, бо це додає безпеки.

Чому я довіряю коду, який ніхто не набирав

Якщо коротко: Wycheproof, node-semver, власні тести pnpm, yarn і bun та 166 репозиторіїв, на яких ми його перевіряли. Деякі баги, які вони знайшли дорогою:

  • Власні тести node-semver знайшли 25 випадків, де jpm із ним розходився, наприклад приймав ^9007199254740991.0.0 як діапазон.
  • З тестів npm-package-arg спершу збігалося лише 117 зі 154. a@a.tar.gz читався як тег.
  • Сканування безпеки виявило, що .npmrc склонованого репозиторію міг відправити ваш токен реєстру через свій власний проксі. PoC перехопив Bearer USER-SECRET-TOKEN. Тепер .npmrc проєкту більше не може знизити безпеку.
  • На реальних репозиторіях кількість посилань на залежності, де встановлення jpm відрізнялося від pnpm, упала з 1 320 до 183, а від npm — з 614 до 14.
  • Під macOS jpm навіть не компілювався, доки я не протестував його на macOS.

А ще за стільки років у розробці я знаю, що нічого не буває без багів, хоч якими були б тести й тестування. І довіру треба заслужити, тож я збираюся й надалі використовувати його в усіх своїх проєктах і підтримувати виправлення, навіть надіслані ШІ. Це не означає, що я прийматиму їх усі. Вони мають тестами довести, що справді щось виправляють. Суть у тому, що я не збираюся вимикати PR, але розбиратися з ними мені допомагатиме ШІ.

Де ми зараз

Ми довели виконуваний файл до 2 MB, і він не завжди найшвидший, але серед лідерів. А ще він один із найбезпечніших: не запускає скрипти без схвалення. Не оновлює пакети до останньої версії, якщо їй немає хоча б доби. І захист навколо .npmrc.

Ось один із графіків, який показує, наскільки швидкими ми можемо бути на раннері GitHub Actions порівняно з іншими менеджерами пакетів:

Холодне встановлення nuxt (591 пакет) на GitHub CI, Linux, медіана 5 запусків: jpm 1,22 s, bun 1,28 s, pnpm 1,72 s, aube 3,00 s, upm 3,52 s, deno 7,64 s, yarn 9,63 s, npm 18,54 s

Усі машини, фази й проєкти — на getjpm.sh.

Де він не найшвидший

Скажу чесно: бенчмарк маленький, лише три репозиторії. І ми не завжди найшвидші. Наприклад, на Windows на nuxt нас убивають перевірки Defender. На macOS bun і aube виграють на теплих встановленнях nuxt. Тож якщо кінцева мета — швидкість, є куди рости. Ми прогнали встановлення 166 проєктів на Linux, macOS і Windows, плюс іще більше випадкових на Linux, щоб шукати баги, але не тестували продуктивність кожного з них у CI та на машинах.

Чи ідеальний jpm? Ні. Як і ніщо з того, що я написав руками за 27 років, і ніщо з того, що будь-коли написала людина. Ідеального не буває. Буває достатньо добре, буває чудово, і буває протестовано достатньо, щоб знати, що з цього маєте ви.

Спробуйте: curl -fsSL https://getjpm.sh | sh на macOS і Linux або irm https://getjpm.sh/install.ps1 | iex на Windows, а потім запустіть jpm install у будь-якому проєкті. Якщо знайдете баг, створіть issue на GitHub.