სემანტიკური ვერსიონირება 2.0.0

მოკლედ

როდესაც გვაქვს ვერსიის ნომერი მაჟორული.მინორული.პატჩი, უნდა გაიზარდოს:

  1. მაჟორული ვერსია, როდესაც შეტანილია არა უკუთავსებადი ცვლილებები API-ში.
  2. მინორული ვერსია, როდესაც ემატება ახალი ფუნქციონალი, უკუთავსებადობის დაურღვევლად.
  3. პატჩ-ვერსია, როდესაც ემატება უკუთავსებადი ჩასწორებები.

პრე-რელიზის და ბილდის მეტა-მონაცემების დამატებითი აღნიშვნები შესაძლებელია მიემატოს ფორმატს მაჟორული.მინორული.პატჩი

შესავალი

პროგრამული უზრუნველყოფის შემუშავების მართვის პროცესში არსებობს ცნება “დამოკიდებულებათა ჯოჯოხეთი” (dependency hell). რაც უფრო იზრდება სისტემა და რაც უფრო მეტი ბიბლიოთეკის ინტეგრირება ხდება პროექტში, მით უფრო იზრდება ამ სიტუაციაში მოხვედრის ალბათობა.

სისტემაში სადაც ბევრი დამოკიდებულებაა, ახალი ვერსიის გამოშვება შეიძლება გადაიქცეს დიდ თავის ტკივილად. თუ დამოკიდებულებების სპეციფიკაცია ზედმეტად მკაცრია, არსებობს ახალი ვერსიის გამოშვების ბლოკირების საფრთხე (შეუძლებელია პაკეტის განახლება თითოეული დამოკიდებული ბიბლიოთეკის განახლების გარეშე). თუ დამოკიდებულებების სპეციფიკაცია ზედმეტად თავისუფალია, ადრე თუ გვიან აუცილებლად დადგება ვერსიების შეუსაბამობის პრობლემა (უსაფუძვლო იმედი მომავალ ვერსიებთან თავსებადობისა). “დამოკიდებულებათა ჯოჯოხეთი” არის მდგომარეობა როდესაც დამოკიდებულებების არსებობა ხელს უშლის პროექტის წინსვლას და განვითარებას.

ამ პრობლემის მოგვარებისთვის გთავაზობთ უბრალო წესების და მოთხოვნების კრებულს, რომელიც განსაზღვრავს როგორ ენიჭება და იცვლება ვერსიის ნომრები. იმისათვის რომ ამ სისტემამ იმუშაოს, თქვენ უნდა განსაზღვროთ გარე (public) API. ის შეიძლება აღიწეროს როგორც დოკუმენტაციაში, ასევე თავად კოდის მეშვეობით. მთავარია, რომ ეს API იყოს გასაგები და ზუსტი. მას შემდეგ რაც ერთხელ აღწერთ გარე API-ს, მასში ცვლილებების შესახებ მის მომხმარებლებს თქვენ ამცნობთ სპეციალური ვერსიის ნომრის გაზრდის მეშვეობით. განვიხილოთ ვერსიის ფორმატი X.Y.Z (მაჟორული, მინორული, პატჩი). ბაგ-ფიქსები რომელთაც არ აქვთ გავლენა API-ზე, ზრდის პატჩ-ვერსიას, უკუთავსებადი დამატებები და ცვლილებები ზრდის მინორულ ვერსიას, ხოლო არა უკუთავსებადი ცვლილებები ზრდის მაჟორულ ვერსიას.

ამას ვუწოდებ “სემანტიკურ ვერსიონირებას” (Semantic Versioning). ამ სქემის მიხედვით ვერსიის ნომრები და მათი ცვლილება აღწერს იმას, თუ როგორ შეიცვალა და რა შეიცვალა კოდში ერთი ვერსიიდან მეორემდე.

სემანტიკური ვერსიონირების სპეციფიკაცია (SemVer)

სიტყვები “უნდა” (MUST), “არ უნდა” (MUST NOT), “აუცილებელია” (REQUIRED), “სასურველია” (SHOULD), “არ არის სასურველი” (SHOULD NOT), “რეკომენდირებულია” (RECOMMENDED), “შეიძლება” (MAY) და “არასავალდებულოა” (OPTIONAL) ამ დოკუმენტში უნდა იქნეს ინტერპრეტირებული შესაბამისად RFC 2119 სპეციფიკაციისა.

  1. პროგრამული უზრუნველყოფა რომელიც იყენებს სემანტიკურ ვერსიონირებას, “უნდა” აღწერდეს გარე API-ს. ეს 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.

ხელს ხომ არ უშლის სწრაფ დეველოპმენტს (Rapid Development) და მოკლე იტერაციებს (Fast Iteration)?

მაჟორული ვერსია 0 ზუსტადაც რომ ნიშნავს სწრაფ დეველოპმენტს. თუ თქვენ ცვლით API-ს ყოველ დღე, უნდა გაჩერდეთ ვერსიაზე 0.y.z ან უნდა იმუშაოთ განსხვავებულ ტოტზე (branch) შემდეგი მაჟორული ვერსიის რელიზისთვის.

გარე API-ში ძალიან უმნიშვნელო არა უკუთავსებადი ცვლილებებიც საჭიროებს ახალი მაჟორული ვერსიის გამოშვებას? ეს ხომ არ გამოიწვევს იმას, რომ ძალიან მალე ვერსიის ნომერი გახდება 42.0.0?

წინდახედული და პასუხისმგებლიანი კითხვაა. არა უკუთავსებადი ცვლილებები არ შეიძლება რომ წარმოდგენილი იყოს როგორც უმნიშვნელო ცვლილებები, მითუმეტეს როდესაც ამ API-ზე დამოკიდებულია კიდევ სხვა კოდი. განახლების ღირებულება შეიძლება დიდი აღმოჩნდეს. არა უკუთავსებადი ცვლილებების შემთხვევაში, მაჟორული ვერსიის გაზრდის პრაქტიკა ნიშნავს, რომ თქვენ მოგიწევთ ფიქრი ცვლილებების შესახებ და ღირებულება/სარგებელის შეფარდების გათვალისწინება.

სრული API-ს დოკუმენტირება ძალიან დიდი სამუშაოა!

პროგრამული უზრუნველყოფის სწორი დოკუმენტირება არის თქვენი პასუხისმგებლობა, როგორც პროფესიონალი დეველოპერის. მითუმეტეს, როდესაც ეს პროგრამული უზრუნველყოფა გათვლილია ფართო აუდიტორიაზე. პროგრამული უზრუნველყოფის კომპლექსურობის მართვა არის მნიშვნელოვანი ნაწილი პროექტის მაღალი ეფექტურობის შენარჩუნებისთვის. და ეს ძალიან რთულია, როდესაც მომხმარებელმა არ იცის როგორ გამოიყენოს თქვენი პროგრამული უზრუნველყოფა ან რომელი მეთოდის გამოძახება შეუძლია უსაფრთხოდ. ხანგრძლივ პერსპექტივაში სემანტიკური ვერსიონირება და გარე API-ს ხარისხიანი დოკუმენტირებისადმი დაჟინებული სწრაფვა დაგეხმარებათ შეუფერხებლად მუშაობაში.

როგორ მოვიქცე თუ შემთხვევით არა უკუთავსებადი ცვლილებები დავარელიზე როგორც მინორული ვერსია?

როგორც კი აღმოაჩენთ, რომ დაარღვიეთ სემანტიკური ვერსიონირების სპეციფიკაცია, გაასწორეთ პრობლემა და გამოუშვით ახალი მინორული ვერსია, რომელიც აღადგენს უკუთავსებადობას. ასეთ ვითარებაშიც კი დაუშვებელია უკვე გამოშვებული რელიზის მოდიფიცირება. თუ საჭიროდ ჩათვლით, სასურველია ასევე დოკუმენტაციაში უკუთავსებადობის დარღვევის მითითება იმ კონკრეტული ვერსიისთვის და მომხმარებლების ინფორმირება იმის თაობაზე რომ ვერსიების მიმდევრობა დარღვეულია.

რა უნდა გავაკეთო, თუ ვანახლებ საკუთარ დამოკიდებულებებს გარე API-ს ცვლილების გარეშე?

ეს შეიძლება განიხილოს როგორც თავსებადი ცვლილებები, რაკი მას გავლენა არ აქვს გარე API-ზე. პროგრამულ უზრუნველყოფას რომელიც თავისმხრივ დამოკიდებულია იმავე დამოკიდებულებებზე, უნდა გააჩნდეს საკუთარი დამოკიდებულებების სპეციფიკაციები და შესაბამისად ავტორი შეიტყობს შესაძლო კონფლიქტების შესახებ. ცვლილებები უნდა აღიწეროს როგორც პატჩ-ვერსია თუ მინორული, ეს
განისაზღვრება იმით, დამოკიდებულებები განახლდა ხარვეზის გასწორების გამო თუ დაემატა ახალი ფუნქციონალი. უკანასკნელის შემთხვევაში, როგორც წესი ემატება ახალი კოდი და შესაბამისად ახლდება მინორული ვერსია.

რა უნდა გავაკეთო, თუ შემთხვევით შევცვალე გარე API და არასწორად გავზარდე ვერსიის ნომერი (მაგალითად კოდი შეიცავს არა უკუთავსებად ცვლილებებს პატჩ-ვერსიაში)?

მოიქეცით თქვენი შეხედულებისამებრ. თუ თქვენი აუდიტორია საკმაოდ დიდია და გახდა საჭირო API-ს ძველი ფუნქციონალის დაბრუნება, მაშინ სასურველია ახალი მაჟორული ვერსიის გამოშვება, მიუხედავად იმისა, რომ ცვლილებები შეიცავს პატჩ-რელიზის შესაბამის ჩასწორებებს. დაიხსომეთ, სემანტიკურ ვერსიონირებაში ვერსიის ნომრები იცვლება სპეციფიკაციის მკაცრი დაცვით. თუ ცვლილებები მნიშვნელოვანია თქვენი მომხმარებლებისთვის, გამოიყენეთ ვერსიის ნომერი მათი ინფორმირებისთვის.

როგორ მოვიქცე მოძველებულ ფუნქციონალთან მიმართებაში?

ფუნქციონალის მოძველებულად გამოცხადება — ეს ჩვეულებრივი ამბავია დეველოპმენტის პროცესში და ხშირად აუცილებელიც კია პროდუქტის განსავითარებლად. როდესაც გარე API-ს რაიმე ნაწილს აცხადებთ მოძველებულად, თქვენ უნდა გააკეთოთ ორი რამ: (1) განაახლოთ თქვენი დოკუმენტაცია, რათა მომხმარებლებმა შეიტყონ ამ ცვლილების შესახებ; (2) გამოუშვათ ახალი რელიზი მინორული ვერსიის გაზრდით. მანამ, სანამ მთლიანად ამოიღებთ კოდიდან მოძველებულ ფუნქციონალს და დაარელიზებთ მაჟორულ ვერსიას, უნდა არსებობდეს მინიმუმ ერთი მინორული რელიზი, რომელიც შეიცავს ფუნქციონალის მოძველებულად გამოცხადებას. ეს აუცილებელია იმისათვის, რომ მომხმარებლებმა უმტკივნეულოდ შეძლონ ახალ API-ზე გადასვლა.

სემანტიკურ ვერსიონირებას თუ გააჩნია ვერსიაში შესაძლო სიმბოლოების ლიმიტი?

არა, თუმცა იხელმძღვანელეთ საღი აზრით. ალბათ დამეთანხმებით 255 სიმბოლო ვერსიის აღწერაში ზედმეტია. ასევე, ზოგიერთ სისტემას შეიძლება გააჩნდეს საკუთარი შეზღუდვები ხაზის დასაშვები სიგრძის მიმართ.

ავტორის შესახებ

სემანტიკური ვერსიონირების სპეციფიკაციის ავტორია ტომ პრესტონ-ვერნერი, Gravatar-ის დამფუძნებელი და GitHub-ის თანადამფუძნებელი.

თუ გსურთ დატოვოთ თქვენი გამოხმაურება, გთხოვთ შექმნათ დაამატოთ თემა GitHub-ზე.

ქართული თარგმანი

ლიცენზია

Creative Commons — CC BY 3.0