Семантичне Версіонування 2
З огляду на номер версії МАЖОРНАЯ.МІНОРНАЯ.ПАТЧ, слід збільшувати:
- Мажорних версію, коли зроблені назад несумісні зміни API.
- Мінорні версію, коли ви додаєте новий функціонал, не порушуючи зворотної сумісності.
- ПАТЧ-версію, коли ви робите назад сумісні виправлення.
Додаткові позначення для предрелізний і білд-метаданих можливі як доповнення до МАЖОРНАЯ.МІНОРНАЯ.ПАТЧ формату.
вступ
У світі управління процесом розробки є поняття «пекло залежностей» (dependency hell). Чим більше зростає ваша система і чим більше бібліотек ви інтегруєте в ваш проект, тим більша ймовірність опинитися в цій ситуації.
В системі з множинними залежностями випуск нової версії може швидко перетворитися на кошмар. Якщо специфікації залежно занадто жорсткі, ви перебуваєте в небезпеці блокування випуску нової версії (неможливість оновити пакет без необхідності випуску нової версії кожної залежної бібліотеки). Якщо специфікація залежностей занадто вільна, вас неминуче наздожене версійність невідповідність (необгрунтоване припущення сумісності з майбутніми версіями).
Як вирішення проблеми я пропоную простий набір правил і вимог, які визначають, як призначаються і збільшуються номера версій. Для того щоб ця система працювала, вам необхідно визначити публічний API. Він може бути описаний в документації або визначатися самим кодом. Головне, щоб цей API був ясним і точним. Одного разу визначивши публічний API, ви повідомляєте про зміни в ньому особливим збільшенням номера версій. Розглянемо формат версій X.Y.Z (мажорна, мінорна, патч). Баг-фікси, які не впливають на API, збільшують патч-версію, назад сумісні додавання / зміни API збільшують мінорну версію і назад несумісні зміни API збільшують мажорну версію.
Я називаю цю систему «Семантичне Версіонування» (Semantic Versioning). За цією схемою номера версій і то, як вони змінюються, передають сенс змісту вихідного коду і що було модифіковано від однієї версії до іншої.
Специфікація Семантичного версіонірованія (SemVer)
ПО, що використовує Семантичне Версіонування, має оголосити публічний API. Цей API може бути оголошений самим кодом або існувати суворо в документації. Як би не було це зроблено, він повинен бути точним і вичерпним.
Звичайний номер версії ПОВИНЕН мати формат X.Y.Z, де X, Y і Z - невід'ємні цілі числа і НЕ ПОВИННІ починатися з нуля. X - мажорна версія, Y - мінорна версія і Z - патч-версія. Кожен елемент ПОВИНЕН збільшуватися чисельно. Наприклад: 1.9.0 -> 1.10.0 -> 1.11.0.
Мажорна версія нуль (0.y.z) призначена для початкової розробки. Все може змінитися в будь-який момент. Публічний API не повинен розглядатися як стабільний.
Версія 1.0.0 визначає публічний API. Після цього релізу номера версій збільшуються в залежності від того, як змінюється публічний API.
Патч-версія Z (x.y.Z | x> 0) ПОВИННА бути збільшена тільки якщо містить назад сумісні баг-фікси. Визначення баг-фікс означає внутрішні зміни, які виправляють некоректну поведінку.
Мінорна версія (x.Y.z | x> 0) ПОВИННА бути збільшена, якщо в публічному API представлений новий назад сумісний функціонал. Вона ПОВИННА бути збільшена, якщо який-небудь функціонал публічного API позначений як застарілий (deprecated). Вона МОЖЕ бути збільшена в разі реалізації нового функціоналу або суттєвого вдосконалення в приватному коді. Вона МОЖЕ включати в себе зміни, характерні для патчів. Патч-версія ПОВИННА бути обнулена, коли збільшується мінорна версія.
Мажорна версія X (X.y.z | X> 0) ПОВИННА бути збільшена, якщо в публічному API представлені будь-які назад несумісні зміни. Вона МОЖЕ включати в себе зміни, характерні для рівня мінорних версій і патчів. Коли збільшується мажорна версія, мінорна і патч-версія ПОВИННІ бути обнулені.
Предрелізний версія МОЖЕ бути позначена додаванням дефіса та серією розділених крапкою ідентифікаторів, таких відразу за патч-версією. Ідентифікатори ПОВИННІ містити тільки 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.
Пріоритет визначає, як версії співвідносяться один з одним, коли упорядковуються. Пріоритет версій ПОВИНЕН розраховуватися шляхом поділу номерів версій на мажорну, мінорну, патч і передрелізні ідентифікатори. Саме в такій послідовності (складальні метадані не фігурують в розрахунку). Пріоритет визначається за першим відмінності при порівнянні кожного з цих ідентифікаторів зліва направо: Мажорна, мінорна і патч-версія завжди порівнюються чисельно. Приклад: 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, тоді користувачі будуть знати правила і отримувати з цього користь.
Що я повинен робити з ревізіями в 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, то краще випустити новий реліз зі збільшенням головною версією, навіть незважаючи на те, що фікс містить виправлення рівня патча. Запам'ятайте, в Семантичному версіонірованія номера версій змінюються строго дотримуючись специфікації. Якщо ці зміни важливі для ваших користувачів, використовуйте номер версії, щоб інформувати їх.
Що робити із застарілим функціоналом?
Чи є в SemVer ліміти на довжину рядка версії?
Ні, але керуйтеся здоровим глуздом. 255 символів в рядку версії, мабуть, перебір. Крім того, певні системи можуть пред'являти свої власні обмеження на розмір рядка.
Якщо ви хочете залишити відгук, будь ласка, створіть запит на GitHub.