مشکل بزرگ ما و ضربه بدی که خوردیم چی بود؟
همیشه راه حل ها از مشکلات ساخته میشن. همونجور که از اسمشون پیداست راهی برای حل کردن یک مسئله. ماجرا از جایی شروع شد که ما با یک مشکل بزرگ روبه رو شدیم. اپلیکیشنی که پابلیش شد، حاوی یه سری فیچر بود که در حال توسعه بودند. یعنی هنوز کارمون با اونا تموم نشده بود ولی در دسترس تعداد زیادی کاربر قرار گرفت و کلی مشکلات به وجود آورد از جمله که کارفرما یکی از این فیچر ها رو خیلی رقابتی میدونست و حالا دیگه رقیب ها هم میدونستن که قراره چی بیاد توی ورژن بعدی. امیدوارم اهمیت مسئله رو خوب بیان کرده باشم. از اونجایی که اکثر ماها سعی میکنیم مشکلی پیش بیاد تا بعد رفعش کنیم و به جلوگیری اعتقادی نداریم این موارد پیش میاد. در نتیجه تصمیم بر این شد این مقاله رو بنویسیم تا کسایی که هنوز با مشکلاتی شبیه چیزی که ما تجربه کردیم مواجه نشدن، بخونن و به فکر پیشگیری بیوفتن.
اگر بخوام مثال های دیگه ای برای اینکه چرا ما به یک روند واحد برای مدریت گیت پروژه هامون نیاز داریم بزنم میتونم به
چرا GitFlow؟ اصلا Gitflow چیه؟
توی دنیا، از شرکت های خیلی کوچیک تا بزرگ برای نگه داشتن کدهاشون، توسعه اون ها و مدیریتشون از ابزارهای خاصی استفاده میکنند. بعضی ها پروژه رو به صورت یه فایل زیپ بین هم جابجا میکنن و بعضی هم پیشرفت کردند (که اکثرا دیگه میشه گفت ۹۹٪ شرکتها) و از گیت برای مدیریت پروژه هاشون استفاده میکنند. اینجوری در هر زمان افراد مختلف میتونن روی کدها کار کنند، تغییرات هم رو ببینین، تاریخچه کدها رو داشته باشن، محیط امنی فراهم کنند تا از اتفاقات ناخواسته جلوگیری کنند و خیلی موارد دیگه. قطعا اگر دارین این مطلب رو میخونین درباره گیت میدونین ولی اگر احیانا نمیدونین میتونین از این لینک اطلاعات خوبی درباره گیت بدست بیارین.
دقیقا GitFlow چیکار میکنه؟ شرکت ها برای اینکه هماهنگی بین تیمشون به وجود بیارن، میان و یک روند استاندارد برای تیمشون تعریف میکنن تا همه اعضای تیم دقیقا بدونن که چه طور برنچ جدید درست کنند، چطور فیچر اضافه کنند، باگ فیکس ها رو چطور مدیریت کنند، خروجی ها و ریلیز ها چطور مدیریت میشه روی گیت اون شرکت. روشهای استانداردی برای اینکار وجود داره مثلا گیتهاب و گیتلب روش های پیشنهادی خودشون رو گفتن و حتی یک مقاله خوب هم دراین باره نوشته شده که میتونین در اینجا مطالعه کنین و خیلیها هم دارن به همون روش ها جلو میرن، ولی این روش ها همیشه و الزاما برای همه بهترین گزینه نیستند، چون همونجور که اول مطلب گفتیم، اول مشکل پیش میاد بعد راه حلش، در نتیجه این مطالب هرچند فراگیر باشند و بتونین جامعه زیادی رو پوشش بدن، فرض کنیم ۹۰٪ جامعه رو پوشش بدن، ولی همیشه هستند شرکت و تیم هایی که این روشها نمیتونن نیاز هاشونو برطرف کنند، شاید شما و ما هم یکی از اون ۱۰٪ باشیم.
بهتره اول روش عمومی و پیشنهادی رو بیان کنم
روش استاندارد GitFlow
توی این روش ما دو برنچ اصلی Master و Develop داریم، همه دولوپرها ملزم به استفاده از برنچ Develop به عنوان برنچ اصلی هستند و تنها زمانی که یک کد ریلیز میشه، کدهای اون ریلیز روی مستر push میشه. برنچ دولوپ از مستر ساخته میشه و همیشه حضور داره و هیچوقت پاک نمیشه، دولوپر ها همه کارهاشون روی این برنچ انجام میدن. زمانیکه نیاز به ریلیز باشه، یک برنچ ریلیز از برنچ دولوپ گرفته میشه اون کدها ریلیز میشن و در نهایت برنچ ریلیز با مستر merge میشه و کدهای اون ریلیز روی مستر قرار میگیره. برنچ دولوپ اینجور مدیریت میشه که برای هر فیچر جدید که قراره به اپلیکیشن اضافه بشه دولوپر ها یک برنچ از روی Develop میسازن و روی اون برنچ جدید کارهاشونو انجام میدن و وقتی تموم میشه push میشه و در نهایت اون برنچ حذف میشه. این روند استاندارد هستش و خیلی وقت ها تمام نیاز ها رو جواب میده
هیچ ایرادی نداره به صورت پیشفرض همگی از این روش استفاده میکنیم ولی چون روش استاندارد هستش باید یه سریی چیز ها برای سازگاری با محیط ایران به اون اضافه کنیم.
ما چرا باید به روند استاندارد که بالا گفته شد چیزی اضافه کنیم؟
همینجوری که همه میدونیم، ایران خیلی چیزهاش شبیه بقیه دنیا نیست، معمولا روشهایی که روی همه کشور ها جواب میده با توجه به وضعیت اقتصادی، سیاسی، فرهنگی و شخصیتی ایرانی ها، روی ما جواب نمیده. اگه بخوام به یکی از این موارد و معضلات اشاره کنم، اینه که نه تیم فنی و نه کارفرما، برنامه ریزی دقیقی ندارند و در واقع بیشتر کارفرما بجای اینکه از روی یه پلن از پیش تعیین شده بخواد جلو بره، به صورت لحظه ای تصمیم گیری میکنه و در نتیجه تیم فنی نیاز به هماهنگ شدن دارند، این معضل با توجه به شرایط سیاسی و اقتصادی ایران، کارفرما رو مجاب میکنه که بجای برنامه ریزی بلند مدت، تصمیم به انجام کارهای کوچیک کوتاه مدت و مقتضی اون زمان بکنه، در نتیجه تیم باید اینقدر چابک باشه تا بتونه با سرعت به تغییرات مورد نیاز کارفرما و درخواست های جدیدش رسیدگی کنه. از اونجایی که همچین وضعیت هایی خیلی کمتر توی شرکتهای اروپایی و امریکایی پیش میاد، در علمی که اکثرا از اون سمت به وجود میاد، این شرایط نادیده یا کمرنگ تر در نظر گرفته میشه و شاید راه حل ها و پیاده سازی های اونها در بعضی موارد با نیاز های ما توی ایران سازگار نباشه.
ما در روند فوق، ایراد هایی دیدیم که شاید مقتضی شرایط کاری ما باشه و احتمالا به خیلی های دیگه در ایران میتونه کمک کنه.
ما میخوایم به این مسائل پاسخ بدیم:
۱. ما میخوایم که Master، حاوی کد های اصلی و استیبل و پایدار برنامه باشه و هرکسی که به تیم اضافه شد یا حتی از بیرون اومد، بدونه اینو و مستر همیشه و در همه حال آماده خروجی گرفتن و ریلیز هستش. دولوپر ها به این برنچ دسترسی نداشته باشه
۲. خیلی وقت ها شده که ما میخوایم یه لیبل یا رنگ یک دکمه یا ارتفاع یک ستون یا موارد ساده کوچیکی مثل اینها رو برطرف کنیم ولی این ها نباید مستقیم روی برنچ مستر انجام بشه حتی اگر یک ثانیه کار داره
۳. مشکلاتی مثل Merge Hell رو باید پاسخ بدیم. Merge Hell در دو حالت به وجود میاد، زمانیکه تعداد درخواست های مرج خیلی زیاد باشه و یا اینکه موارد تغییر کرده در یک درخواست اینقدر زیاده که کسی که در حال بررسی این درخواست هستش نمیتونه دقیق پیش بره و کارش خیلی سخت میشه برای بررسی این درخواست
۴. بعضی وقت ها فیچر هایی به اپ اضافه میشه که نمیخوایم همون موقع در خروجی قرار بگیره یا در ریلیز بعدی باشه، ممکنه زمانی مثلا ۳ یا ۴ ماه بمونه و ریلیز نشه
۵. گاهی وقتها چند نفر روی یک برنچ کار میکنن یا چند نفر روی چند برنچ کار میکنن این باید مدیریت بشه
۶. گاهی وقت ها نیازه که در همون لحظه یک باگ برطرف بشه و خروجی گرفته بشه بدون اینکه فیچر های جدید درون اون ریلیز قرار بگیرند
۷. ما نیاز داریم که قبل از ریلیز نهایی که دست کاربران قرار میگیره، خروجی کاملا تست بشه و کل اپلیکیشن یکبار توسط تیم تست بررسی بشه.
۸. فیچر ها تا قبل اینکه تمام بشن به هیچ عنوان روی برنچ های ریلیز قرار نگیرند
۹. شاید یک دولوپر کارش نیازمند و وابسته به کار دولوپر دیگیری باشد
روند (GitFlow) پیشنهادی ما
برنچ های پیشنهادی ما :
برنچ Develop : این برنچ حاوی کد پایدار است. این برنچ تمام فیچر های کامل شده و پایدار را شامل میشود. این فیچر ها تست شدند، تمام مراحلشان تمام شده و اماده ریلیز شدن هستند. این برنچ فقط توسط مدیر تیم یا ارشد هایی که وظیفه بررسی کد را دارند مدیریت میشود و دولوپر ها تنها از طریق Pull-Request، میتوانند درخواست خود را برای ثبت کدهایشان روی برنچ Develop بدهند و بعد از اینکه ارشد ها، کدها را بررسی کردند و از همه نظر سالم بود، این درخواست را قبول میکنند و آن کدها روی برنچ Develop با Merge (یا Rebase) قرار می گیرند.
برنچ های Feature : این برنچ ها از Develop ساخته می شوند. وقتی قرار است یک فیچر به اپلیکیشن اضافه شود، یک برنچ از Develop گرفته میشود و فیچر جدید روی آن توسعه داده می شود. برای قاعده مند بودند اسامی برنچ ها، هر فیچر جدید با کلمه features به همراه یک / شروع می شوند مثل
features/login-page
features/profile-elastic-header
معمولا هر دولوپر روی یک برنچ فیچر توسعه میدهد. ولی زمانی پیش میاد که چند دولوپر میخوان روی یک برنچ فیچر کار کنن. مثلا فیچر خیلی بزرگ هستش. در این مورد، ما تنها یک برنچ روی ریموت و آنلاین و ریپازیتوری داریم ولی دولوپر ها هرکدام، این برنچ را روی سیستم خودشون کلون میکنند و روی سیستم لوکال خودشون برنچ خودشون رو درست میکنند. در این حالت پیشنهاد می شود تنها در دو حالت روی برنچ فیچر اصلی که روی ریموت قرار داره، push زده بشه،
فیچر ها میتونند همون موقع که تموم میشند روی Develop قرار بگیرند یا اینکه منتظر باشن تا زمانیکه ریلیز بشند. نحوه برخورد ما با این مسئله اینگونه است :
برنچ ّIssue (یکی از برنچهایی که ما به فلو استاندارد اضافه کردیم): این برنچ یک برنچ همیشگی است. ما آن را برای رفع مشکل تغییرات یا باگ های کوچک می سازیم و هیچوقت پاک نمی شود. این برنچ هر روز صبح از برنچ Develop یکبار pull می شود و تغییراتی مثل تغییر رنگ یک لیبل، تغییر یک نوشته، بزرگ کوچک کردن یک دکمه از آن استفاده میکنیم. این برنچ را ساختیم چون به صورت مستقیم روی برنچ Develop نمی توانیم push کنیم و از طرفی ساختن یک برنچ برای هر باگ یا تغییر کوچکی مثل مثال هایی گفته شد، کار زمان بر و خسته کننده ای هستش. ما اخر هر روز این تغییرات کوچیک انجام شده رو pull-request برای برنچ Develop میفرستیم و مرج میکنیم. نکته ای که وجود داره اینه که هر روز صبح pull و آخر روز کاری اگر تغییری انجام شده با برنچ Develop مرج کنیم (pull-request) و همچنین هر زمانی که خواستیم ریلیز بدیم، اونو اول با برنچ Develop مرج (pull-request) کنیم.
برنچ های Bug : این برنچ ها برای رفع باگ ها از برنچ Develop گرفته میشن و بعد از تصحیح و رفع اون باگ، برای برنچ Develop درخواست فرستاده میشه (pull-request) و بعد از تایید و مرج شدن با برنچ Develop، این برنچ ها پاک میشند.(البته ممکن است برای یک برنچی باشند که هنوز روی برنچ Develop قرار نگرفته که در این حالت از خود اون برنچ ساخته میشه و بعد اتمام روش push میشه) در قاعده نامگذاری به این شکل کار میکنیم. کلمه bugs به همراه / و نام اون برنچ و اگر در جیرا یا جایی دیگر ریپورت شده شماره اون ریپورت به همراه #
bugs/login-message-not-show#44
bugs/api-repsonde-404-not-handled#4337
برنچ های Release : این برنچ ها تنها قبل از ریلیز شدن ساخته میشن. قبل از هر ریلیز ما از برنچ Develop یک برنچ می سازیم و اونو میدیم به تیم تست، این تست شامل کل اپلیکیشن است، فیچر ها قبلا دونه دونه و جدا جدا در زمان توسعه تست شدند و روی برنچ Develop قرار گرفتند، در نتیجه برنچ Develop ما باید استیبل باشد ولی قبل از ریلیز نهایی، ما برنچی می سازیم و آن را میدهیم دست تیم تست تا یکی دو روز آن را تست کنند و یکپارچگی سیستم را بررسی کنند، چون ممکن است کدها هیچ ایرادی نداشته باشند ولی در جایی چیزی را پاک کردیم که چند ماه قبل در جایی دیگر از آن استفاده کردیم، این ها را یکبار توسط یونیت تست و بار دیگر توسط تیم تست به صورت دستی تست میکنیم. شاید بگیم که برنچ Release نیاز نیست و می شود مستقیم از برنچ Develop این کار تست را انجام داد، ولی بعضی وقتها تست یکی دو روز یا سه روز انجام می شود و تیم توسعه در حال توسعه سرویس های دیگر است. در این مدت تست، میتواند برنچ Develop تغییر کند و فیچر جدیدی اضافه شود ولی نیاز نباشد در ریلیز قرار بگیرد. در نتیجه ما از برنچ Release استفاده میکنیم تا بتوانیم این مورد را برطرف کنیم. وقتی تست تمام شد و تیم تست تایید کرد، پابلیش می شود و خروجی میگیریم و این برنچ، روی برنچ Master مرج می شود (توسط مدیر تیم یا ارشد (pull-request))و در نهایت حذف می شود. برای نام گذاری این برنچ ها از این روش استفاده میکنیم. کلمه برنچ Releases به همراه / و سپس ورژن این ریلیز مثل
releases/v1.1
برنچ Master : این برنچ شامل کد نهایی که پابلیش شده و دست مردم است، می باشد. این برنچ شامل تاریخچه ریلیز های ما است. این برنچ همیشه در کنار برنچ Develop هست و پاک نمی شود. بعد از اینکه تیم تست تایید نهایی را روی برنچ Release دادند ما آن برنچ را روی Master مرج میکنم. ریلیز میدهیم و این ریلیز را تگ میکنیم و برنچ Release را پاک میکنیم. با تگ کردن، ما تاریخچه ریلیز هامون رو نگه میداریم و هر جا نیاز بود می توانیم به آن ها مراجعه کنیم. این برنچ پایه و اساس درخت گیت ما هستش و هرکسی نیاز به خروجی و ریلیز گرفتن داشت باید به این برنچ مراجعه کند و از این برنچ استفاده کند به این دلیل که این برنچ شامل پایدارترین حالت اپلیکیشن است و این برنچ چیزی است که در حال حاضر دست مردم قرار دارد.
برنچ های Hotfix : ما از این برنچ ها برای رفع سریع مشکلات موجود و دیده نشده درون ورژنی که ریلیز شده استفاده میکنیم. این برنچ ها از روی برنچ Master ساخته می شوند و باگ فیکس شده و به سرعت روی برنچ Master مرج می شود (pull-request) و همان برنچ ریلیز دوباره برای پابلیش شدن می رود. دقت کنیم وقتی hotfix انجام شد، یک درخواست روی برنچ Master ارسال میکند و یک درخواست روی برنچ Develop، اینجوری برنچ برنچ Develop هم اون تغییرات رو میگیره و اپدیت میشه و این باگ هم روی برنچ Develop رفع شده و هم روی برنچ Master. بعد برنچ Master با ورژن جدید تگ میشه و وقتی این باگ روی برنچ Develop مرج شد، این برنچ hotfix پاک می شود. برای نام گذاری از hotfixes به همراه / استفاده میکنیم. مثل
hotfixes/removing-currency-rate
چه جایگزین هایی هست؟
مواردی که گفته شد، شاید برای بعضی شرکت ها زیاد باشه، برای ما تئوریکال خیلی خوب بود. ولی وقتی در عمل وارد میشی شاید همه چیز اینقدر مرتب و رویایی نباشه، ما متوجه شدیم که نمیتونیم همیشه تمیز و با ادب جلو بریم، اینجا هستش که دستورات Merge و Rebase به کمکمون میاد و همچنین متوجه شدیم که این Flow که بش فکر میکنیم شاید پایبند بودن بش سخت باشه.
مهمترین نکته برای استفاده از این فلو، اینه که ما دو برنچ اصلی و مهم داریم که روی یکی فیچرهای تمام شده رو نگه میداریم و روی دیگیری، نسخه ای که دست مردم است در این حالت همیشه استیبل ترین حالت ما می شود نسخه ای که دست مردم است و همیشه از این برنچ که مستر است می توانیم خروجی بگیریم و نسخه ای که برای توسعه استفاده میکنیم برنچ Develop است.
قوانینی که رعایت میکنیم
استفاده از کاراکتر های خاص در کامیت ها برای اینکه به سرعت بفهمیم که این کامیت مربوط به چه چیزی بوده و چه کاری روش انجام شده. این ها کاملا قراردادی بوده و هرکسی برای خودش میتونه درست کنه. لیست کامل کاراکتر ها و اموجی ها رو میتونین اینجا ببینین
?tada: این فیچر تمام شده است
? :construction: این فیچر هنوز تمام نشده و پوش در آخر روز
? :lipstick: ظاهر کاربری این فیچر تکمیل شده است
? :pencil: کدهای این فیچر تکمیل شده است
? :bug: باگ برطرف شده است
? :wrench: نیاز به ریویو و ریفکتور دارد
✅ :white_check_mark: تست برای این فیچر تکمیل شد
یا استفاده از کلمات ساده مثل
done: این فیچر تمام شده است
construction: این فیچر هنوز تمام نشده و پوش در آخر روز
ui: ظاهر کاربری این فیچر تکمیل شده است
code: کدهای این فیچر تکمیل شده است
bug: باگ برطرف شده است
review: نیاز به ریویو و ریفکتور دارد
test: تست برای این فیچر تکمیل شد
اسم گذاری برنچ ها به این شکل انجام می شود
releases/{version}-{buildNumber(optional)} --> releases/v2.4.23-1023 hotfixes/{hotfix-title} --> hotfixes/removing-currency-rate bugs/{bug-title} --> bugs/api-repsonde-404-not-handled#4337 features/{sub-feature(optional)}/{feature-title} --> features/login-page/register features/{feature-title} --> features/firebase-page