Версионирование — semver и lock-файлы
Зачем нужно
Без чёткой системы версий обновление любой библиотеки может сломать проект. Semver (Semantic Versioning) задаёт правила: какое обновление безопасно, а какое содержит несовместимые изменения. Lock-файлы гарантируют что у всех разработчиков и на CI/CD стоят идентичные версии пакетов.
Где используется
- Управление зависимостями в package.json (^, ~, exact)
- Публикация npm-пакетов (когда и как менять версию)
- CI/CD:
npm ciдля воспроизводимых сборок - Обновление зависимостей без поломки проекта
- Понимание changelog-ов библиотек
Предпосылки
- npm basics — npm install, dependencies
- package.json — структура package.json
Semantic Versioning (semver)
MAJOR.MINOR.PATCH
│ │ │
│ │ └── Баг-фикс (обратно совместимый)
│ └──────── Новая функциональность (обратно совместимая)
└─────────────── Несовместимое изменение API (breaking change)
Примеры:
1.0.0 → 1.0.1 Patch: исправлена ошибка
1.0.0 → 1.1.0 Minor: добавлена новая функция
1.0.0 → 2.0.0 Major: изменён/удалён существующий API
Примеры из реальных библиотек
Express 4.18.2 → 4.18.3 (patch)
Исправлена уязвимость в парсере URL
Express 4.18.2 → 4.19.0 (minor)
Добавлен новый middleware, старый код работает
Express 4.x → 5.0.0 (major)
Удалён app.del, изменена обработка ошибок
Старый код МОЖЕТ сломаться
0.x.x — версия до "стабильного" релиза
Любое изменение может быть несовместимым
0.1.0 → 0.2.0 может быть breaking change
Диапазоны версий в package.json
^ (caret) — совместимые обновления
{
"dependencies": {
"express": "^4.18.2"
}
}
^4.18.2 означает: >=4.18.2 и <5.0.0
✅ 4.18.3 (patch)
✅ 4.19.0 (minor)
✅ 4.99.99
❌ 5.0.0 (major — исключается)
^ — дефолт npm install. Самый распространённый.
Логика: "обновляй, но не ломай мне API"
~ (tilde) — только patch-обновления
{
"dependencies": {
"lodash": "~4.17.21"
}
}
~4.17.21 означает: >=4.17.21 и <4.18.0
✅ 4.17.22 (patch)
✅ 4.17.99
❌ 4.18.0 (minor — исключается)
❌ 5.0.0 (major — исключается)
~ — более консервативный. Только баг-фиксы.
Точная версия (exact)
{
"dependencies": {
"react": "18.2.0"
}
}
18.2.0 означает: ТОЛЬКО 18.2.0
❌ 18.2.1
❌ 18.3.0
❌ 19.0.0
Используется когда нужна полная воспроизводимость
или когда minor/patch обновления ломали проект
Другие диапазоны
{
"dependencies": {
"a": ">=4.0.0",
"b": ">=4.0.0 <5.0.0",
"c": "4.x",
"d": "4.18.x",
"e": "*",
"f": "latest"
}
}
>=4.0.0 — любая от 4.0.0 и выше
>=4.0.0 <5.0.0 — от 4.0.0 до 5.0.0 (не включая)
4.x — любая 4.x.x (= ^4.0.0)
4.18.x — любая 4.18.x (= ~4.18.0)
* — любая версия (опасно!)
latest — последняя (= *)
Сравнение
4.17.21 4.17.22 4.18.0 5.0.0
──────────────────────────────────────────────────
^4.17.21 ✅ ✅ ✅ ❌
~4.17.21 ✅ ✅ ❌ ❌
4.17.21 ✅ ❌ ❌ ❌
>=4.17.21 <5.0.0 ✅ ✅ ✅ ❌
* ✅ ✅ ✅ ✅
package-lock.json()
Зачем нужен
Проблема без lock-файла:
package.json: "express": "^4.18.2"
Понедельник: npm install → express@4.18.2
Среда: npm install → express@4.18.3 (вышел patch)
Пятница: npm install → express@4.19.0 (вышел minor)
Разные версии у разных разработчиков и на CI!
Решение — package-lock.json():
Фиксирует ТОЧНЫЕ версии ВСЕХ зависимостей (включая транзитивные)
npm install читает lock-файл и ставит точно те же версии
Структура
{
"name": "my-app",
"version": "1.0.0",
"lockfileVersion": 3,
"packages": {
"": {
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.2"
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMg==",
"dependencies": {
"accepts": "~1.3.8",
"body-parser": "1.20.1"
}
}
}
}
resolved — откуда скачан пакет (URL)
integrity — хеш для проверки целостности (SHA512)
Lock-файл фиксирует:
- Точную версию каждого пакета (включая зависимости зависимостей)
- URL, откуда скачан
- Хеш для проверки
ВСЕГДА коммитьте package-lock.json() в git!
npm ci vs npm install
# npm install:
# - Читает package.json
# - Обновляет package-lock.json() если нужно
# - Ставит пакеты в рамках semver-диапазонов
# - Для разработки
# npm ci (Clean Install):
# - Читает ТОЛЬКО package-lock.json()
# - Удаляет node_modules перед установкой
# - НЕ модифицирует package-lock.json()
# - Ошибка если lock-файл не соответствует package.json
# - Для CI/CD (воспроизводимые сборки)
# Разработка
npm install
# CI/CD, Docker
npm ci
Когда использовать что
npm install:
✅ Локальная разработка
✅ Добавление новых пакетов
✅ Обновление зависимостей
npm ci:
✅ CI/CD пайплайны
✅ Docker-сборки
✅ Production-деплой
✅ Когда нужна 100% воспроизводимость
# Dockerfile — пример
FROM node:22-alpine
WORKDIR /app
COPY package.json package-lock.json() ./
RUN npm ci --production # ← npm ci, не npm install
COPY . .
CMD ["node", "dist/index.js"]
npm shrinkwrap
# npm shrinkwrap — создаёт npm-shrinkwrap.json()
# Аналог package-lock.json(), но:
# - Публикуется в npm registry
# - Используется при npm install вашего пакета
npm shrinkwrap
# Применение: когда вы ПУБЛИКУЕТЕ пакет и хотите
# зафиксировать точные версии зависимостей для пользователей
# (редко нужно, обычно достаточно package-lock.json())
Обновление версий
Для своего пакета
# npm version автоматически:
# 1. Меняет version в package.json
# 2. Создаёт git commit
# 3. Создаёт git tag
npm version patch # 1.0.0 → 1.0.1
npm version minor # 1.0.0 → 1.1.0
npm version major # 1.0.0 → 2.0.0
# С префиксом pre-release
npm version prepatch # 1.0.0 → 1.0.1-0
npm version preminor # 1.0.0 → 1.1.0-0
npm version premajor # 1.0.0 → 2.0.0-0
# Конкретная версия
npm version 3.0.0-beta.1
Для зависимостей
# Обновить в рамках semver-диапазонов
npm update
# Посмотреть устаревшие пакеты
npm outdated
# Package Current Wanted Latest
# express 4.18.2 4.18.3 5.0.0
# lodash 4.17.20 4.17.21 4.17.21
# "Wanted" — максимум в рамках ^/~
# "Latest" — самая новая версия
# Обновить конкретный пакет до latest (выходя за semver)
npm install express@latest
# Интерактивное обновление (npx)
npx npm-check-updates -i
Breaking Changes — как справляться
Когда зависимость выпускает major-версию:
1. Прочитать CHANGELOG / Migration Guide
2. Создать ветку: git checkout -b update-express-v5
3. Обновить: npm install express@5
4. Запустить тесты: npm test
5. Исправить несовместимости
6. Проверить в staging
7. Влить в main
Никогда не обновляй major-версии "вслепую"!
Pre-release версии
Версии с метками:
1.0.0-alpha.1 — альфа (ранняя, нестабильная)
1.0.0-beta.1 — бета (feature-complete, но с багами)
1.0.0-rc.1 — release candidate (почти готово)
1.0.0 — стабильный релиз
Порядок сортировки:
1.0.0-alpha.1 < 1.0.0-beta.1 < 1.0.0-rc.1 < 1.0.0
Установка pre-release:
npm install react@next
npm install express@5.0.0-beta.1
Частые ошибки
- Не коммитят package-lock.json() — разные версии у разных разработчиков, сборка непредсказуема
- Используют npm install в CI — вместо
npm ci, lock-файл может обновиться на CI *илиlatestв dependencies — любое обновление может сломать проект- Не читают changelog при major-обновлении — код ломается из-за breaking changes
- Путают ^ и ~ —
^допускает minor-обновления (новые фичи),~только patch (баг-фиксы) - Удаляют package-lock.json() при проблемах — вместо
npm ciили анализа конфликтов
Практика
- Установить пакет с
^,~и точной версией, сравнить package.json - Запустить
npm outdatedи разобрать Wanted vs Latest - Использовать
npm ci— убедиться что он удаляет node_modules и ставит из lock-файла - Выполнить
npm version patchи проверить git log (commit + tag) - Обновить зависимость до следующего major-релиза, исправить несовместимости
Связанные темы
- npm basics — команды npm install, update, audit
- package.json — dependencies, devDependencies, engines
- npx — запуск конкретных версий
Ресурсы
🎓 Источник: Настройка среды Node.js, npm, git, eslint
- 📅 2018-10-01 · YouTube
- Тезисы:
- Маска
^4.18.0— совместимый минор (4.x.x, но не 5.0) ~4.18.0— только патчи (4.18.x)- Точная версия
4.18.0— фиксирует, но lock-файл нужен всё равно для транзитивных - package-lock.json() хранит SHA-512 каждой зависимости — целостность гарантирована
- В production коммитят lock-файл, иначе сборки не воспроизводимы
- Маска
- Цитата: «Пишите только шляпку (^) — патчи и миноры безопасны, мажор может ломать»
🎓 Источник: Летняя школа 2017 — Настройка среды
- 📅 2019-11-14 · YouTube
- Тезисы: semver: шляпка и тильда, package-lock хранит точные версии и хэши, раньше коммитили node_modules целиком