Когда разработчик видит номер версии вроде "2.3.7", он подсознательно ожидает определённое поведение от обновления. Он предполагает, что обновление безопасно, что код не сломается и что изменения будут ограниченными. Именно для этого и существует версионирование.
Версионирование - это способ заранее предупредить других людей о последствиях обновления. Если версии присваиваются без правил, цифры теряют смысл, а обновления становятся лотереей.
Зачем версионирование нужно на практике
Рассмотрим несколько типичных ситуаций.
Ситуация 1. Обновление библиотеки
Проект использует библиотеку версии 1.4.2. Вы обновляете её до 1.4.3 и ожидаете:
только исправления ошибок
отсутствие изменений в API
стабильную работу
Если же после такого обновления код ломается, значит правила версионирования не соблюдаются.
Ситуация 2. Большой релиз продукта
Вы видите релиз 3.0.0 и понимаете:
появились серьёзные изменения
возможно, придётся адаптировать код
нужно читать описание релиза
Номер версии уже сообщает важную информацию, ещё до прочтения changelog.
Основные подходы к версионированию
Простая нумерация
v1
v2
v3Такой подход ничего не говорит о характере изменений. Версия v10 может отличаться от v9 одной строкой кода, а может быть полностью переписанной.
Версионирование по дате
2024.11
2025.02Хорошо подходит для продуктов с регулярными релизами. Например, операционные системы или SaaS-сервисы. Но здесь тоже нет информации о совместимости.
Semantic Versioning
Semantic Versioning вводит чёткий формат, который помогает понять какой тип изменений ожидать в обновлении:
MAJOR.MINOR.PATCHДалее рассмотрим Semantic Versioning подробнее.
Semantic Versioning (SemVer)
Стандарт, который описывает не просто номер версии, а смысл изменений. Он показывает, безопасно ли обновляться и чего ожидать от релиза. Именно этот подход чаще всего используется в библиотеках, фреймворках и API.
Рассмотрим каждую часть подробнее:
PATCH: исправления без последствий
PATCH увеличивается, когда исправляются ошибки и ничего не ломается.
Примеры изменений:
исправлен баг в расчёте скидки
устранена утечка памяти
поправлена валидация формы
Пример версий:
1.0.0 → 1.0.1
1.2.5 → 1.2.6Пример коммита:
fix(cart): исправлен расчёт итоговой суммыПользователь может обновляться без опасений.
MINOR: новые возможности без ломки
MINOR увеличивается, когда добавляется функциональность, но старый код продолжает работать.
Примеры изменений:
добавлен новый API-метод
добавлена настройка, но старая логика сохранена
расширен ответ API новыми полями
Пример:
1.3.0 → 1.4.0Пример коммитов:
feat(profile): добавить аватар пользователя
feat(api): вернуть поле "created_at" в ответеВажно: если старый код не ломается, это не MAJOR-изменение.
MAJOR: ломающие изменения
MAJOR увеличивается, когда изменения могут сломать существующий код.
Типичные причины:
удалён метод или эндпоинт
изменён формат ответа API
изменено поведение функции
обязательные параметры изменены
Пример:
1.9.2 → 2.0.0Пример коммита:
feat(api): изменить формат ответа пользователя
BREAKING CHANGE: поле "name" заменено на "full_name"Или короткий вариант:
feat!: изменить контракт API пользователяЭто прямой сигнал о необходимости обновления интеграций.
Что такое пре-релизы на практике
Пре-релизы - это промежуточные версии между текущей стабильной версией и будущим полноценным релизом. Они используются, когда изменения уже готовы, но ещё не считаются полностью стабильными.
В SemVer такие версии обозначаются дополнительным суффиксом:
2.0.0-alpha
2.0.0-beta
2.0.0-rc.1Примеры использования:
alpha - ранняя версия для команды
beta - версия для тестирования пользователями
rc - кандидат в релиз
Такие версии помогают найти ошибки до стабильного релиза.
Также, важно понимать, что любые пре-релизы считаются нестабильными и по умолчанию не предназначены для продакшена.
Conventional Commits с примерами
Conventional Commits - это способ писать коммиты так, чтобы их понимали и люди, и инструменты.
Формат:
type(scope): описаниеПримеры хороших коммитов
fix(auth): исправлена ошибка обновления токена
feat(search): добавить поиск по тегам
docs(readme): обновить инструкцию запуска
refactor(order): упростить логику расчёта
chore(ci): обновить pipeline сборкиПо одному коммиту уже понятно, что произошло.
Примеры плохих коммитов
update
fix
changes
workТакие сообщения не несут информации и бесполезны для истории проекта.
Breaking changes в Conventional Commits
Изменения, которые могут поломать код, всегда должны быть явно обозначены.
Пример:
feat(auth): изменить механизм авторизации
BREAKING CHANGE: токены старого формата больше не поддерживаютсяИли:
feat!: удалить устаревший метод логинаЭто позволяет автоматически увеличить MAJOR-версию.
Связь между коммитами и версиями
На практике работает простое правило:
fix→ PATCHfeat→ MINORfeat!илиBREAKING CHANGE→ MAJOR
Пример истории коммитов:
fix(ui): исправить отображение кнопки
feat(profile): добавить редактирование профиляСледующая версия:
1.2.3 → 1.3.0Если появляется breaking change:
feat!: изменить формат настроекВерсия становится:
2.0.0Changelog на основе коммитов
Хороший changelog может выглядеть так:
## 2.1.0
- Добавлена фильтрация заказов
- Улучшена производительность поиска
## 2.0.0
- Изменён формат API заказов
- Удалена устаревшая логика авторизации
## 1.9.4
- Исправлена ошибка расчёта доставкиОн формируется автоматически из истории коммитов.
Когда всё это действительно нужно
SemVer и Conventional Commits особенно полезны, если:
проект развивается долго
над кодом работает команда
есть внешние пользователи или API
используется CI/CD
важно поддерживать обратную совместимость
Для MVP или прототипа можно начать проще и внедрять правила постепенно. Они придадут проекту порядок и прозрачность.