Эволюция методологий версионирования
Эволюция методологий версионирования

Привет, Хабр. Всех с наступившим Новым Годом.

На днях наткнулся на статью Махмуда Хашеми, в которой обсуждаются некоторые недостатки методологии семантического версионирования (SemVer), и в качестве решения этих недостатков предлагается использовать календарное версионирование (CalVer). В организации, где я работаю, по стандарту разработки требуется обязательно версионировать приложения по SemVer. Из собственного опыта использования SemVer скажу, что нашёл в ней ещё ряд недостатков, для исправления которых пришлось искать новый способ версионирования.

Что ещё не так с SemVer?

Кратко напомню схему семантического версионирования:

MAJOR.MINOR.PATCH

Где MAJOR изменяется при несовместимых изменениях, MINOR - при совместимом добавлении функционала, и PATCH - при совместимом исправлении ошибок.

Не буду пересказывать упомянутую выше статью, рекомендую всем желающим ознакомиться с её доводами самостоятельно. Сразу перейду к проблемам, с которыми столкнулся лично. А для этого немного погружу в контекст своих условий разработки.

Определение версии в CI

Я работаю в финтех организации и разрабатываю несколько микросервисов крупной системы. Мы используем стандартизированный pipeline сборки с доставки наших приложений. У нас настроен CI/CD, который собирает, проверяет и доставляет доработки в промышленный контур автоматически после сливания pull request в главную ветку. Такая схема требует на начальном этапе сборки автоматически установить версию приложению. И вот тут как раз SemVer даёт сбой!

Не существует алгоритма, который позволяет определить, совместимые или же нет сделаны доработки по отношению в предыдущей версии. И более того, даже сами разработчики не всегда это могут определить.

К примеру, мы декомпозируем одну полноценную доработку на мелкие задачи, после выполнения каждой из которых часть целой доработки в отключённом состоянии проходит весь pipeline и устанавливается в промышленный контур. Только когда все части доработки будут реализованы и протестированы, тогда эту доработку можно будет включить в промышленном контуре. При этом ещё одна пересборка не требуется, достаточно переключить тоггл в конфигурации. Учитывая такой подход, в какой момент мы должны повысить минорную версию? При выполнении первой части доработки, или последней? А если параллельно выполняется несколько доработок?

На практике, в такой серьёзно автоматизированной системе SemVer вырождается в следующую схему:

STATIC_PREFIX.BUILD_NUMBER

Где STATIC_PREFIX - практически всегда 1.0, а BUILD_NUMBER - номер сборки в pipeline. Фактически версионирование свелось к обычному порядковому номеру, с бесполезной припиской.

Свои сервисы я разрабатываю уже несколько лет, и за это время пережил несколько реализаций pipeline. На каждом новом pipeline счётчик сборки обнулён, и чтобы избежать конфликта версий, в моём случае схема выглядит следующим образом:

STATIC_PREFIX.PIPELINE_NUMBER.BUILD_NUMBER

Где STATIC_PREFIX равен 1, а PIPELINE_NUMBER увеличивается при смене pipeline. Вот такой SemVer.

Восприятие SemVer

Помимо разработки я ещё провожу аттестацию других разработчиков. Вопрос о SemVer задаётся всем сотрудникам в обязательном порядке. Вот тут я был очень удивлён, но многие разработчики не понимают, когда и какую часть версии следует увеличивать. Конечно это более характерно начинающим разработчикам, но и от матёрых ребят доводилось слышать, что после версии 1.0.9 должна следовать версия 1.1.0.

Задавая конкретный вопрос: "Когда увеличивается мажорная часть версии?", я часто получаю ответ "Когда происходят масштабные доработки программы". Эта проблема кстати тоже упоминается в указанной в начале статье.

Практика показывает, что увеличение мажорной части версии, далеко не всегда сопровождается "масштабными" изменениями. Оно скорее всего сопровождается масштабным рефакторингом с вашей стороны. Мой любимый нелюбимый пример такого изменения версии - переход со spring boot 2 на spring boot 3, где нужно по всему коду и по всем библиотекам изменить пакеты javax на jakarta.

Всё это заставляет сделать вывод, что части семантической версии вовсе не очевидны по своему смыслу, и создают заблуждения среди разработчиков.

Несколько мажорных версий

Вернёмся к примеру со spring boot. Требуемый рефакторинг для перехода между мажорными версиями нельзя сделать частично, поскольку невозможно одновременно использовать несколько версий одной библиотеки. И даже такой незначительный рефакторинг, как смена названий пакета, не всегда можно сделать за один раз. Либо всё, либо ничего.

Кроме того, выпуск новой мажорной версии означает прекращение развития предыдущей мажорной ветви приложения. В лучшем случае выпускаются исправления безопасности.

Разработчик в таких условиях попадает в неопределённую ситуацию: на одной чаше весов сложный рефакторинг, который не принесёт явной пользы, поскольку нет нового функционала, с другой - накопление техдолга, который только сильнее будет осложнять рефакторинг. Скажите, много ли из вас тех, кто уже забыл про java8?

И даже если рефакторинг стоит затраченного на него времени, всё равно будет сложный переходный период. Вспомните как изощрялись linux дистрибутивы для одновременной поддержки python 2 и 3.

CalVer - календарное версионирование

Календарное версионирование преподносится как способ решение проблем семантического версионирования. У CalVer нет строгой схемы, есть только набор рекомендаций и вариантов построения, и каждый может настроить вид версии на свой вкус. Но если брать условно среднее, то схема получится такой:

YY.MM.PATCH

Где YY - год, MM - месяц, PATCH - порядковый номер.

Посмотрим как CalVer решит описанные выше проблемы:

  1. Автоматически рассчитать и назначить версию можно без проблем, поскольку требуется лишь текущая дата.

  2. С восприятием тоже всё хорошо. Год и месяц поймут даже люди далёкие от разработки ПО.

  3. А вот проблему нескольких мажорных версий с помощью CalVer решить не получится.

Недостатки CalVer

Хоть CalVer не решает все наши проблемы, она достаточно хороша. Признаюсь честно, до момента возникновения замысла этой статьи, я и сам склонялся к идеи использовать CalVer для своих приложений. Для меня, и для многих других разработчиков её достаточно. Но...

В процессе решить проблему восприятия версии и защиты от неоправданных ожиданий при мажорном переходе, CalVer потерял важное свойство, которое присутствовало в семантическом версионировании.

По CalVer при переходе с одной версии на другую невозможно понять, были ли несовместимые изменения в новой версии по отношению к старой!

Я считаю это очень серьёзным недостатком методологии, даже не смотря на то, что в моих приложениях этот недостаток скорее всего бы не проявился.

Требование к методологии версионирования

В статье, на которую я сослался в начале, выдвигаются 3 требования, которые должны выполняться при версионировании приложений:

  1. Версия должна быть числом.

  2. Версия должна только увеличиваться.

  3. Номер версии должен коррелировать с уровнем развития ПО. Чем выше версия, тем более зрелое приложение.

Я предлагаю добавить ещё 4 требования.

4. Все части версии должны нести смысл

Не должно в версии быть констант вроде 1.0. Каждая часть версии должна давать какую-то полезную информацию.

5. Версия должна отражать несовместимые изменения

Не должна повторяться ошибка, которую допустили в CalVer.

6. Версия должна строго вычисляться алгоритмически

Требования для удобства настройки CI/CD.

7. Пересборка одной и той же версии кода должна давать тот же результат

Последний пункт больше относится к системе сборки, но методология версионирования точно не должна его нарушать.

Внезапно немного биологии

В современной биологической систематике принято классифицировать живые организмы согласно их эволюционному развитию. Наименьшим таксоном такой классификации является Вид. Однозначно дать определение биологическому виду сложно, но для нашего контекста очень удачно подходит определение Эрнста Майра:

Вид — это репродуктивно связанная совокупность популяций.

Другими словами, особи одного вида способны свободно скрещиваться между собой и давать плодовитое потомство. Как только между особями теряется репродуктивная совместимость, то можно говорить о разделении на новые виды. А для нового вида необходимо придумать новое название.

Скрытый текст

Я понимаю, что при биологическом разделении невозможно сказать, какой из видов новый, по отношению к исходному. Более правильно сказать, что исходный вид вымер, но при этом эволюционировал в 2 или более разных новых видов.

К чему была эта биологическая вставка? При несовместимых изменениях между особями одного вида, выделяется новый вид. Давайте воспользуемся аналогичным приёмом при версионировании приложений.

EvoVer - эволюционное версионирование

Предлагаю ввести новую методологию, в которой возьмём лучшие свойства из SemVer, CalVer и биологической систематики. К вашему вниманию - эволюционное версионирование (EvoVer).

SPECIES EPOCH[.PERIOD].GENERATION
ВИД     ЭПОХА[.ПЕРИОД].ПОКОЛЕНИЕ

Где:

  • EPOCH (ЭПОХА) - увеличивается при переходе в следующую эпоху. Лучше всего использовать год, в любом удобном для вас формате: 2026, 26.

  • PERIOD (ПЕРИОД) - опциональный элемент, увеличивающийся при переходе в следующий период. Это может быть квартал, месяц, неделя, спринт или что-нибудь ещё.

  • GENERATION (ПОКОЛЕНИЕ) - порядковый номер сборки в периоде/эпохе.

  • SPECIES (ВИД) - название вашего приложения/сервиса/библиотеки/фреймворка/плагина/API. Изменяется при несовместимых изменениях.

А теперь попытаюсь предвидеть некоторые ваши вопросы и возражения.

Это же тот же CalVer, только с другими названиями!

Действительно, часть EPOCH[.PERIOD].GENERATION это и есть CalVer практически в чистом виде. Главное отличие здесь в том, что мы запрещаем несовместимые изменения в приложении, чего не было в CalVer.

Когда вы хотите сделать несовместимое изменение, вы должны создать другое приложение с другим названием.

Можете воспринимать EvoVer, как CalVer 2.0.0, но так как мы запрещаем несовместимые изменения, то придумываем для методологии новое название - EvoVer.

Почему именно ЭВОЛЮЦИОННОЕ версионирование?

Вы постоянно делаете доработки своего приложения. Выпуская следующую версию, ваш код улучшается, адаптируясь при этом под современные условия и требования. Версия на основе даты при этом отражает хронологию поколений вашего приложения.

И когда вам необходимо внести несовместимые изменения, вы не сбрасываете версию в 1.0.0 или даже в 0.0.1, а продолжаете развитие вашей идеи под новым названием. Вы не начинаете новое приложение с чистого листа, вы создаёте новую ветвь в дереве программного обеспечения.

Именно так и происходит эволюция.

А зачем новые названия для частей версии?

Исключительно поэтический момент. Чтобы была корреляция с названием методологии.

Первое требование - версия должна быть числом, а здесь появляется название приложения!

А версия и является числом! За версию отвечает часть EPOCH[.PERIOD].GENERATION, которая состоит только из чисел. SPECIES введён для того, чтобы отразить принцип работы с несовместимыми изменениями.

Почему PERIOD определён не чётко, и ещё опциональный?

Для той же гибкости, которая присуща CalVer.

В компании, где я работаю, используется квартальное планирование. Задачи, бюджет, отчётность, всё строится и выделяется на квартал. Логично и PERIOD использовать для указания квартала и увеличивать число в течении года от 1 до 4. Такой подход будет понятен бизнесу, и по номеру версии можно определять прогресс за учётный период.

Но не все компании используют квартальное планирование. Кому-то удобно будет использовать номер месяца, кому-то недели, кому-то спринты или что-нибудь ещё. Но если вы не видите полезного применения для PERIOD, то вспомните четвёртое требование: "Все части версии должны нести смысл". Не нужно - не используйте.

А если у меня несущественные несовместимые изменения. Зачем ради этого менять название?

Смотрите на свою ситуацию сами. Я могу сказать только одно:

Несовместимые изменения и точка!

Если вы тоже переходите со spring boot 2 на 3, то для конечных сервисов это не должно повлиять на совместимость, но для стартеров придётся придумать новое название.

Если вы хотите удалить устаревший метод, то лучше пометьте его deprecated. А когда устаревшего кода будет слишком много, и поддерживать его будет тяжело - создайте новое приложение. Считайте вашу первую попытку черновиком. А теперь настало время переписать всё на чистовик.

У меня программа в процессе проектирования, мы ещё не нашли нужное API.

Речь о мажорной версии 0. Посмотрите ещё раз, что я написал в предыдущем пункте. Если вы хотите несовместимо переделать API, у вас 2 пути:

  1. Написать рядом, а старое пометить deprecated.

  2. Создать новое приложение без всего лишнего.

Это относится не только к приложениям, но и к библиотекам, фреймворкам, API, плагинам, и ко всему, что можно версионировать.

Зато представьте какие вы получите от этого преимущества:

  • Выпуская новое API под новым названием, вы можете использовать обе свои реализации одновременно на время переходного периода. Не нужен крупный рефакторинг всего приложения, можно переходить по частям.

  • В новом API вы сможете предоставить адаптер для предыдущего решения, и упростить переход для ваших потребителей.

  • Гораздо проще становится управление зависимостями. Указывая версию зависимости, вы определяете минимально необходимую версию. Для сборки можно спокойно выбрать максимальную из требуемых транзитивно версий, а можно и просто самую свежую.

  • Если вам ваш проект кажется устаревшим, то не факт, что с вами согласятся те, кто от него зависят. Позвольте им продолжить развитие вашей первоначальной идеи, а сами развивайте новую.

Задумайтесь над тем, что вы пытаетесь сохранить при выпуске новой мажорной версии. Вы сохраняете только название! Нужно ли оно вам так сильно, чтобы попортить жизнь коллегам, которые зависят от вашего ПО?

Название является брендом! Мы вложили в него много средств и собрали вокруг инструмента комьюнити. Нам не выгодно менять его!

Спросите любого разработчика, что он думает по поводу перехода со spring boot 2 на spring boot 3. Ни один разработчик не будет в восторге от переименования javax на jakarta. Выпуск новой мажорной версии - удар по репутации.

Есть много случаев, когда несовместимой версии давали новое название, и это приводило к успеху:

А так же есть случаи, когда не стали переименовывать, и создали этим проблемы и скандалы:

  • Выпуск python 3 заставил linux дистрибутивы поддерживать обе версии языка, искусственно вводя пакет python3 наравне с python, что собственно и можно рассматривать как разные названия.

  • Gnome переделав полностью интерфейс в версии 3 получили массу критики, в том числе и от Линуса Торвальдса. По итогу мы получили развитие прошлой оболочки под названием Mate.

    Скрытый текст

    Лично мне новый gnome нравится больше

  • Ну и мой любимый нелюбимый пример, передача JavaEE в Eclipse с переименованием в JakartaEE, что породило огромное число бесполезных мажорных переходов, в роде spring boot 3, хотя можно было сделать переход аккуратнее.

Если вы всё же так дорожите своим названием, попробуйте найти компромисс:

  • Используйте дополнительное название для вашей мажорной версии, как это делается в Ubuntu.

  • Или включите мажорный номер в ваше название, а не в версию, как это делает Windows.

Но если мои доводы вас не убедили, и компромисс вас не устраивает, то поищите вдохновение в методологии версионирования, принятой в Haskell. Там целых 2 мажорные версии.

Итог

Я бы не сказал, что EvoVer является чем-то радикально новым. EvoVer - очередной шаг в развитии систем версионирования. Многие уже приходили к аналогичным мыслям. Даже автора SemVer посещала похожая идея.

Теперь EvoVer отправляется на ваш суд. В конце лишь приведу сводную таблицу со сравнениями всех методологий:

SemVer

CalVer

EvoVer

Требования к версионированию

Версия должна быть числом

+

+

+

Версия должна только увеличиваться

+

+

+

Номер версии должен коррелировать с уровнем развития ПО

+

+

+

Расширенные требования

Все части версии должны нести смысл

+

+

+

Версия должна отражать несовместимые изменения

+

-

+

Версия должна строго вычисляться алгоритмически

-

+

+

Пересборка одной и той же версии кода должна давать тот же результат

+

+

+

Дополнительные свойства

Прозрачный переход мажорной версии от 0 к 1

-

+

+-

Неоправданные ожидания от мажорного перехода

-

+

+

Существования нескольких мажорных версий

-

-

+

Комментарии (38)


  1. kenomimi
    04.01.2026 11:03

    Я бы сделал это как SPEC.YYYY.[MM * 10000 + GEN] - 10к пересборок врядли возможны за месяц, а если возможны - ну дорисовать еще нолик. Первый номер сидит в переменных гитлаба, и меняется ручками ооочень редко, остальное вычисляется на лету. При этом оно вполне информативно.


    1. tuzhms Автор
      04.01.2026 11:03

      А зачем объединять месяц и порядковый номер? Почему так же через точку не указать?


  1. ruomserg
    04.01.2026 11:03

    Требование "сделать новое приложение при значительных изменениях" - абсолютно нереалистично в энтерпрайзе, IMHO - и будет источником дикого оверхеда в репозиториях, пайплайнах, и проч...

    Я бы предложил не усложнять себе жизнь без причины, и продолжать пользоваться либо SemVer, либо просто инкрементальными номерами билдов (это когда приложение уже который год 0.1.X, и X>200 :-)

    А разработчикам объяснять смысл версионирования, а не правила. Ведь смысл версионирования в чем - если у нас на проде есть версия X.Y.Z, а нас просят откатить до A.B.C - то надо или внимательно читать changelog (или git diff), или:

    • Отличие только в последнем компоненте - откатывать можно без проблем, оно не упадет.

    • Отличие в средней цифре - смотреть changelog, ибо там может быть что-то необратимое (а-ля миграция в БД), а может быть - и нет. Протестировать новую конфигурацию надо бы, но скорее для собственного успокоения.

    • Отличие в major-версии - гарантированно сломается все вокруг. Если очень надо - то это целый проект с селективным даунгрейдом соседних сервисов и обязательным тщательным тестированием совместимости.

    И, соответственно, разработчик должен в своей голове прокручивать эти сценарии, и решать что он поменял, и какую condensed-информацию он доносит изменением версии. Если не уверен - озвучивать на дейлике, и пусть команда решает...


    1. tuzhms Автор
      04.01.2026 11:03

      IMHO - и будет источником дикого оверхеда в репозиториях, пайплайнах, и проч...

      Прикинул насколько реалистично реализовать такое в нашей компании. Репозиторий создаётся одной кнопкой. Чтобы шаблонный пайплайн развернуть, нужно заполнить небольшой конфиг на пол экрана и файл стендозависимых параметров, за пол часа сделать можно.

      Вот ресурсы для запуска выделить не так просто. Нужно как минимум обратиться к руководителю, чтобы сервер выделил на тесте и проме. Но несказать, что это невозможно, всё же несовместимые версии не часто выпускаются. Зато если к руководителю разарботчик 5 раз за год приходит, и говорит, что снова несовместимо поменял API, у руководителя возникнут вопросы к этому разработчику.

      Самая сложная проблема в бюрократии: нужно подготовить арх документацию, согласовать всё с безами, выпустить сертификаты, роуты создать, проходы открыть. Вот это действительно проблема. 10 раз подумаешь, нужны ли тебе несовместимые изменеия.

      Вообще я бы сказал, что в интерпрайзе несовместимые изменения - редкость. Сделать новое API не проблема, а вот заставить всех потребителей бросить продуктовые задачи и переписывать работающую интеграцию очень не просто.


    1. tuzhms Автор
      04.01.2026 11:03

      Ведь смысл версионирования в чем - если у нас на проде есть версия X.Y.Z, а нас просят откатить до A.B.C - то надо или внимательно читать changelog (или git diff)

      А часто вам приходилось откатываться на предыдущую мажорную версию?

      У нас требуется все доработки закрывать тогглами. Поэтому если что-то ломается после релиза, сразу откатываемся на предыдущую версию и чиним, что поломали. Если что-то ломается после включения тоггла, то выключаем тоггл, и чиним новую доработку.


  1. Vindicar
    04.01.2026 11:03

    Ну т.е. у вас получилось что-то типа убунтовской схемы?
    Софтина "Баба Яга" 24.4, Софтина "Баба Яга" 25.4, Софтина "Горыныч" 25.9, Софтина "Кошей" 26.4 и т.д.?


    1. tuzhms Автор
      04.01.2026 11:03

      Да, главное чтобы в зависимостях название изменилось при несовместимом изменении, и можно было постепенно провести переход


  1. shasoftZ
    04.01.2026 11:03

    MyApp 1.0
    MyApp 1.1
    MyApp 1.2
    MyApp2 1.3
    MyApp2 1.4
    MyApp3 1.5

    Т.е. мы просто MAJOR номер переносим в имя. А если имя делать совсем другое, то встретив два приложения вы не сможете понять что это одно и тоже приложение


    1. tuzhms Автор
      04.01.2026 11:03

      Т.е. мы просто MAJOR номер переносим в имя

      Так можно, но я бы это считал именно компромисным решением.

      А если имя делать совсем другое, то встретив два приложения вы не сможете понять что это одно и тоже приложение

      А это не одно и то же приложение. Да, они оба могут выполнять одну задачу, и одно может быть логическом продолжением другого, но приложения разные.

      Я пока искал примеры переименований мажорных разного ПО, наткнулся где-то, что Hadoop MapReduce вместо выпуска следующей мажорной версии сделали отдельный проект Spark. Прям идеальный пример для статьи. Стал икать ему подтверждение, но не нашёл. Пришёл к выводу, что Spark делала независимо другая команда, но впечатлялась реализацией Hadoop MapReduce, решая при том проблемы Hadoop MapReduce.

      В статью этот пример включать не стал, но вопрос филосовский, подходит он или нет. Вся разница - та же самая команда выпускает новую библиотеку, или другая похожую. Обе библиотеки решают одну и ту же проблему, обе используеются в одних и тех же кейсах, они разные, но зато обе можно одновременно использовать в одном приложении, и постепенно перейти с одной на другую. А при выпуске новой мажорной версии, вы не сможете реализовать постепенный переход, но рафакторинг может потребоваться не меньше, чем переход на другую бибилиотеку.


      1. shasoftZ
        04.01.2026 11:03

         Да, они оба могут выполнять одну задачу, и одно может быть логическом продолжением другого, но приложения разные.

        Так это и плохо, на мой взгляд. Не сразу понятно что программа B это развитие программы A


  1. MonkAlex
    04.01.2026 11:03

    Ну т.е. семвер не осилили, придумали свой велосипед?

    То что у вас семвер скатился к 1.0.%buildnumber% не значит что в семвере проблема.

    UPD: почитал статью Махмуда Хашеми по ссылке и это какой-то... мусор? Типа "смотрите, кто-то может не соблюдать семвер, поэтому семвер плохой".


    1. tuzhms Автор
      04.01.2026 11:03

      Я не утверждаю, что SemVer плохой. Когда он появился, он решал проблемы, которые на тот момент существовали, когда все использовали GitFlow и нужно было решать проблемы с зависимостями.

      Сейчас мы работаем по TBD. Количество установок в пром - одна из метрик, по которой моё руководство оценивает работу разработчиков, и чем выше тем лучше. В таких условиях приходится все процессы максимально автоматизировать, и вставлять в пайплайн ручную операцию по установки версии сборки неразумно.

      Но а в то, что по SemVer невозможно автоматически расчитать версию - это факт, с которым нет смысла спорить.

      SemVer для моих условий не подходит, но как и писал в статье, мои проблемы решаются с помощью CalVer. Но Махмуд Хашеми в своей статье приводит CalVer, как универсальное решение. Я не считаю CalVer универсальным решением. Он не подходит для библиотек и фреймворков. И попытался найти способ сделать более универсальную методологию.

      почитал статью Махмуда Хашеми по ссылке и это какой-то... мусор

      Для меня эта статья стала поводом задуматься. Автор решал проблемы, которые для него казались критичными. Я релаю проблемы, которые мне кажутся критичными


      1. MonkAlex
        04.01.2026 11:03

        Версию используете для чего-то? Почему бы тогда просто id пайплайна не использовать или генерируемый растущий номер?

        Потому что если версия используется кем-то кроме вас лично, то там семвер удобен. Если только вы сами - то что угодно делайте, пока сами понимаете что и зачем.

        Велосипеды делать, когда кто-то кроме вас будет этим пользоваться - очень грустная история.

        ПС: версии можно поднимать просто при мержах. Либо руками (разработчик ломает совместимость, поднимает версию), либо как то автоматизируя (https://gitversion.net/docs/reference/version-increments вот тут v3 позволяет делать это разработчику). Никто кроме разработчика не знает, сломал он совместимость или нет. И даже разработчику об этом думать надо, не знаю, может нынче какой-нибудь ИИ инструмент может помочь.


      1. powerman
        04.01.2026 11:03

        Но а в то, что по SemVer невозможно автоматически расчитать версию - это факт, с которым нет смысла спорить.

        SemVer прекрасно рассчитывается автоматически на базе префиксов коммитов (feat!:, feat:, fix:, etc.).


        1. thethee
          04.01.2026 11:03

          А это уже культура разработки, которой может не быть, человеческий фактор, не проставил, или проставил не то, не та версия инкрементнулась. В этом плане календарное версионирование решает проблему.

          В принципе какая разница что использовать если это используется и обосновано конкретно у вас в компании. Хоть A/B/C пишите вместо версии если вы 3 версии в год выпускаете, главное чтобы клиентам было понятно...

          Или пишите четвертую цифробукву в семантическом/календарном версионирование, первые три стандартизированы и дальше хоть id пайплайна, хоть инкрементальный id сборки. Даже после прочтения статьи не очень понимаю какую именно проблему пытается решить автор. Чтобы все использовали новую улучшенную версию версионирования? А смысл?..

          Вот у моего приложения одно версионирование, у приложения Яндекс Музыка другое, у JetBrains PyCharm третье, у Ubuntu четвертое. Я вижу что циферка пошла вверх, я понимаю что у меня устаревшая система. Какая вот мне разница это цифра билда или количество звезд на гитхабе в момент сборки...


          1. powerman
            04.01.2026 11:03

            А это уже культура разработки, которой может не быть, человеческий фактор, не проставил, или проставил не то, не та версия инкрементнулась.

            Это решаемо. Например, утилита https://git-cliff.org/ позволяет вручную корректировать такие "плохие" описания коммитов. В ней это сделано для генерации ChangeLog из коммитов, но она и SemVer отлично вычисляет (и её удобно использовать для определения версии на CI).

            В этом плане календарное версионирование решает проблему.

            И CalVer и EvoVer - это просто альтернативные/красивые способы записи BuildID. Никакой проблемы помимо увеличения номера версии они не решают.


  1. inkelyad
    04.01.2026 11:03

    При этом ещё одна пересборка не требуется, достаточно переключить тоггл в конфигурации. 

    Тут единицей, подлежащей версионированию, должен быть набор собранного бинарника и включенных тогглов. Его версия меняется, когда тоггла переключают.

    "Мы запустили систему версии X.Y+1.Z". А то что бинарник не поменялся - это уже особенности имплементации. Он подчиненный артефакт, составная часть приложения версии X.Y+1.Z
    Что касается самого бинарника - я бы инкрементировал версию тогда, когда изменения становятся хоть кому-то видны(включая тестовые стенды).


    1. MonkAlex
      04.01.2026 11:03

      А тоглы важны в этой схеме? Я по статье не понял, если честно.

      Контракты апи нужно версионировать, но это должно быть сделано при билде. Добавили контракты, апнули версии. Сломали контракты - отметили факт в версии. Включен или выключен тогл, вроде как особенность внутренней реализации сервиса и на поведение (в том числе версии чего либо) влиять не должно.


      1. inkelyad
        04.01.2026 11:03

        А тоглы важны в этой схеме? Я по статье не понял, если честно.

        Это же тоггл функционала. Пока не переключен - то ничего нового как бы и нет, хотя в бинарнике и присутствует. Т.е. видимая версия(системы, не бинарника) должна оставаться той же (ну или patch версию можно увеличить опять же у всей системы)


      1. tuzhms Автор
        04.01.2026 11:03

        Тогглы в схеме не важны. Про тогглы написал лишь для примера, что не всегда очевидно, как по SemVer инкрементировать версию.


        1. MonkAlex
          04.01.2026 11:03

          У семвера по моему вполне ясно описано, как инкрементировать. Если тоглы не важны, то зачем про них писать?


        1. inkelyad
          04.01.2026 11:03

          Тогглы в схеме не важны.

          Это невозможно. Если они что-то меняют, то система с одними включенными тогглами - это не та же система, что с другими.

          Это другая версия системы, с другими возможностями, несмотря на то что бинарные артефакты те же самые. И этот набор возможностей вполне можно (и очень желательно) версионировать.


          1. tuzhms Автор
            04.01.2026 11:03

            Команду ls можно запустить с разными ключами и получить от этого разное поведение. Но это не значит, что вы запускаете разную версию команды.


    1. tuzhms Автор
      04.01.2026 11:03

      В таком случае непонятно, что вы версионируете?

      По мне, версия должна быть установленна при сборке артефакта, в CI. Состояние тогглов - это стендозависимые параметры.


      1. inkelyad
        04.01.2026 11:03

        В таком случае непонятно, что вы версионируете?

        Весь сервис целиком. (Или, в другой терминологии - конфигурация системы)
        Условно:
        Service ver. 3.4.5
        depends on:
        binary ver.: 3.5.6
        toggle set: 3.4.5

        Когда тоггл перекидывается, ставится

        Service ver. 3.5.1
        depends on:
        binary ver.: 3.5.6
        toggle set: 3.5.1

        И происходит сборка/деплой. Но не бинарника (он тот же самый остается), а всего сервиса.

        установленна при сборке артефакта, в CI. Состояние тогглов - это стендозависимые параметры.

        Состояние/конфигурация стенда - это тоже артефакт.

        Если после переключения тогглов стенд начал что-то дополнительное уметь - значит, вот этот весь стенд должен добавить в минороной версии.
        Если обратную совместимость при этом сломали (т.е. то/те, кто им пользуются, начали ломаться) - то должна повысится и можорная версия.


        1. tuzhms Автор
          04.01.2026 11:03

          Понял, о чём вы. В вашей терминологии я подразумевал binary ver.

          Состояние/конфигурация стенда - это тоже артефакт.

          С этим соглашусь. Но мы тогглы рассматриваем не набором с текущим состоянием. У нас каждый тоггл живёт независимым от остальных жизненным циклом.

          Подход, который вы предложили тоже может существовать. Но вам понадобилось 2 версии по semver, чтобы описать текущую версию запущенной системы. На мой вкус - это лишь подтверждает, что в данном примере использование semver не так очевидно.


          1. inkelyad
            04.01.2026 11:03

            Но вам понадобилось 2 версии по semver, чтобы описать текущую версию запущенной системы.

            Версия, видимая наружу - одна. На нее потребители и смотрят. А то что там внутри - это версии компонентов и набор зависимостей, интересующая только разработчиков сервиса. Которые в любом случае целой кучей версий зависимостей оперируют.


  1. YChebotaev
    04.01.2026 11:03

    Вы не совсем правильно понимаете SemVer. SemVer нужен для публичных библиотек, чтобы их пользователи могли понять, когда обновляться относительно безопасно. Если вы делаете конечный продукт, который не входит в состав более крупного, он может следовать любой схеме версий


    1. inkelyad
      04.01.2026 11:03

      Если вы делаете конечный продукт, который не входит в состав более крупного, он может следовать любой схеме версий

      Ну почему... Это как трактовать.
      В конечном продукте версии 14.17.36 пользователи смогут работать так же успешно (и искать инструкции в своих Wiki) как с продуктом версии 14.16.1 (увеличилась минорная версия, т.е. где-то какие-то дополнительные менюшки появились, которые ничему не мешают, пока в них не лезешь)
      А вот продукту версии 15.1.1 уже придется обучаться заново, потому что весь API/UI пользователя поломали и переделали.


      1. powerman
        04.01.2026 11:03

        Не совсем так. SemVer оперирует понятием "совместимое изменение". И это ключевой фактор: чтобы определить является ли изменение совместимым нужно иметь возможность как-то эту совместимость оценивать. Если продукт это библиотека, то способ оценить совместимость очевиден: по API. Но если продукт это CLI/GUI приложение, то понятие совместимость становится крайне туманным. Например для CLI можно считать совместимостью формат аргументов/флагов, но является ли совместимым изменением незначительное отличие в формате выводимых данных - уже не так очевидно.

        Ещё один ключевой момент SemVer, который упустил автор статьи - задача SemVer коммуницировать ключевую информацию чтобы пользователь мог определиться нужно ли ему (полезность) и когда лучше (сложность) обновляться. Это полезно только в ситуации, когда время обновления выбирается пользователем. Что, очевидно, не так для микросервисов и для веб-фронта - и то и другое обновляется централизованно компанией, а не вразнобой пользователями.

        При отсутствии любого из этих двух факторов пропадает смысл использовать SemVer.


        1. tuzhms Автор
          04.01.2026 11:03

          Ещё один ключевой момент SemVer, который упустил автор статьи - задача SemVer коммуницировать ключевую информацию чтобы пользователь мог определиться нужно ли ему (полезность) и когда лучше (сложность) обновляться

          Эту проблему я упоминал, но не со стороны пользователя конечного приложения. Для пользователя действительно может быть не так важен переход на следующую мажорную версию.

          Я рассматривал пример, когда вы потребитель библиотеки, которая выпускает новую мажорную версию. В таком случае у вас не такой свободный выбор. После выпуска новой мажорной версии библиотеки предыдущая ветвь развиваться не будет. В такой ситуации разработчик вынужден переходить, даже если нет в этом необходимости, либо копить техдолг.


          1. powerman
            04.01.2026 11:03

            В таком случае у вас не такой свободный выбор.

            Вполне свободный: обновиться сейчас или позднее, отказаться от обновления, перейти на другую библиотеку…

            После выпуска новой мажорной версии библиотеки предыдущая ветвь развиваться не будет.

            Зависит от библиотеки, некоторые продолжают поддерживать багфиксами предыдущий мажорный релиз ещё какое-то время. Особенно если по объективным причинам многие пользователи не хотят обновляться на последний мажор.

            В такой ситуации разработчик вынужден переходить, даже если нет в этом необходимости, либо копить техдолг.

            Отказ от перехода на новый мажор - это не всегда техдолг.

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


  1. nikolay-ermolenko
    04.01.2026 11:03

    На мой взгляд, проблема при использовании semVer заключается в том, что для многих продуктов смена мажора это юридически-маркетингово-сертификационная штука и она не связана с какими-то несовместимыми изменениями. Т.е. мажор скорее используется, как эпоха. За ней должен идти мажор (который про совместимость) далее с минор (про фичи) и патч (про исправления).

    Например 2026.2.7.4

    В 2026-м году выпущено два несовместимых обновления, для последнего выпущено семь фича-релизов, для последнего выпущено четыре фикса.

    Проблему нежелания заказчика обновляться, а получать обновления патчами (как следствие необходимость поддерживать N старых релизов) ни один способ версионирования не решит, т.к. это из другой плоскости.


    1. tuzhms Автор
      04.01.2026 11:03

      для многих продуктов смена мажора это юридически-маркетингово-сертификационная штука и она не связана с какими-то несовместимыми изменениями

      Эта проблема упоминается в статье, на которую я сослался https://sedimental.org/designing_a_version.html#case-study-chrome-vs-firefox . Поэтому не стал заострять на этом внимание


  1. antirek
    04.01.2026 11:03

    в статье не увидел примеров версионирования по EvoVer


  1. AgaFonOff
    04.01.2026 11:03

    Совместимость или не совместимость может определяться набором тестов. Если вы понаписали кода и теперь тесты не проходят - делаете новые версии тестов и обновляете мажор. Вроде бы так, не?


    1. tuzhms Автор
      04.01.2026 11:03

      Несовместимость можно обнаружить тестами, но всё же при поломке тестов нужно не мажорную версию поднимать, а смотреть, всё ли мы правильно реализовали.

      Роль тестов - проверить реализацию, а не определять версию сборки.


  1. sophist
    04.01.2026 11:03

    Можете воспринимать EvoVer, как CalVer 2.0.0

    Как CalVer 2026.01, извините :)