В публичное api внесено обратно несовместимое изменение какая версия
Перейти к содержимому

В публичное api внесено обратно несовместимое изменение какая версия

  • автор:

Политика обратной совместимости при разработке фреймворка на примере Magento 2. Часть 1

image

*Поломанный кран в офисе Magento и быстрое решение воплощенное в жизнь одним из инженеров — типичный Backward Compatible фикс.

Почему обратная совместимость важна

Разрабатывая фреймворк, после выпуска нескольких его версий, вы сталкиваетесь с тем, что пользователь (или в случае Magento — заказчик), который его купил/скачал/установил, а также программист, который разрабатывает собственные расширения и модули на основе фреймворка хотят чтобы апгрейд между версиями был максимально легким.

Для заказчика/пользователя процесс должен быть экономически эффективным. Затраты, потраченные на переезд на новую версию продукта, в которые входят: сам апгрейд, автоматизированное тестирование после апгрейда, регрессионное тестирование, исправление багов, которые привнесла новая версия платформы, обновление кастомизаций и сторонних модулей; должны быть минимальными.

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

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

Политика обратной совместимости для кода

image

Семантическое версионирование (SemVer) дает ответ на дилемму, помогая не отказываться ни от одной из опций (частые backward compatible релизы).

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

Концепт публичного и приватного кода

Разработчики имеющие опыт работы с C++ или Java хорошо знакомы с этим концептом. Когда на выходе программа поставляется в виде .h (header) файла, содержащим описания внешних контрактов, семантику методов которых легко прочитать любым текстовым редактором и DLL файла, содержащий собранный и скомпилированный код, который тяжело прочитать и нельзя изменить.

PHP на уровне языка не предоставляет такую возможность. Поэтому «правильный» подход фреймворки, написанные на этом языке, искали давно. Например, Magento 1, как и многие другие фреймворки того времени (Symfony 1) использовали Inheritance Based API, когда фреймворк для кастомизации и расширения своих компонентов, предлагал отнаследоваться от любого из своих классов, и переопределить или расширить поведение в классе наследнике. Соответственно в Magento 1 приватные свойства и методы не использовались вообще, а Magento core разработчики обязаны были следить за двумя контрактами (Public — контракт который формируют публичные свойства, методы и константы сущностей; Protected — контракт наследования сущностей) и предотвращать добавление обратно несовместимых изменений в оба. Когда все сущности и все методы этих сущностей в код базе являются API, то добавление новых изменений и фикс багов в любом случае может кого-то поломать.

В Magento 2 в связи с этим решили следовать другому подходу — Inheritance Based API запретили для внутреннего использования, и не рекомендуют использование такого подхода для кастомизации классов фреймворка или модулей сторонними разработчиками. Запретили использование Protected модификатор доступа для атрибутов и методов классов. Основная идея в этих изменениях заключается в том, что имея только Public и Private — нам нужно следить только за одним контрактом — публичным. У нас нет контракта-наследования.

Следующим шагом было разделение кода на Публичный (аналог header файлов) — код и конструкции отмеченные аннотацией @api и Приватный (аналог скомпилированного DLL) — код, не отмеченный аннотацией, говорящей, что это API.
Закрытый код не предполагается к использованию сторонними разработчиками. Таким образом его изменения приведут только к увеличению PATCH версии компонента, где этот код изменялся.

Изменения в Публичном коде всегда увеличивают MINOR или MAJOR версию компонента.

Мы обещаем быть обратно совместимыми для классов отмеченных @api внутри MINOR и PATCH релизов компонентов. В случае когда нам нужно внести изменения в класс/метод отмеченный как @api , мы отмечаем его как @deprecated и он будет удален не раньше следующего MAJOR релиза компонента.

Примеры того, что попадает под определение Публичного кода в Magento 2

  • PHP интерфейсы отмеченные @api
  • PHP классы отмеченные @api
  • JavaScript интерфейсы отмеченные @api
  • JavaScript классы отмеченные @api
  • Virtual Type отмеченные @api
  • URL paths
  • Консольные команды и их аргументы
  • Less Variables & Mixins
  • Топики очереди сообщений AMQP и их типы данных
  • Декларация UI компонентов
  • Layout декларация модулей
  • События, которые тригерятся модулями
  • Схема конфигурации, добавляемая модулями
  • Структура системной конфигурации

API vs SPI (Точки Расширения)

PHP контракты в Magento могут быть использованы по-разному. Таких способов использования 3:

  • API использование: методы интерфейса вызываются в PHP коде
  • Service Provider Interface (SPI) использование: интерфейс может быть реализован, позволяя новой реализации расширять текущее поведение платформы
  • API и SPI одновременно

API и SPI не являются взаимоисключающими, поэтому мы не разделяем их отдельно в коде. SPI имеют такую же аннотацию как и API — @api .

Но кто же тогда определяет, что есть API, а что есть SPI? — Те, кто их использует, т.е. внешние разработчики

Правила указания зависимостей

  • get
  • save
  • delete

Так у нас появился отдельный сервис для поиска категорий.

image

В случае с удалением метода из интерфейса — обратная история, для SPI использование это не ломающие изменения, для API — это проблема.

API

Если модуль использует (вызывает) контракты, задекларированные другим модулем Magento, он должен зависеть на MAJOR версию этого модуля. И система предоставляет обратную совместимость в рамках всего мажорного релиза.

< . "require": < "magento/module-customer": "~100.0", // (>=100.0 <101.0.0) >, . > 

SPI (Точки расширения)

Если модуль предлагает свою реализацию для контрактов задекларированных другим Magento модулем он должен зависеть на MAJOR+MINOR версию этого модуля. И система предоставляет обратную совместимость в рамках минорного релиза.

< . "require": < "magento/module-customer": "~100.0.0", // (>=100.0.0 <100.1.0) >, . > 
Зависимость на приватный код

Если же модуль зависит на сущность не отмеченную как @api , тогда модуль должен зависеть на MAJOR+MINOR+PATCH версию. И уже апгрейд на следующий патч релиз может обернуться проблемами для данного модуля.

Ближайший мажорный коммерческий релиз 2.2 — Чего ждать?

В текущих версиях Magento 2.0.x и 2.1.x нельзя обойтись без зависимостей на приватный код.
Потому что у нас недостаточное покрытие @api для этого. Некоторые модули не имеют сервис контрактов вообще (например, wishlist). Поэтому у сторонних разработчиков нет другого выхода кроме как зависимости на непомеченные api аннотацией классы.

Разработка API — самый сложный процесс в проектировании и разработке программного обеспечения. И так как мы пока не знаем когда именно закончится работа по добавлению сервис контрактов во все модули Magento мы решили в релизе 2.2 отметить @api все сущности, которые необходимы для написания/кастомизации модулей на мадженто сторонними программистами.
Т.е. в 2.2 мы отметим все «честные контракты», т.е. если функциональность, которую предоставляет модуль реализуется хелпером или ресурс моделью. И получить данную функциональность путем вызова API модуля нельзя, то мы пометим данный хелпер как @api .
Для примера, у нас есть ProductInterface, который определяет набор операций над сущностью продукта. И есть продуктовая модель, которая имплементирует этот интерфейс. Так как сейчас продуктовая модель наследуется от абстрактной модели и соответственно имеет контракт абстрактной модели, то мы не можем сказать сейчас, что сторонний разработчик можем полагаться только на ProductInterface, и если кто-то предоставит свою реализацию этого интерфейса, то сможет легко подменить внутреннюю реализацию мадженто (если это не будет наследник продуктовой модели). Т.е. достаточно много кода в Magento использует метод get/setData который пришел из абстрактной модели. Поэтому в 2.2 мы пометим продуктовую модель как @api , не смотря на то, что у нас уже есть API в виде ProductInterface.

До релиза 2.2. мы считаем весь код — публичным, т.е. на все классы распространяется политика обратной совместимости. Не только на классы, помеченные @api . Разделение концепта на публичный и приватный начнется с 2.2 релиза.

Теперь как мы определяем, что используют, а что нет.

Мы анализируем какие зависимости между модулями Magento использует внутри себя. А также мы анализируем какие зависимости используются экстеншенами на Marketplace (non api dependency). И если зависимости валидны, т.е. такой результат нельзя получить используя текущие API модуля — мы помечаем сущность как @api

*Данная статья является частью 1; Часть 2, которая выйдет вскоре опишет ограничения в коде, которые привносятся политикой обратной совместимости и что мы делаем, чтобы не останавливать рефакторинг следуюя BC политике и не аккумулировать технический долг из-за этих ограничений

  • magento 2
  • magento
  • backward compatibility
  • обратная совместимость
  • рефакторинг
  • php
  • semantic versioning
  • версионирование
  • PHP
  • Анализ и проектирование систем
  • Проектирование и рефакторинг
  • Разработка под e-commerce
  • Magento

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

Учитывая номер версии МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ, следует увеличивать:

  1. МАЖОРНУЮ версию, когда сделаны обратно несовместимые изменения API.
  2. МИНОРНУЮ версию, когда вы добавляете новую функциональность, не нарушая обратной совместимости.
  3. ПАТЧ-версию, когда вы делаете обратно совместимые исправления.

Дополнительные обозначения для предрелизных и билд-метаданных возможны как дополнения к МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ формату.

Вступление

В мире управления процессом разработки есть понятие «ад зависимостей» (dependency hell). Чем больше растёт ваша система и чем больше библиотек вы интегрируете в ваш проект, тем больше вероятность оказаться в этой ситуации.

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

В качестве решения данной проблемы я предлагаю простой набор правил и требований, которые определяют, как назначаются и увеличиваются номера версий. Для того чтобы эта система работала, вам необходимо определить публичный API. Он может быть описан в документации или определяться самим кодом. Главное, чтобы этот API был ясным и точным. Однажды определив публичный API, вы сообщаете об изменениях в нём особым увеличением номера версий. Рассмотрим формат версий X.Y.Z (мажорная, минорная, патч). Баг-фиксы, не влияющие на API, увеличивают патч-версию, обратно совместимые добавления/изменения API увеличивают минорную версию и обратно несовместимые изменения API увеличивают мажорную версию.

Я называю эту систему «Семантическое Версионирование» (Semantic Versioning). По этой схеме номера версий и то, как они изменяются, передают смысл содержания исходного кода и что было модифицировано от одной версии к другой.

Спецификация Семантического Версионирования (SemVer)

Слова «ДОЛЖЕН» (MUST), «НЕ ДОЛЖЕН» (MUST NOT), «ОБЯЗАТЕЛЬНО» (REQUIRED), «СЛЕДУЕТ» (SHOULD), «НЕ СЛЕДУЕТ» (SHOULD NOT), «РЕКОМЕНДОВАННЫЙ» (RECOMMENDED), «МОЖЕТ» (MAY) и «НЕОБЯЗАТЕЛЬНЫЙ» (OPTIONAL) в этом документе должны быть интерпретированы в соответствии с RFC 2119.

  1. ПО, использующее Семантическое Версионирование, должно объявить публичный API. Этот API может быть объявлен самим кодом или существовать строго в документации. Как бы ни было это сделано, он должен быть точным и исчерпывающим.
  2. Обычный номер версии ДОЛЖЕН иметь формат X.Y.Z, где X, Y и Z — неотрицательные целые числа и НЕ ДОЛЖНЫ начинаться с нуля. X — мажорная версия, Y — минорная версия и Z — патч-версия. Каждый элемент ДОЛЖЕН увеличиваться численно. Например: 1.9.0 ->1.10.0 -> 1.11.0.
  3. После релиза новой версии пакета содержание этой версии НЕ ДОЛЖНО быть модифицировано. Любые изменения ДОЛЖНЫ быть выпущены как новая версия.
  4. Мажорная версия ноль (0.y.z) предназначена для начальной разработки. Всё может измениться в любой момент. Публичный API не должен рассматриваться как стабильный.
  5. Версия 1.0.0 определяет публичный API. После этого релиза номера версий увеличиваются в зависимости от того, как изменяется публичный API.
  6. Патч-версия Z (x.y.Z | x > 0) ДОЛЖНА быть увеличена только если содержит обратно совместимые баг-фиксы. Определение баг-фикс означает внутренние изменения, которые исправляют некорректное поведение.
  7. Минорная версия (x.Y.z | x > 0) ДОЛЖНА быть увеличена, если в публичном API представлеа новая обратно совместимая функциональность. Она ДОЛЖНА быть увеличена, если какая-либо функциональность публичного API помечена как устаревший (deprecated). Версия МОЖЕТ быть увеличена в случае реализации новой функциональности или существенного усовершенствования в приватном коде. Версия МОЖЕТ включать в себя изменения, характерные для патчей. Патч-версия ДОЛЖНА быть обнулена, когда увеличивается минорная версия.
  8. Мажорная версия X (X.y.z | X > 0) ДОЛЖНА быть увеличена, если в публичном API представлены какие-либо обратно несовместимые изменения. Она МОЖЕТ включать в себя изменения, характерные для уровня минорных версий и патчей. Когда увеличивается мажорная версия, минорная и патч-версия ДОЛЖНЫ быть обнулены.
  9. Предрелизная версия МОЖЕТ быть обозначена добавлением дефиса и серией разделённых точкой идентификаторов, следующих сразу за патч-версией. Идентификаторы ДОЛЖНЫ содержать только ASCII буквенно-цифровые символы и дефис [0-9A-Za-z-]. Идентификаторы НЕ ДОЛЖНЫ быть пустыми. Числовые идентификаторы НЕ ДОЛЖНЫ начинаться с нуля. Предрелизные версии имеют более низкий приоритет, чем соответствующая релизная версия. Предрелизная версия указывает на то, что эта версия не стабильна и может не удовлетворять требованиям совместимости, обозначенными соответствующей нормальной версией. Примеры: 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92.
  10. Сборочные метаданные МОГУТ быть обозначены добавлением знака плюс и ряда разделённых точкой идентификаторов, следующих сразу за патчем или предрелизной версией. Идентификаторы ДОЛЖНЫ содержать только ASCII буквенно-цифровые символы и дефис [0-9A-Za-z-]. Идентификаторы НЕ ДОЛЖНЫ быть пустыми. Сборочные метаданные СЛЕДУЕТ игнорировать, когда определяется старшинство версий. Поэтому два пакета с одинаковой версией, но разными сборочными метаданными, рассматриваются как одна и та же версия. Примеры: 1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85.
  11. Приоритет определяет, как версии соотносятся друг с другом, когда упорядочиваются. Приоритет версий ДОЛЖЕН рассчитываться путём разделения номеров версий на мажорную, минорную, патч и предрелизные идентификаторы. Именно в такой последовательности (сборочные метаданные не фигурируют в расчёте). Приоритет определяется по первому отличию при сравнении каждого из этих идентификаторов слева направо: Мажорная, минорная и патч-версия всегда сравниваются численно. Пример: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1. Когда мажорная, минорная и патч-версия равны, предрелизная версия имеет более низкий приоритет, чем нормальная версия. Пример: 1.0.0-alpha < 1.0.0. Приоритет двух предрелизных версий с одинаковыми мажорной, минорной и патч-версией ДОЛЖНЫ быть определены сравнением каждого разделённого точкой идентификатора слева направо до тех пор, пока различие не будет найдено следующим образом: идентификаторы, состоящие только из цифр, сравниваются численно; буквенные идентификаторы или дефисы сравниваются лексически в ASCII-порядке. Численные идентификаторы всегда имеют низший приоритет, чем символьные. Больший набор предрелизных символов имеет больший приоритет, чем меньший набор, если сравниваемые идентификаторы равны. Пример: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.

Зачем использовать семантическое версионирование?

Это не новая или революционная идея. Вероятно, вы уже используете что-то подобное. Проблема в том, что «подобное» — не достаточно хорошо. Без соответствия формальной спецификации, номера версий практически бесполезны для управления зависимостями. Ясно определив и сформулировав идею версионирования, становится легче сообщать о намерениях пользователям вашего ПО. Когда эти намерения ясны, гибки (но не слишком), спецификации зависимостей наконец могут быть созданы.

Простой пример демонстрирует, как Семантическое Версионирование может сделать «ад зависимостей» вещью из прошлого. Представим библиотеку, названную «Firetruck». Она требует Семантически Версионированный пакет под названием «Ladder». Когда Firetruck был создан, Ladder был 3.1.0 версии. Так как Firetruck использует функциональности версии 3.1.0, вы спокойно можете объявить зависимость от Ladder версии 3.1.0, но менее чем 4.0.0. Теперь, когда доступен Ladder 3.1.1 и 3.2.0 версии, вы можете интегрировать его в вашу систему и знать, что он будет совместим с текущей функциональностью.

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

Если это звучит соблазнительно, всё что вам нужно — это начать использовать Семантическое Версионирование, объявить, что вы его используете, и следовать правилам. Добавьте ссылку на этот сайт в вашем README, тогда пользователи будут знать правила и извлекать из этого пользу.

FAQ

Что я должен делать с ревизиями в 0.y.z на начальной стадии разработки?

Самое простое — начать разработку с 0.1.0 и затем увеличивать минорную версию для каждого последующего релиза.

Как я узнаю, когда пора делать релиз 1.0.0?

Если ваше ПО используется на продакшене, оно, вероятно, уже должно быть версии 1.0.0. Если у вас стабильный API, от которого зависят пользователи, версия должна быть 1.0.0. Если вы беспокоитесь за обратную совместимость, вероятно, версия вашего ПО уже 1.0.0.

Не препятствует ли это быстрой разработке и коротким итерациям?

Мажорная версия 0 как раз и означает быструю разработку. Если вы изменяете API каждый день, вы должны быть на версии 0.y.z или на отдельной ветке разработки работать над следующей главной версией.

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

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

Документирование всего API — слишком много работы!

Это ваша ответственность, как профессионального разработчика, правильно документировать ПО, предназначенное для широкого использования. Управление сложностью ПО очень важная часть поддержки высокой эффективности проекта. Это тяжело сделать, если никто не знает, как использовать ваше ПО или какой метод можно вызывать безопасно. В долгосрочной перспективе Семантическое Версионирование и настойчивость в качественном документировании публичного API поможет всем и всему работать слаженно.

Что мне делать, если я случайно зарелизил обратно несовместимые изменения как минорную версию?

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

Что я должен делать, если я обновляю свои собственные зависимости без изменения публичного API?

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

Что если я нечаянно изменил публичный API в несоответствии с изменением номера версии (т.е. код содержит обратно несовместимые изменения в патч-релизе)?

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

Что делать с устаревшей функциональностью?

Объявление функциональности устаревшей — это обычное дело в ходе разработки и часто необходимо для продвижения вперёд. Когда вы объявляете устаревшим часть публичного API, вы должны сделать две вещи: (1) обновить вашу документацию, чтобы дать пользователям узнать об этом изменении; (2) выпустить новый релиз с увеличением минорной версии. Прежде чем вы полностью удалите устаревшую функциональность в релизе с увеличением главной версии, должен быть как минимум один минорный релиз, содержащий объявление функциональности устаревшим, чтобы пользователи могли плавно перейти на новый API.

Есть ли в SemVer лимиты на длину строки версии?

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

Об авторе

Авторство спецификаций Семантического Версионирования принадлежит Тому Престон-Вернеру, основателю Gravatars и соучредителю GitHub.

Если вы хотите оставить отзыв, пожалуйста, создайте запрос на GitHub.

Чернышев Егор. Блог

При разработке программных решений встает задача управления изменениями ваших публичных API во времени. Например, у вас есть 2 сервиса А и Б. Сервис А делает запросы к сервису Б по REST API для получения необходимой информации. А зависит от Б, так как для его работы необходима информация из Б. Мы выпускаем новую версию сервиса Б, в которой изменилось API. Как сообщить клиентам сервиса Б, совместим ли API с предыдущей версией?

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

Для решения вышеописанных проблем можно воспользоваться Семантическим версионированием (Semantic Versioning) — набором правил и требований, которые определяют, как назначаются и увеличиваются номера версии.

Основная идея

В семантическом версионировании рассматривается формат версий X.Y.Z (мажорная, минорная, патч). Баг-фиксы, не влияющие на API, увеличивают патч-версию, обратно совместимые добавления/изменения API увеличивают минорную версию и обратно несовместимые изменения API увеличивают мажорную версию.

По ссылке выше сформулирована спецификация именования версий, которая на данный момент состоит из 11 пунктов. Подробнее со спецификацией можно ознакомится на странице.

Зачем это использовать

Это не новая или революционная идея. Вероятно, вы уже используете что-то подобное. Проблема в том, что «подобное» — не достаточно хорошо. Без соответствия формальной спецификации, номера версий практически бесполезны для управления зависимостями. Ясно определив и сформулировав идею версионирования, становится легче сообщать о намерениях пользователям вашего ПО. Когда эти намерения ясны, гибки (но не слишком), спецификации зависимостей наконец могут быть созданы.

Примеры

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

Например, внутри вашего решения есть REST API. Вы можете использовать semver для его версионирования. Сообщать версии API можно через http заголовок accept в каждом ответе вашего API:

Все клиенты могут проверять accept заголовок ответов вашего API и «понимать» могут ли они работать с API данной версии или нет.

StasyakOFF Blog

Номер версии представляется как MAJOR.MINOR.PATCH, где увеличивается:
1. MAJOR версия, когда добавляется несовместимое с предыдущей версией api,
2. MINOR версия, когда ты добавляешь обратно-совместимую функциональность,
3. PATCH версия, когда ты добавляешь обратно-совместимое исправление ошибки.
Допускается добавление дополнительных меток для предварительного релиза и метаданных о сборке, как расширение формата MAJOR.MINOR.PATCH

Введение

В мире управления программным обеспечением есть внушающая ужас вещь, под названием «ад зависимостей» (dependency hell). Чем быстрее растет ваша система и чем больше зависимостей от сторонних библиотек она имеет, тем выше вероятность, что, в один прекрасный момент, вы попадете в эту яму отчаяния.
В системах с большим количество зависимостей выпуск новых версий может быстро превратиться в настоящий кошмар.
Для решения этой проблемы предлагается простой набор правил, определяющий, как должны назначаться номера версий. Эти правила основаны (но не ограничены) на, давно используемых, принципах нумерации проприетарного и свободного программного обеспечения. Для использования семантического версионирования необходимо определить публичный API (в виде документации или набора интерфейсов). Это очень важно, т.к. именно по характеру изменений этого API определяется, какую новую версию получит ваше программное обеспечение.

Правила Семантического Версионирования (СВ)

1. Программное обеспечение использующее СВ должно иметь публичный API (в виде документации или набора интерфейсов).

2. Стандартный номер версии должен иметь вид X.Y.Z — где X,Y и Z целые неотрицательные числа и не содержать ведущих нулей (т.е. не допускаются версии вида, например 1.01.0 — правильно 1.1.0). При внесении изменений в ПО должен изменяться один или несколько компонентов версии X, Y или Z.

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

4. В начале разработки MAJOR версия равно 0, версия имеет вид 0.Y.Z. Публичное API нестабильно. Все изменения API считаются обратно-совместимым, т.е при любом изменении API увеличивается Y компонент.

5. Версия 1.0.0 присваивается публичному API в стабильном состоянии. Фактически в этот момент происходит первый релиз ПО.

6. Z компонент версии (патч-номер версии) должен быть увеличен, только если внесены обратно-совместимые исправления ошибок. Исправление ошибки — это внутреннее изменение, исправляющее некорректное поведение без изменения публичного API. Также он МОЖЕТ увеличиваться при незначительных функциональных улучшениях кода без изменения публичного API.

7. Y компонент версии (минорный номер версии) должен быть увеличен, если добавлена новая обратно-совместимая функциональность в публичный API. Также Y должен быть увеличен если какие-либо функции публичного API помечены как устаревшие. Компонент Y МОЖЕТ увеличиваться, если внесены значительные функциональные улучшения кода без изменения публичного API, при этом Z компонент версии (патч-номер) должен быть сброшен в 0. Если одновременно с улучшениями производились исправления ошибок, то Z компонент версии можно не менять.

8. X компонент версии должен увеличиваться при внесении обратно-несовместимых изменений в публичный API. Если одновременно с этим были обратно-совместимые изменения API и исправление ошибок не меняющих API, то компоненты Y и Z можно не менять. При изменении X компонента, компоненты Y и Z должны быть сброшены в 0.

9. Предрелизные версии могут быть обозначены путем добавления дефиса и идентификаторов, разделенных точкой, сразу после версии патча. Идентификаторы могут содержать только символы из набора [0-9A-Za-z]. Числовые идентификаторы не должны содержать ведущий ноль. Предрелизная версия указывает на то, что соответствующая версия публичного API еще не стабильна. Примеры нумерации: 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92

10. Метаданные сборки могут быть добавлены в конец релизной или предрелизной версии после знака «+». Метаданные сборки — это один или несколько непустых идентификаторов разделенных точкой. Идентификаторы могут содержать только символы из набора [0-9A-Za-z]. При определении старшинства версии метаданные сборки не учитываются, т.е. если 2 версии отличаются только метаданными сборки — эти версии имеют одинаковый приоритет. Пример: 1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85.

11. Старшинство версий должно рассчитываться путем сравнения X, Y, Z. При определении очередности слева направо сравниваются соответствующие компоненты версий. Например: 1.0.0

Зачем использовать Семантическое Версионирование?

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

Рассмотрим пример в котором есть приложение «Пожарная машина», которое зависит от библиотеки «Лестница». Библиотека «Лестница» использует семантическое версионирование. Приложение «Пожарная машина» использует библиотеку «Лестница» версии 3.1.0, т.к именно в этой версии были добавлены необходимые приложению функции. Это означает, что мы можем указать для приложения «Пожарная машина» диапазон совместимых версий библиотеки «Лестница» [3.1.0, 4.0.0). Это означает, что если в системе будет библиотека «Лестница» версии 3.1.1 или 3.2.5, то приложение «Пожарная машина» будет работать так же хорошо как и с версией 3.1.0
Однако с версией 4.0.0 и выше приложение может быть уже несовместимо.

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

Если все это выглядит привлекательно — все что вам нужно сделать — это объявить что вы используете Семантическое Версионирование и начать следовать правилам! Делайте ссылку на этот пост или на оригинальный текст в Вашем README, чтобы пользователи Вашего ПО понимали Вас лучше.

FAQ

→ Как менять версии формата 0.Y.Z на стадии разработки?
↑Самое простое — это начать с версии 0.1.0, а затем увеличивать Y при каждом новом выпуске, т.е. использовать 0.Y.0 (если вам не принципиально что вошло в новую версию, улучшения или исправления ошибок).

→ Как определить что пора выпускать версию 1.0.0?
↑Как только от Вашего ПО начинает зависеть стороннее ПО или пользователи — смело ставьте версию 1.0.0

→ Если даже при малейшем изменении обратной совместимости в публичном API надо увеличивать X компонент версии, не получится ли так что он быстро вырастет до больших значений, например, 42.0.0?
↑Это вопрос планирования и предвидения развития Вашего ПО. При изменении публичного API следует помнить, что это влечет изменение зависимого ПО, что может быть очень затратно с экономической точки зрения.

→ Как удалять из публичного API устаревшие части?
↑Устаревание кода является неотъемлемой частью разработки ПО и часто необходимо для успешного развития. Когда вы помечаете часть публичного ПО, как устаревшее, вам необходимо выпустить минимум один минорный релиз, чтобы переход на новый код был плавным, и подробно описать в документации произведенные изменения.

→ Есть ли ограничение на длину строки версии?
↑Данная спецификация не накладывает никаких ограничений. Просто используйте здравый смысл.

Вольный перевод с дополнениями для Оригинал

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *