نسخه بندی معنایی v2.0.0-rc.1

در دنیای مدیریت نرم‌افزار یک موقعیت ناخوشایند به نام "جهنم وابستگی" وجود دارد. هر چه سیستم شما بزرگ‌تر می‌شود و هر چه کتابخانه‌های بیشتری به نرم‌افزار خود می‌افزایید، به همان میزان احتمال گرفتار شدن شما در این دام بیشتر خواهد شد.

در سیستم‌هایی با وابستگی بسیار زیاد، انتشار نسخه جدید کم کم تبدیل به یک کابوس می‌شود. اگر وابستگی شما به کتابخانه‌ها بسیار زیاد و پیچیده باشد، شما در معرض خطر قفل شدن انتشار نسخه جدید قرار می‌گیرید (عدم توانایی برای بروزرسانی یک کتابخانه بدون انتشار نسخه جدید کتابخانه‌های وابسته). اگر وابستگی شما به کتابخانه‌ها خیلی بی‌قاعده و کم باشد، شما ناگزیر توسط نسخه‌های بی‌قاعده درگیر می‌شوید. هنگامی که مشکل قفل شدن انتشار نسخه جدید و/یا انتشار نسخه‌های بی‌قاعده شما را از حرکت مناسب و خوب برای تکمیل پروژه‌تان بازمی‌دارد، در واقع شما در "جهنم وابستگی" قرار دارید.

به عنوان یک راه حل برای حل این مشکل، یک مجموعه ی ساده از قوانین و الزامات که چگونگی طراحی شماره های نسخه و افزایش آن را دستور میدهد را پیشنهاد می‌کنیم. برای کار کردن این سیستم، شما ابتدا نیاز به اعلام API عمومی دارید. این خود ممکن است شامل مستندات و یا اجرای کد باشد. علی رغم آن، مهم است که این API روشن و دقیق باشد. هنگامی که API عمومی خود را تعیین کردید، تغییرات برنامه شما بر روی نسخه API عمومی تاثیر خواهد داشت و آنرا افزایش خواهد داد. بر این اساس، این مدل نسخه‌بندی را در نظر بگیرید: X.Y.Z یعنی (Major.Minor.Patch). رفع حفره‌هایی که بر روی API عمومی تاثیر نمی‌گذارند، مقدار Patch را افزایش می‌دهند، تغییرات جدیدی که سازگار با نسخه قبلی است، مقدار Minor را افزایش می‌دهند و تغییرات جدیدی که کاملا بدیع هستند و به نحوی با تغییرات قبلی سازگار نیستند مقدار Major را افزایش می‌دهند.

تعریف‌های نسخه بندی معنایی (SemVer)

واژه‌های "باید"، "نباید"، "الزامی"، "بهتر است"، "توصیه می‌شود"، "می‌تواند" و... در این سند مورد استفاده است و توضیحات کامل آن در RFC2119 داده شده است.

  1. نرم‌افزارهایی که از نسخه بندی معنایی استفاده می‌کنند، باید یک API عمومی داشته باشند. این API می تواند در خود کد یا و یا به طور صریح در مستندات باشد که باید دقیق و جامع باشد.

  2. یک شماره نسخه صحیح باید به‌ شکل X.Y.Z باشد که در آن X،Y و Z اعداد صحیح غیر منفی هستند. X نسخه‌ی Major می‌باشد، Y نسخه‌ی Minor و Z نسخه‌ی Patch می‌باشد. هر عنصر باید یک به یک و بصورت عددی افزایش پیدا کند. به عنوان مثال:
    1.9.0 -> 1.10.0 -> 1.11.0

  3. هنگامی که به یک نسخه‌ی Major یک واحد اضافه می‌شود، نسخه‌ی Minor و Patch باید به حالت 0 (صفر) تنظیم مجدد گردد. هنگامی که به شماره نسخه‌ی Minor یک واحد اضافه می‌شود، نسخه‌ی Patch‌ باید به حالت 0 (صفر) تنظیم مجدد شود. به عنوان مثال:
    1.1.3 -> 2.0.0
    2.1.7 -> 2.2.0

  4. هنگامی که یک نسخه از یک کتابخانه منتشر می‌شود، محتوای کتابخانه مورد نظر نباید به هیچ وجه تغییری داشته باشد. هرگونه تغییر جدید باید در قالب یک نسخه جدید انتشار پیدا کند.

  5. نسخه‌ی Major صفر (0.Y.Z) برای توسعه‌ی اولیه است. هر چیزی ممکن است در هر زمان تغییر یابد. API عمومی را نباید پایدار در نظر گرفت.

  6. نسخه 1.0.0 در حقیقت API عمومی را تعریف می‌کند. چگونگی تغییر و افزایش هر یک از نسخه‌ها بعد از انتشار این نسخه، وابسته به API عمومی و تغییرات آن می‌باشد.

  7. نسخه Patch یا (x.y.Z | x > 0) فقط در صورتی باید افزایش پیدا کند که تغییرات ایجاد شده در حد برطرف کردن حفره‌های نرم‌افزار باشد. برطرف کردن حفره‌های نرم‌افزار شامل اصلاح رفتارهای اشتباه در نرم‌افزار می‌باشد.

  8. نسخه Minor یا (x.Y.z | x > 0) فقط در صورتی افزایش پیدا خواهد کرد که تغییرات جدید و سازگار با نسخه قبلی ایجاد شود. همچنین این نسخه باید افزایش پیدا کند اگر بخشی از فعالیت‌ها و یا رفتارهای قبلی نرم‌افزار به عنوان فعالیت منقرض شده اعلام شود. همچنین این نسخه می‌تواند افزایش پیدا کند اگر تغییرات مهم و حیاتی از طریق کد خصوصی ایجاد و اعمال گردد. تغییرات این نسخه می‌تواند شامل تغییرات نسخه Patch هم باشد. توجه به این نکته ضروری است که در صورت افزایش نسخه Minor، نسخه Patch باید به 0 (صفر) تغییر پیدا کند.

  9. نسخه Major یا (X.y.z | X > 0) در صورتی افزایش پیدا خواهد کرد که تغییرات جدید و ناهمخوان با نسخه فعلی در نرم‌افزار اعمال شود. تغییرات در این نسخه می‌تواند شامل تغییراتی در سطح نسخه Minor و Patch نیز باشد. باید به این نکته توجه شود که در صورت افزایش نسخه Major، نسخه‌های Minor و Patch باید به 0 (صفر) تغییر پیدا کنند.

  10. یک نسخه قبل از انتشار می‌تواند توسط یک خط‌تیره (dash)، بعد از نسخه Patch (یعنی در انتهای نسخه) که انواع با نقطه (dot) از هم جدا می‌شوند، نشان داده شود. نشان‌گر نسخه قبل از انتشار باید شامل حروف، اعداد و خط‌تیره باشد [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

  11. یک نسخه Build ‌‌می‌تواند توسط یک علامت مثبت ()، بعد از نسخه Patch یا نسخه قبل از انتشار (یعنی در انتهای نسخه) که انواع آن با نقطه (dot) از هم جدا می‌شوند، نشان داده شود. نشان‌گر نسخه Build باید شامل حروف، اعداد و خط‌تیره باشد [0-9A-Za-z-]. باید به این نکته دقت داشت که نسخه‌های Build خود به تنهایی یک انتشار به حساب می‌آیند و اولویت و اهمیت بیشتری نسبت به نسخه‌های عادی دارند. برای مثال: 1.0.0build.1
    1.3.7build.11.e0f985a

  12. اولیت‌بندی نسخه‌ها باید توسط جداسازی بخش‌های مختلف یک نسخه به اجزای تشکیل دهنده آن یعنی Minor، Major، Patch، نسخه قبل از انتشار و نسخه Build و ترتیب اولویت بندی آن‌ها صورت گیرد. نسخه‌های Minor، Major و Patch باید بصورت عددی مقایسه شوند. مقایسه نسخه‌های قبل از انتشار و نسخه Build باید توسط بخش‌های مختلف که توسط جداکننده‌ها (نقطه‌های جداکننده) تفکیک شده است، به این شکل سنجیده شود:
    بخش‌هایی که فقط حاوی عدد هستند، بصورت عددی مقایسه می‌شوند و بخش‌هایی که حاری حروف و یا خط‌تیره هستند بصورت الفبایی مقایسه خواهند شد. بخش‌های عددی همواره اولویت پایین‌تری نسبت به بخش‌های غیر عددی دارند. برای مثال:
    1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0-rc.1build.1 < 1.0.0 < 1.0.00.3.7 < 1.3.7build < 1.3.7build.2.b8f12d7 < 1.3.7build.11.e0f985a

چرا استفاده از نسخه بندی معنایی؟

این یک ایده‌ی جدید یا انقلابی نیست. در حقیقت ممکن است کارهایی شبیه به آن را قبلا انجام داده باشید. موضوع این است که شباهت به این کار به اندازه‌ی کافی خوب نیست. شماره بندی نسخه‌ها بدون تطبیق با برخی کلاس بندی نسخه‌ها، برای مدیریت وابستگی بی فایده می‌باشد. با اسم گذاری و تعریف روشنی از ایده فوق، به راحتی میتوانید منظور خود را به کاربران نرم‌‌افزار خود منتقل کنید. هنگامی که این مفاهیم روشن باشند، شما می‌توانید جزئیات وابستگی‌های خود را مشخص کنید.

یک مثال ساده دقیقا می‌تواند بیانگر این موضوع باشد. یک نرم‌افزار به نام "Firetruck" را در نظر بگیرید. این نرم‌افزار نیاز به یک کتابخانه به نام "Ladder" دارد که از نسخه بندی معنایی پیروی می‌کند. وقتی Firetruck ایجاد می‌شود، Ladder در نسخه‌ی 3.1.0 است. از آنجایی که Firetruck خواصی که در ابتدا در 3.1.0 معرفی شد را استفاده می‌کند، می‌توانید با آرامش خاطر از نسخه‌های بزرگتر از 3.1.0 و کمتر از 4.0.0 استفاده کنید. حال وقتی Ladder نسخه‌ی 3.1.1 و 3.2.0 در دسترس باشد، شما می توانید به راحتی آن‌ها را منتشر کنید و بدانید با نرم‌افزارهایی که با آن وابسته هستند سازگاری کامل دارد.

دنیای واقعی پیچیده است، نمی‌توان کاری کرد اما باید گوش به زنگ بود. آنچه شما می‌توانید انجام دهید آن است که اجازه دهید نسخه بندی معنایی شما را به یک راه عاقلانه برای انتشار و به روز رسانی بسته‌ها مجهز کند، این کار در وقت و انرژی شما صرفه جویی می‌کند.

اگر تمام این‌ها مطلوب به نظر برسد، تمام آن چیزی که شما احتیاج دارید برای شروع استفاده از نسخه بندی معنایی این است که اول مشخص کنید می‌خواهید از آن استفاده کنید و سپس از قوانین مشخص شده پیروی نمایید!

پرسش و پاسخ (FAQ)

چگونه باید در شروع ایجاد یک نرم‌افزار نسخه اولیه را تعریف کرد و در توسعه‌های بعدی نسخه را افزایش داد؟

بهترین و آسان‌ترین راه برای ایجاد و تعریف نسخه اولیه نرم‌افزارتان این است که با نسخه 0.1.0 شروع کنید و سپس در هر گسترش با توجه به قوانین تعریف شده اقدام به افزایش نسخه کنید.

چه زمانی نسخه 1.0.0 باید منتشر شود؟

اگر نرم‌افزار شما در مرحله استفاده کاربردی است و بصورت محصول ارائه شده است، احتمالا در نسخه 1.0.0 قرار دارید. اگر یک API بی‌عیب و نقص دارید که مشتریانی از آن استفاده می‌کنند و یا به آن وابستگی دارند، شما در نسخه 1.0.0 قرار دارید. اگر نسبت به سازگاری نرم‌افزار خود با بسترهای قدیمی حساسیت زیادی دارید، پس احتمالا در وضعیت نسخه 1.0.0 قرار دارید.

آیا این گردش‌های سریع و توسعه سریع نرم‌افزار را نکوهش نمی‌کند؟

به این نکته توجه کنید که نسخه Major صفر (0) دقیقا به توسعه سریع اشاره دارد و با توسعه سریع همراه است. اگر API شما دائما تغییر می‌کند، یا در حال کار بر روی نسخه 0.x.x هستید یا اینکه در حال کار بر روی نسخه بعدی نرم‌افزار، در یک شاخه (Branch) توسعه دیگر هستید.

اگر حتی برای کوچک‌ترین تغییر نامطابق با تغییرات گذشته، مقدار نسخه Major را افزایش دهیم، آیا خیلی سریع و ناشیانه به نسخه 42.0.0 نمی‌رسیم؟

این سوال بسیار مهمی است، بهتر است کمی دقیق‌تر به اصل موضوع توجه کنیم. در واقع، تغییرات ناسازگار نباید به راحتی در یک نرم‌افزار با وابستگی‌های بسیار زیاد ایجاد شود. هزینه‌ای که برای بروزرسانی پرداخت می‌شود واقعا قابل توجه است. افزایش دادن نسخه Major به منظور ارائه برخی تغییرات ناسازگار در یک نرم‌افزار باید با توجه میزان، اهمیت و تاثیرگذاری تغییرات ایجاد شده باشد نه هر تغییر کوچک.

مستندسازی کل API عمومی کار بسیار زیاد و سختی است!

این در حقیقت عکس‌العمل شما به عنوان یک برنامه‌نویس حرفه‌ای است که به استفاده کنندگان از برنامه شما کمک می‌کند تا راحت‌تر بتوانند با برنامه شما کار کنند. مدیریت کردن یک نرم‌افزار یکی از مهم‌ترین بخش‌ها در جهت حفظ کارایی یک پروژه است، و این بسیار سخت و پیچیده خواهد بود اگر کسی نداند که چطور با برنامه شما کار کند، کدام method برای صدا زدن بهتر است یا چه بخش‌هایی از برنامه منقرض شده‌اند. در طی زمان و با بالا رفتن عمر پروژه، نسخه بندی معنایی و پافشاری بر تهیه یک API عمومی استاندارد و طبق قوانین تعیین شده، به ادامه پروژه یا پروژه‌های که از برنامه شما استفاده می‌کنند کمک می‌کند که به راحتی و بدون مشکل کار کنند.

چه عکس‌العملی باید صورت گیرد اگر یک تغییر ناسازگار ارائه شده باشد اما بجای نسخه Major، نسخه Minor افزایش پیدا کرده باشد؟

به محض اینکه متوجه شدید قوانین نسخه بندی معنایی را زیر پا گذاشته‌اید، سریعا مشکل را برطرف سازید و یک نسخه Minor جدید منتشر کنید که مشکل در آن برطرف شده است. به خاطر داشته باشید، این اصلا قابل پذیرش نیست که نسخه‌های منتشر شده را ویرایش یا اصلاح کنید،‌ حتی در یک حالت بحرانی. در هر صورت اگر در حالتی خاص مجبور به انجام این کار شدید، باید آن را مستند کنید و نسخه‌ای که اصلاح شده را در مستندات ذکر کنید تا مشتریان و کاربران نرم‌افزار از مشکل بوجود امده مطلع گردند و از استفاده نسخه دچار مشکل پرهیز کنند.

چه کاری باید انجام دهم وقتی می‌خواهم یک وابستگی داخلی نرم‌افزارم را بروز کنم؟

اگر بروز کردن وابستگی‌های نرم‌افزارتان بر روی API عمومی تاثیر گذار نباشد، هنوز می‌توانید بدون تغییر به کارتان ادامه دهید. اینکه بعد از بروز رسانی وابستگی‌های پروژتان، باید چه نسخه‌ای منتشر شود دقیقا مرتبط به این است که شما به چه دلیل وابستگی‌هایتان را بروز کرده‌اید. بروزرسانی برای ایجاد یک امکان جدید در نرم‌افزار انجام شده یا مشکل/حفره‌ای که از قبل وجود داشته را با این کار حل کرده‌اید؟

چگونه فعالیت‌ها/قابلیت‌هایی که در نرم‌افزار منقرض شده‌اند را مدیریت کنم؟

منقرض اعلام کردن یک قابلیت یا فعالیت، روال طبیعی تولید نرم‌افزار است که گاهی اوقات برای ادامه بهتر و سریع‌تر پروژه لازم است. زمانی که بخشی از API عمومی را می‌خواهید منقرض شده اعلام کنید، باید دو عمل مهم را انجام دهید:

توصیه می‌شود قبل از انتشار نسخه Major به منظور حذف کامل قسمت منقرض شده، حداقل یک نسخه Minor قبل از انتشار این نسخه وجود داشته باشد که به کاربران و ذی‌نفعان تغییر اعمال شده را اطلاع‌رسانی کند تا بتوانند راحت‌تر به نسخه جدید API کوچ کنند و از آن استفاده کنند.

درباره ما

سند نسخه بندی معنایی نوشته شده توسط Tom Preston-Werner، مخترع Gravatars و سازنده Github

ترجمه شده به فارسی توسط usabli.ca

این سایت متعلق است به شرکت توسعه سه ضلع فن آوری اطلاعات رایبد با مدیریت محمد صادق مالکی

لطفا توجه کنید این سند هنوز دوران جوانی خود را طی می‌کند لذا ممکن است نواقصی در متن آن مشاهده شود، کاری که شما می‌توانید انجام دهید این است که در تکمیل آن یاری برسانید.

اگر مایل هستید بازخوردی یا اصلاحی برای این سند ایجاد کنید به این صفحه در Github مراجعه کنید

حق انتشار

Creative Commons License
سند نسخه بندی معنایی از لایسنس Creative Commons Attribution 3.0 Unported License تبعیت می‌کند.