Back

Семантическое версионирование: разбираемся в деталях

Семантическое версионирование: разбираемся в деталях

Если вы когда-нибудь открывали файл package.json и задумывались, почему одна зависимость указана как ^1.4.2, а другая — как ~2.0.1, значит, вы уже сталкивались с практическими последствиями семантического версионирования. Понимание того, как работает SemVer, помогает принимать более взвешенные решения об обновлении зависимостей, избегать сломанных сборок и чётко сообщать об изменениях, когда вы публикуете свои собственные пакеты.

Ключевые выводы

  • SemVer использует формат MAJOR.MINOR.PATCH, где каждый сегмент сигнализирует о конкретном типе изменений: ломающих, добавляющих или исправляющих.
  • Операторы диапазонов npm (^, ~ и точные версии) определяют, как обновляются зависимости. Каретка (^) используется по умолчанию и является наиболее разрешающим оператором в пределах мажорной версии.
  • Lock-файлы вроде package-lock.json обеспечивают воспроизводимость установок в командах и CI-окружениях независимо от указанного диапазона.
  • Версии ниже 1.0.0 считаются нестабильными, а pre-release-теги (например, 1.0.0-beta.1) рассматриваются как явно нестабильные релизы, которые npm не устанавливает через обычные диапазоны без явного запроса.
  • SemVer — это социальный контракт между сопровождающими и потребителями, а не технический механизм принуждения.

Что такое семантическое версионирование?

Семантическое версионирование (SemVer) — это спецификация версионирования, которая придаёт смысл номерам версий. Версия в формате SemVer имеет вид MAJOR.MINOR.PATCH, где каждое число обозначает определённый тип изменений:

  • MAJOR — ломающее изменение, несовместимое с предыдущим публичным API
  • MINOR — новая функциональность, добавленная с сохранением обратной совместимости
  • PATCH — обратносовместимое исправление ошибки

Когда пакет переходит с 2.6.9 на 3.0.0, что-то нарушило обратную совместимость. Переход с 2.6.9 на 2.7.0 добавляет функции, ничего не ломая. Переход на 2.6.10 исправляет ошибку.

Важное уточнение: SemVer работает осмысленно только тогда, когда у пакета есть определённый публичный API. Без чёткого контракта между пакетом и его потребителями номер версии — просто число.

Как npm использует SemVer для диапазонов зависимостей

Версионирование npm напрямую опирается на SemVer, однако синтаксис диапазонов в package.json — это надстройка над ним, а не сам SemVer.

"dependencies": {
  "lodash": "^4.17.21",
  "axios": "~1.6.0",
  "react": "18.2.0"
}

Вот что означает каждый оператор диапазона:

  • ^ (каретка) — разрешает обновления MINOR и PATCH, но не MAJOR. ^4.17.21 принимает всё от 4.17.21 до, но не включая, 5.0.0. Для 0.x-релизов диапазоны с кареткой строже: ^0.2.3 разрешает обновления ниже 0.3.0, а не любые 0.x-версии.
  • ~ (тильда) — разрешает обновления PATCH, когда указана минорная версия. ~1.6.0 принимает 1.6.x, но не 1.7.0.
  • Точная версия18.2.0 устанавливает только эту конкретную версию.

Каретка используется в npm по умолчанию при выполнении npm install. Она обеспечивает автоматическое получение исправлений ошибок и новых функций, защищая при этом от ломающих изменений — при условии, что автор пакета корректно следует SemVer. Это предположение оправдывается не всегда.

Почему важны lock-файлы

Ваш package-lock.json фиксирует точную версию, установленную в конкретный момент времени. Даже если диапазон вроде ^4.17.21 допускает более новые версии, lock-файл закрепляет ту, что фактически используется в вашей команде и CI-окружении. Именно поэтому коммит lock-файла важен для воспроизводимости сборок.

Понимание 0.x-релизов и pre-release-версий

Две области часто сбивают разработчиков с толку:

0.x-релизы по определению нестабильны. Пакет на версии 0.4.2 не даёт никаких гарантий совместимости. Между 0.4.2 и 0.5.0 может измениться что угодно. Не полагайтесь на правила совместимости SemVer для пакетов, ещё не достигших 1.0.0.

Pre-release-версии вроде 1.0.0-beta.1 имеют более низкий приоритет, чем стабильный релиз. npm не установит pre-release-версию, если вы явно её не запросите. Обычный диапазон вроде ^1.0.0 не разрешится автоматически в 1.1.0-beta.1. Это защищает вас от случайного подтягивания нестабильного кода.

Когда какую версию повышать

Если вы сопровождаете пакет, используйте это руководство:

Тип измененияКакую версию повыситьПример
Ломающее изменение APIMAJOR1.4.22.0.0
Новая функция с обратной совместимостьюMINOR1.4.21.5.0
Только исправление ошибкиPATCH1.4.21.4.3
Объявление функции устаревшей (без удаления)MINOR1.4.21.5.0

При повышении MINOR сбрасывайте PATCH в ноль. При повышении MAJOR сбрасывайте в ноль и MINOR, и PATCH.

SemVer — это контракт, а не гарантия

SemVer даёт общий язык для коммуникации изменений. Но технически он не запрещает сопровождающему выпустить ломающее изменение в патч-релизе. Инструменты вроде semantic-release могут автоматизировать версионирование на основе сообщений коммитов, уменьшая количество ручных ошибок и помогая командам более последовательно следовать соглашениям SemVer.

Заключение

Понимание версионирования npm и пакетов через призму SemVer делает вас более уверенным потребителем open-source-пакетов и более ответственным издателем своих собственных. Формат прост, но его последствия глубоки: каждая цифра передаёт намерение, каждый оператор диапазона формирует риск, а каждый lock-файл оберегает воспроизводимость. Относитесь к SemVer как к общему словарю, которым он и был задуман, и управление зависимостями станет заметно спокойнее.

Часто задаваемые вопросы

Вы можете получить ломающие изменения через то, что должно было быть безопасным обновлением — например, через patch- или minor-повышение. Чтобы защититься, коммитьте свой package-lock.json, просматривайте changelog перед обновлением и рассматривайте инструменты вроде Renovate или Dependabot, которые показывают release notes вместе с повышением версий. Для критически важных зависимостей фиксация точных версий — разумная защитная мера.

Каретка используется в npm по умолчанию и хорошо подходит для большинства приложений, так как разрешает minor- и patch-обновления в пределах мажорной версии. Тильда строже и принимает только patch-обновления, что подходит проектам, ставящим стабильность выше новых функций. Для библиотек, которые вы публикуете, предпочтительны диапазоны с кареткой, чтобы потребители автоматически получали обратносовместимые улучшения.

Спецификация SemVer прямо указывает, что всё ниже 1.0.0 считается начальной разработкой, в которой публичный API не должен считаться стабильным. Сопровождающие могут вносить ломающие изменения между любыми 0.x-релизами без повышения мажорного номера. Как только проект достигает 1.0.0, он принимает на себя обязательства следовать всем правилам совместимости SemVer.

Её нужно запрашивать явно — либо указав точную версию (npm install package@1.0.0-beta.1), либо используя тег (npm install package@next). Стандартные диапазоны вроде ^1.0.0 полностью пропускают pre-release-версии, что предотвращает случайную установку нестабильного кода в продакшен-окружении.

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay