ESPN DeportesBarcelona: Christensen, 3 o 4 semanas fueraESPNMLS: Union skyrocket as unbeaten streak reaches 11 gamesDaily MaverickGROUNDUP: Police and NPA liable for Gqeberha murder by man out on bailInquirer EntertainmentCeleste Legaspi recalls Lea Salonga’s early theater days before Walk of Fame starInquirerPNP: 2 vandalism suspects in martial law rally pacified, not arrestedCBS NewsBill Gates says Trump's AI views point to societal blind spotGlobal NewsEd Sheeran breaks down during 1st concert amid Macklemore tour fallout01netLa Switch OLED revient d’entre les morts : à ce prix, elle va se vendre par camions 🚛Screen RantPlayStation Officially Unveils Brand-New Hardware And Redesigned ControllerTagesschauMarktbericht: Entspannung am Ölmarkt stützt den DAXVarietyAmerican Film Market Partners With AMC A-List and B-List Movie Club to Offer Screenings to Frequent MoviegoersBusiness AMAmerikaans scheepsbouwbedrijf begint met voorbereidende werk voor de bouw van nieuw vliegdekschip USS William J. Clinton
The Daily Newsstand · Free, Always
Monday, September 21, 2026

[Перевод] Трагедия версионирования ПО

Translate

As we say, two of the most complicated problems in software are naming things and versioning software

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

Всем привет! Меня зовут Михаил Поливаха, я являюсь техническим лидером Open Source проекта Axelix.

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

Статья будет полезна техническим лидерам или тем, кто метит на позицию Staff Engineer, так как стратегия версионирования ПО, её связь с релизным циклом и природой обновлений вашего ПО - это очень, очень важное решение по продукту. Тут ошибка будет стоить дорого. Использовать ли вам Release Train, работать ли вам в рамках Lockstep, нужно ли вам Compatibility Window, нужна ли матрица совместимости и т.д.

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

В начале было слово

Наше программное обеспечение развивается. Мы добавляем новые возможности, исправляем баги, узнаём что-то новое о предметной области и о том, как пользователи на самом деле применяют продукт. Даже если продукт функционально закончен, ему всё равно однажды понадобится обновление из-за какой-нибудь zero-day CVE.

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

Для этого и существуют версии.

Но вот тут начинается интересное. И тут на самом деле море вопросов:

  1. Что вообще представляет собой версия? Это число или же это строка, набор символов?

  2. Когда вообще надо выпускать новую версию?

  3. А если софт состоит из разных независимых частей, то как версии этих частей соотносятся друг с другом?

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

Версия desktop-приложения, версия Java-библиотеки и версия какой-нибудь APM-платформы типа Dynatrace из двадцати компонентов решают три разные задачи. Пытаться механически применить к ним одну схему можно. Хорошая ли это идея? Давайте разбираться.

Относительно простой случай. Desktop-приложение

Представим обычное desktop-приложение для конечного пользователя. Не IDE с огромной plugin ecosystem, не операционную систему и не Excel, вокруг которого компания построила половину своих процессов. Просто приложение.

У такого продукта, как правило, есть две важные особенности:

1. Основной способ потребления: пользователь взаимодействует с продуктом, а не компилирует свой код против его API

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

Я вчера видел вот эту кнопку на вот этой страничке, а сегодня - её тут нет, она на другой страничке

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

2. Между приложением и пользователем нет большого dependency graph-а из других библиотек

Ещё раз, desktop-приложения бывают разные. Есть те, где интеграций с внешними системами много, например Excel, но “много” - понятие растяжимое, и тут каждый продукт уникален.

CalVer. Распространённый случай

В общем случае вполне хорошо работает Calendar Versioning, или CalVer. Это формат версии, при котором дата выпуска релиза тем или иным образом зашивается в версию. Например, CalVer-версия может быть 2026.1 и означать первый релиз 2026 года. Похожую схему используют IDE семейства JetBrains.

CalVer

CalVer

Известным примером софта, который идёт по CalVer, является, в том числе, Ubuntu от Canonical. Ubuntu выпускается два раза в год, в апреле и октябре, поэтому версия у неё имеет такой формат: 22.04 - апрельский релиз, а 22.10 - октябрьский релиз.

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

Обратите внимание, CalVer не говорит, что продукт стабильный или обратно совместимый с чем-то. Он сообщает время релиза. Для consumer software этого часто достаточно и, как правило, это хороший выбор, потому что оценивать совместимость отдельных библиотек пользователю просто не приходится.

Marketing Version

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

Например, Microsoft вполне успешно выпускала Windows 7, 8, 10 и 11. Этот подход часто называют Marketing Versioning, потому что его основная цель - маркетинг, возможность громко заявить:

Вчера у вас была Windows 10, а спустя несколько лет разработки выходит Windows 11!

За этим релзом будет следовать целая маркетинговая компания. Это не очередной релиз Ubuntu.

![Windows 11 - Одна версия](

Конечным пользователям воспринимать число 10 и понимать, что оно меньше 11, проще, чем сравнивать 22.04 и 24.10 от Ubuntu. Конечно, Marketing Versioning - это очень нишевый подход для фундаментальных, крупных софтверных продуктов, но он оправдан, так как с точки зрения маркетинга людям проще воспринимать и сравнивать одно число.

Опять же, это не значит, что все desktop-приложения обязаны использовать CalVer или Marketing Versioning. Моя мысль проще: если основная ценность номера для пользователя состоит в том, чтобы отличить свежий релиз от старого, а релизы выходят относительно регулярно, то календарная схема вполне честно решает задачу. Если вы планируете выпускать релизы очень редко и каждый релиз является отдельным событием, то можно рассмотреть Marketing Versioning.

Когда от Вас начинают зависеть

Давайте пойдём дальше и немного поменяем ситуацию. Мы разрабатываем не приложение, а библиотеку.

Тут ситуация принципиально другая. Пользователь не просто запускает наш продукт. Он компилирует против него свой код, использует наши классы, методы, annotations и конфигурационные свойства. Наша библиотека становится частью его программы.

И теперь пользователя интересует уже не только свежесть релиза. Перед обновлением он хочет понять:

  • сломается ли компиляция;

  • изменится ли поведение в runtime;

  • нужно ли переписывать конфигурацию;

  • можно ли автоматически принять patch через Dependabot;

  • сколько времени заложить на migration.

Semantic Versioning. Краеугольный камень

Вот тут на сцену выходит многим известный Semantic Versioning, он же SemVer. Его формату следует большое количество проектов:

  • Spring Boot

  • Quarkus

  • Jackson и т. д.

В том числе ему следует Axelix, но об этом позже. В классическом виде версия имеет формат MAJOR.MINOR.PATCH:

  • MAJOR увеличивается при обратно несовместимом изменении публичного API;

  • MINOR увеличивается при добавлении обратно совместимой функциональности;

  • PATCH увеличивается при обратно совместимом исправлении.

У версии также может быть qualifier, но это опционально. Вот несколько широко известных примеров:

  • Alpha/Beta-версии, например 8.0.0.Alpha1. Такие qualifier-ы добавляет Hibernate для альфа- и бета-тестирования. Сейчас мы в это уходить не будем.

  • Milestone-версии, например 4.0.0-M1. В цели выпуска milestone-релизов мы уходить тоже не будем, но я хочу сказать, что крупные проекты, такие как Spring Boot и Axelix, иногда используют их, чтобы получить ранний фидбек.

SemVer формат

SemVer формат

Например, переход с 2.4.1 на 2.4.2 должен быть скучным. Обновили зависимость, прогнали тесты, поехали дальше. А переход с 2.4.2 на 3.0.0 заранее предупреждает: ребята, тут может потребоваться работа.

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

SemVer требует публичного API

Есть нюанс, про который иногда забывают.

SemVer, если вообще почитать спеку, ориентируется на так называемый “публичный API”. Это прямо записано в спецификации. Это логично, ведь иначе невозможно ответить на вопрос, является ли изменение обратно несовместимым. И тут у меня к вам вопрос.

Что считать публичным API Spring Boot starter-а?

Только Java-классы? А configuration properties? А имена bean-ов - это контракт? А формат actuator endpoint-а? А порядок обнаружения auto-configuration-ов? А стратегии проксирования автоконфигурации? Тут, как говорится:

Гладко было на бумаге, да забыли про овраги

На бумаге правило “сломал API, увеличь MAJOR” выглядит просто. Но в реальном framework-е поверхность совместимости огромна.

Иногда даже исправление критического бага в patch-релизе само по себе меняет поведение, на которое кто-то уже успел положиться. Иногда CVE невозможно закрыть без ужесточения старых правил. Иногда major-обновление транзитивной зависимости ломает пользователя, хотя Ваш собственный Java API вообще не изменился.

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

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

Spring Boot и индекс боли

Очень хороший пример тут Spring Boot.

Spring Boot использует знакомый формат MAJOR.MINOR.PATCH, но команда проекта прямо пишет, что строгий Semantic Versioning для Boot практически невозможен. Иначе major пришлось бы увеличивать слишком часто.

Вместо этого номер версии используется как условный индекс ожидаемой боли (pain index) при обновлении:

  • patch должен быть максимально близок к drop-in replacement;

  • minor приносит новые возможности и обычно требует небольших усилий при обновлении;

  • major оставляют для крупных breaking changes, где migration ожидаема.

Это похоже на SemVer, но не является строгим SemVer. Minor-релиз Spring Boot может содержать несовместимые изменения. Кроме того, Boot управляет версиями большого количества сторонних библиотек, и обновление самого Boot обновляет весь этот набор.

И мой вам совет: схема выше жизнеспособна, и если вы хотели бы следовать SemVer, рассмотрите её всерьёз. Она в целом выполняет функцию, которую был призван выполнить SemVer: дать возможность относительно быстро, не вдаваясь в release notes, без пересборок и т.д. понять масштаб изменений и примерно то, насколько запарным будет переезд.

Просто SemVer строго приписывает: Major == обратно несовместимые изменения. На практике формула такая:

  • Patch: обновляйся, всё будет норм.

  • Minor: придётся, может быть, чуть-чуть попыхтеть, но, скорее всего, ничего страшного.

  • Major: точно придётся потратить время на миграцию.

Версионирование существует для пользователей, а не пользователи для версионирования. Если строгая схема вынуждает зрелый framework выпускать Spring Boot 47, то, возможно, схема плохо описывает реальность конкретного проекта.

Важно другое: обещание должно быть сформулировано явно. Пользователь должен понимать, что 3.4 -> 3.5 означает:

Скорее всего, обновление будет относительно простым”.

Это не означает математическую гарантию отсутствия несовместимых изменений.

В общем, друзья, не путайте синтаксис SemVer с его семантикой. 1.2.3 можно написать над чем угодно. Ценность появляется только там, где проект объяснил правила и действительно старается им следовать.

А если продукт состоит из нескольких компонентов?

Теперь вносим в уравнение ещё больше интересных деталей. Современная software platform редко состоит из одного артефакта.

Возьмите любое APM-решение, например Dynatrace или Datadog. У них есть сервер, есть отдельные агенты для сбора информации - это разные компоненты. Возьмите Kubernetes: там есть kubelet на каждой ноде, есть Control Plane и т. д. Это всё разные части одного продукта.

У нас в Axelix, например, есть:

  • build plugins для Maven и Gradle;

  • Spring Boot starter;

  • Axelix Master как отдельный исполняемый JAR;

  • Docker image для Master;

  • Helm chart.

И что важно: как правило, все эти “компоненты” распространяются отдельно, а отвечать за них могут разные команды. Например, в нашем случае starter и плагины приезжают в сервис из Maven Central. Это просто Maven-артефакты, за обслуживание которых отвечает отдельная продуктовая команда.

Разные Версии

Разные Версии

А вот Axelix Master разворачивает platform/ops team. Docker image и Helm chart вообще живут в своих distribution-каналах.

Такая архитектура говорит, что технически каждый компонент может иметь независимую версию и независимую частоту изменений. Например, в starter-е ничего не поменялось, а в Master мы исправили UI. Или наоборот, потребовалось выпустить patch build plugin-а, не затрагивая сервер.

И тут перед нами появляется выбор из двух стратегий.

Независимое версионирование

Каждый компонент получает собственную версию:

  • Master 3.7.1;

  • starter 2.4.0;

  • Maven plugin 5.1.3;

  • Helm chart 1.9.2.

С точки зрения нас как maintainer-ов это чистая и логичная модель. Поменялся один компонент, увеличили версию только ему согласно вот этому вот “Pain Index”. Не нужно публиковать артефакты, код которых не изменился. Нам, мейнтейнерам, так проще.

Но теперь сложность переехала к пользователю.

Совместим ли starter 2.4 с Master 3.7? Какой plugin нужен к starter-у 2.4? Можно ли использовать chart 1.9 для image 3.7.1? В результате рядом с продуктом появляется “compatibility matrix”, которую нужно документировать, тестировать и постоянно поддерживать.

Очень хорошим примером этого является эпопея между Spring Boot и Spring Cloud. Поскольку технически это разные проекты со своими стратегиями версионирования, между ними существует Compatibility Matrix, которая позволяет понять, можно ли использовать вот этот Spring Cloud с вот этим Spring Boot.

И тут вопрос: а плохо ли иметь Compatibility Matrix вообще? Вы же сами пользователи программных продуктов. Ответьте себе: насколько классно каждый раз выяснять, совместима ли вот эта версия компонента A с вот этой версией компонента B? Далеко не самое приятное времяпрепровождение.

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

Lockstep versioning

Вторая стратегия называется lockstep versioning. Все компоненты продукта выпускаются под одной версией.

Если мы выпускаем Axelix 1.4.2, то Master, starter, plugins, image и chart получают версию 1.4.2. Такой стратегии придерживаются модули внутри проектов:

  • Spring Boot (actuator, autoconfigure, plugin-ы и т. д.)

  • коровые модули React (react, react-dom и т. д.)

  • коровые модули Quarkus

Здесь есть очень важное различие. SemVer и CalVer отвечают на вопрос, как устроен номер версии. Lockstep отвечает на другой вопрос: какие компоненты разделяют этот номер.

Поэтому вполне можно иметь SemVer вместе с lockstep. Или CalVer вместе с lockstep. Это не конкурирующие понятия. Плюсы lockstep понятны:

1. Пользователь оперирует одной версией продукта

Это очень удобно. Нужно знать лишь одну версию. Когда вы спрашиваете своего коллегу:

“У вас микросервис на каком Spring Boot?”

Нужно же понимать, что Spring Boot - это зонтик модулей, но все отвечают 2.7 или 3.5, то есть люди оперируют одной версией. Это тупо удобно. Точно так же на вопрос “Какая у Вас версия Axelix?” можно ответить 1.1.

2. Совместимая комбинация компонентов очевидна

Человек понимает, что для Axelix Master версии 1.1.0 ему можно использовать Spring Boot Starter версии 1.1.0 (на деле всё чуть-чуть сложнее, об этом ниже).

3. Документация, examples и release notes привязаны к одному номеру

Для platform product-ов это очень сильные преимущества. Но бесплатного обеда, конечно, нет, у lockstep-а есть существенные проблемы.

Цена lockstep

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

1. Обратно несовместимое изменение одного компонента формально влияет на версию всего продукта

Иными словами, как только вы понимаете, что надо апать major для компонента A, сразу же приходит понимание: апать major надо для всего продукта целиком.

Допустим, мы сломали публичный API только Maven plugin-а. Если проект следует строгому SemVer и все компоненты двигаются lockstep, major придётся увеличить у Master, starter-а, Docker image и Helm chart, хотя они сами по себе не сломались.

Эту проблему частично можно митигировать, если следовать не SemVer в его строгом понимании, а именно пониманию SemVer командой Spring Boot, о чём я писал выше.

2. Patch одного компонента приводит к patch-релизу всего набора

Предположим, CVE находится только в web dependency Master-а. Starter этой dependency не имеет и вообще не менялся. Но в lockstep-модели новый релиз продукта всё равно будет, например, 1.4.3 для всех артефактов.

Для пользователей это удобно. Для release engineering это дополнительная работа:

  • собрать все компоненты;

  • прогнать общий pipeline;

  • опубликовать несколько типов артефактов;

  • синхронно обновить документацию;

  • следить, чтобы никакой registry не остался на старой версии.

От себя скажу, что и это можно сильно митигировать.

Lockstep и монорепозитории

Lockstep хорошо подходит для монорепозиториев, использующих Trunk Based Development или GitHub Flow. Именно поэтому lockstep хорошо лёг на Spring Boot, это монорепа с GitHub Flow, и на Axelix, это монорепа с немного модифицированным TBD, о котором было в отдельной статье. Если проект организован как монорепа с TBD, то lockstep часто не просто упрощается, а становится прямо естественной стратегией.

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

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

Но тут я напомню, что Open Source разрабатывается маленькими командами. Core-команды Open Source решений маленькие!

  • Axelix: core-команда из четырёх человек и десятки внешних contributor-ов

  • Spring Boot: core-команда, на данный момент, условно из 5-7 человек и тоже десятки внешних contributor-ов

И, как правило, поскольку всю вот эту платформу разрабатывает одна небольшая команда, для неё lockstep не создаёт большого overhead-а в коммуникации: координации изменений, релизов и т. д.

Поэтому, если ваша команда маленькая, то коммуникационного оверхеда lockstep-а вы, скорее всего, не заметите. Если Вы разрабатываете platform product, то удобство пользователя часто важнее красоты внутренних release-процессов, особенно если их можно как-то упростить за счёт структуры кодовой базы.

Тем не менее, и это ещё не всё…

Lockstep-релиз не означает lockstep-обновление

Вот тут мы подходим к самому интересному.

Можно выпустить все компоненты под версией 1.4, но невозможно заставить организацию обновить их одновременно. Например, Axelix Master контролирует platform team. Она может обновить один deployment за вечер, для этого есть отдельная процедура в документации.

А starter и build plugin находятся в десятках или сотнях сервисов. У каждого сервиса своя команда, backlog, release window и свои приключения.

Если для перехода Master с 1.3 на 1.4 нужно в ту же минуту обновить сто сервисов, то система формально использует lockstep versioning, но практически не обновляется вообще. Такую систему невозможно обновлять на практике - операционно невозможно десятки команд заставить одновременно обновить версии тех или иных компонентов.

Иными словами, Вам, как Technical Leader-у или старшему инженеру, важно не просто думать над тем, как вы будете выпускать версии, какими они будут и как люди будут их воспринимать. Вам важно заранее думать, как люди будут обновлять ваш продукт у себя.

Делюсь с Вами опытом того, как мы сделали в Axelix и как к этому подходят крупные проекты на примере Kubernetes.

Знакомьтесь, Compatibility/Skew Window

Для решения такой проблемы создают так называемое Compatibility Window, то есть “окно совместимости”. Не путайте его с Compatibility Matrix.

Даже самые нетривиальные системы, которые состоят из набора компонентов, имеют чёткий порядок обновлений: сначала обновляется компонент A, потом B.

Часто компонент, который обновляется первым, можно обновить относительно атомарно. Например, первым всегда обновляется Axelix Master. Обновить его довольно просто: нужно попросить платформенную команду вечером провести апгрейд версии.

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

Master поддерживает стартеры последних N релизов (не считая patch), включая свой собственный.

Compatibility матрица в Axelix Master

Compatibility матрица в Axelix Master

Для нас сейчас N == 4. На конкретном примере это выглядит следующим образом:

Версия starter-а

Поддерживается Master 1.4

1.4.x

Да

1.3.x

Да

1.2.x

Да

1.1.x

Да

1.0.x

Нет

Это compatibility window - окно, направленное назад: новый Master понимает старые starter-ы. Starter новее Master запускать нельзя.

Существующий пример. K8S Skew Policy

Похожий принцип можно увидеть в официальной Version Skew Policy Kubernetes:

  • kubelet не может быть новее kube-apiserver;

  • kubelet может быть до трёх minor-версий старше kube-apiserver;

  • сначала обновляется kube-apiserver, и только потом можно постепенно обновлять kubelet на отдельных нодах.

Например, с kube-apiserver версии 1.37 поддерживаются kubelet версий 1.37, 1.36, 1.35 и 1.34. Логика та же: сначала атомарно обновляем централизованную часть, а затем постепенно подтягиваем распределённые компоненты.

На деле “Skew Policy” - это вот то самое скользящее окно. Это то же самое по сути, просто другими словами.

Надеюсь, общая логика понятна. В таком случае процедура обновления получается следующая:

  1. Platform team смотрит, какой самый старый starter остался во fleet-e.

  2. Сначала обновляет Master.

  3. Команды сервисов независимо обновляют starter и build plugin.

  4. Постепенно весь fleet сходится к новой версии.

Compatibility Window. Trade-Off

Само собой, это trade-off.

Чем больше N, тем больше у пользователей есть времени на обновление, но тем сложнее будет вам - нужно будет поддерживать большой хвост старого API и не забывайте, что всё это ещё надо тестировать в матричных билдах.

Чем меньше N, тем меньше будет у пользователей времени на обновление и тем им некомфортнее, но тем проще будет вам - вы сможете агрессивно дропать старый API.

Важный совет: если вы в начале пути построения продукта, то приоритезируйте возможность агрессивных обновлений, пока продукт ещё не получил массового adoption-а и огромного числа пользователей. Со временем двигайте N в сторону увеличения.

Какую схему выбрать

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

Давайте подведём практический итог.

Если у Вас consumer application

Задайте себе два вопроса:

  1. Как часто вы релизитесь? Для вас релиз - это эпохальное событие или более-менее отлаженный процесс?

  2. Как много у вас точек для внешней интеграции?

В общем случае, чаще всего, для вас правильным выбором будет либо CalVer, либо Marketing Versioning.

Если у Вас библиотека

SemVer является хорошей отправной точкой. Но сначала явно определите публичный API. Без этого обещание обратной совместимости будет очень туманным.

На практике со временем вы, скорее всего, придёте к пониманию, что поверхность публичного API слишком большая. В таком случае обратите внимание на модель Spring Boot и на “Pain Index” и явно это опишите. Самое неприятное - когда пользователь думает, что получает гарантию, которой на самом деле нет.

Если у Вас несколько тесно связанных компонентов

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

Если выбрали lockstep, то сразу инвестируйте в release automation, потому что даже маленький patch одного компонента станет релизом всего набора. В идеале вам будет нужна монорепа.

Если у Вас экосистема независимых проектов

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

Размер окна совместимости - это trade-off между вашей скоростью разработки и удобством пользователя. На разных стадиях развития продукта этот trade-off должен быть разным.

Заключительное слово

Версия не является просто счётчиком релизов. Это часть публичного контракта продукта.

Хорошая версия должна помочь пользователю ответить хотя бы на один важный вопрос:

  • насколько свежий этот релиз;

  • насколько болезненным будет обновление;

  • совместимы ли компоненты;

  • какой набор зависимостей протестирован вместе;

  • сколько времени у команды осталось на migration.

CalVer отвечает на вопрос времени. SemVer пытается описать совместимость API. “Индекс боли” оценивает сложность обновления. Lockstep уменьшает количество версий продукта.

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

Потому что номер 2.4.1 сам по себе ничего не гарантирует. Гарантию даёт только дисциплина команды, которая объяснила, что означают эти числа, и годами соблюдает собственные правила.

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.