Рядом с Git · Про Git тут ни слова
Почему из пяти строк вырастает полторы тысячи папок, зачем нужен файл-замок и в чём разница между двумя менеджерами, которые делают одно и то же.
Зачем это в твоей миссии
К Git отношения не имеет, но пробел тот же: слова мелькают каждый день, а картинки за ними нет. Агент говорит «ставлю зависимости» — полезно понимать, что при этом происходит с диском, почему нельзя разрешать ему удалять файл-замок и почему свежая папка проекта не запускается.
В твоём проекте GildiyaChat в списке нужного перечислено 5 пакетов. А на диске в папке зависимостей лежит 1432 пакета общим весом 992 мегабайта.
Пять и полторы тысячи. Разберёмся, откуда берётся разница — а заодно станет понятно всё остальное.
Рецепт. Пишете его вы с агентом. Здесь сказано, что проекту нужно: «библиотека для веб-сервера, версия пятая или новее». Формулировки нарочно с люфтом.
Протокол. Пишет менеджер, а не ты. Что именно встало: версия до последней цифры, адрес, контрольная сумма. Люфта тут нет вообще.
Результат. Сами файлы библиотек. В снимках Git не хранится: полностью восстанавливается из первых двух.
Файл-замок называется по-разному в зависимости от менеджера: package-lock.json у npm, pnpm-lock.yaml у pnpm. Смысл один.
Проверил на живом примере. Поставил один пакет — популярный express. На диск приехало 65 пакетов.
Причина простая: у каждой библиотеки есть свои зависимости, у тех — свои. Ты просишь одну коробку, приезжает фура. Это называется транзитивными зависимостями, и это нормальный порядок вещей, а не чья-то ошибка.
Поэтому пять строк в рецепте и полторы тысячи папок на диске — не противоречие. Просто ты видишь верхушку, а менеджер разворачивает всё дерево целиком.
Вот здесь прячется настоящая практическая польза, ради которой стоит дочитать.
В рецепте написано «версия пятая или новее» — это удобно, но означает, что в разные дни установка даст разный результат. Сегодня встанет 5.2, через месяц выйдет 5.3 и встанет она.
А теперь представь: у тебя на маке стоит 5.2, потому что ставил в июле. На сервере разворачивают проект в августе — и туда приезжает 5.3, где что-то поменялось. Приложение падает только на сервере. Ты смотришь на один и тот же код и не понимаешь, в чём дело.
Файл-замок это отсекает. В нём записано «ровно 5.2.1, вот с такой контрольной суммой», и на любой машине встанет то же самое.
Отсюда правило, которое стоит держать в голове
Когда установка не идёт, у агента есть соблазн удалить файл-замок — после этого она обычно проходит. Сиюминутная проблема решена, воспроизводимость сломана. Правильный ответ агенту: «не удаляй, покажи саму ошибку».
npm и pnpm ставят одни и те же пакеты из одного и того же места. Отличаются они только тем, как раскладывают их на диске.
npm — каждому проекту личная копия:
проект-1/node_modules/express/… ← файлы
проект-2/node_modules/express/… ← те же файлы ещё раз
проект-3/node_modules/express/… ← и ещё раз
pnpm — один склад, а в проектах ссылки на него:
склад/express@5.2.1/… ← файлы, один раз
проект-1/node_modules/express ──┐
проект-2/node_modules/express ──┼──▶ туда же
проект-3/node_modules/express ──┘
Замерил на одном и том же пакете:
| npm | pnpm | |
|---|---|---|
| один проект | 3,8 МБ | 4,3 МБ |
| два проекта с тем же пакетом | 7,5 МБ | 5,1 МБ |
Обрати внимание на первую строку: на одном проекте pnpm даже чуть тяжелее. Никакого чуда нет — служебная структура склада тоже занимает место. Выигрыш начинается со второго проекта: у npm вес удвоился, у pnpm вырос на восемьсот килобайт. На двадцати проектах разница уже в гигабайтах.
Механизм называется жёсткой ссылкой, и его стоит отличать от обычного ярлыка:
Это видно напрямую. У файла есть счётчик: сколько имён на него указывает. В проверке выше файл, поставленный через npm, имел одно имя — личная копия. Тот же файл в раскладке pnpm — семнадцать имён, потому что он же используется в других проектах на этой машине.
Отсюда и главное свойство: экономия без потери независимости. Проекты могут держать разные версии одной библиотеки и не мешать друг другу — просто одинаковые куски не дублируются.
Прямо при том. В уроке про worktrees была засада: свежее рабочее дерево приезжает без зависимостей, и их приходится ставить заново.
Если проект на pnpm, эта установка почти бесплатна: файлы уже на складе, создаются только ссылки. Если на npm — каждое новое дерево честно копирует свой гигабайт.
Одно условие
Склад должен лежать на том же диске, что и проекты — жёсткие ссылки не умеют пересекать границу между дисками. Если склад окажется на другом, pnpm молча начнёт копировать, и весь смысл пропадёт. По умолчанию он сам заводит по складу на каждый диск, так что обычно об этом можно не думать.
Обычная установка
Поставь зависимости тем менеджером, чей файл-замок лежит в проекте.
Одна фраза снимает целый класс проблем. Агент, пришедший в чужой проект, иногда ставит привычным ему менеджером — и рядом появляется второй файл-замок.
Установка не идёт
Не удаляй файл-замок. Покажи мне саму ошибку целиком.
Удаление замка выглядит как решение и является сокрытием проблемы. Ошибка почти всегда говорит что-то конкретное про версию или про сеть.
Новый проект
Заводи проект на pnpm.
Дальше это будет экономить и место, и время на каждом новом worktree. Переводить существующие проекты при этом не нужно — там уже всё установлено и работает.
Первоисточники к этому уроку
pnpm → Motivation — объяснение от самих авторов, зачем понадобился ещё один менеджер. Там же про склад и жёсткие ссылки. Про склад на другом диске — в разделе вопросов.
Документация npm про package-lock.json — официальное описание файла-замка и того, зачем он существует.