← Git: рабочие процессы
10 мин · просто · Урок 1 из 3

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

Что значат цифры в версии 2.4.1: MAJOR.MINOR.PATCH, диапазоны зависимостей и связь с git-тегами.

Зачем версии вообще нужны правила

Версия 2.4.1 — не случайный набор цифр. Когда у тебя десяток зависимостей, и каждая обновляется по-своему, нужен общий язык: «эта версия совместима со старой» или «эта сломает API». Договорённость, как читать эти цифры, называется семантическое версионирование (SemVer).

Без правил 1.0 → 1.1 могло бы означать что угодно: мелкую правку или полное переписывание. SemVer убирает гадание.

MAJOR.MINOR.PATCH

Версия из трёх чисел, разделённых точками:

  2  .  4  .  1
MAJOR MINOR PATCH

Правило простое: ломаешь совместимость — поднимай MAJOR. Добавляешь, не ломая — MINOR. Чинишь — PATCH.

До релиза версия часто начинается с 0.x. Это значит «API ещё не стабилизирован, может меняться в любой момент». Предрелизы помечают суффиксом: 1.0.0-rc.1, 2.0.0-beta.

Диапазоны в зависимостях

SemVer нужен, чтобы менеджеры пакетов умели обновлять зависимости безопасно. В package.json встречаются такие записи:

"express": "^4.18.2",
"lodash": "~4.17.21"

Логика опирается на SemVer-обещание: внутри одного MAJOR ничего не ломается, поэтому ^ обновлять относительно безопасно. Если пакет не держит SemVer честно, это обещание не работает. Отсюда lock-файлы, которые фиксируют точные версии для воспроизводимости.

Особый случай: версии 0.x

Правило «caret держит MAJOR» перестаёт работать, когда MAJOR равен нулю. Для нулевых версий SemVer вообще не даёт гарантий: 0.x считается периодом активной разработки, и ломающее изменение там выпускают в MINOR.

Менеджеры пакетов это учитывают и ужимают диапазон:

^1.2.3   →  >=1.2.3 <2.0.0     обновляется до 1.9.9
^0.2.3   →  >=0.2.3 <0.3.0     до 0.3.0 не поднимется
^0.0.3   →  >=0.0.3 <0.0.4     то есть только эта версия

Ошибка тут стоит дорого именно потому, что интуиция говорит обратное. Человек читает ^0.2.3, думает «обновится в пределах нуля, доедет до 0.9», ставит зависимость и ждёт свежих версий. А npm честно держит его на ветке 0.2.x и не подтягивает даже критичные фиксы, выпущенные в 0.3.1.

Практический вывод: у зависимости на 0.x следи за обновлениями руками. Автоматика их не привезёт.

Связь с git-тегами

Версию в git фиксируют тегом — именованной меткой на конкретном коммите. По соглашению тег версии начинается с v:

git tag -a v2.4.1 -m "release 2.4.1"
git push origin v2.4.1

Тег v2.4.1 навсегда указывает на коммит, из которого собран этот релиз. CI обычно настроен так, что push тега запускает сборку и публикацию артефакта с этой версией. Так номер версии, коммит и собранный образ оказываются жёстко связаны.

Что важно запомнить

SemVer читается как MAJOR.MINOR.PATCH: MAJOR ломает совместимость, MINOR добавляет без поломок, PATCH чинит.

^ в зависимостях разрешает обновления внутри одного MAJOR, ~ — только PATCH. Lock-файлы фиксируют точные версии, когда пакеты держат SemVer нечестно.

Для нулевых версий caret ведёт себя иначе: ^0.2.3 это >=0.2.3 <0.3.0, а ^0.0.3 вообще пришпиливает одну версию. Такие зависимости обновляют руками.

Версию в git закрепляют тегом vX.Y.Z; push тега обычно запускает сборку релиза в CI.

В следующем уроке разберём теги и релизы в git подробнее, и как из них собираются release-страницы.

Прошли материал до конца? Отметьте урок пройденным.