تصور کنید بعد از چند ماه به سراغ یکی از پروژههای قدیمی خود میروید تا باگی را برطرف کنید، یا به یک تیم نرمافزاری جدید ملحق میشوید و میخواهید بفهمید همتیمیهای شما در هفتههای گذشته چه تغییراتی روی کدهای اصلی اعمال کردهاند.
تاریخچه گیت را باز میکنید و با دهها پیام شبیه به "fix" ،"update" ،"حل شد" یا حتی "دست نزن درست کار میکنه!" مواجه میشوید. در این لحظه، فهمیدن اینکه هر تغییر دقیقاً با چه هدفی انجام شده، شبیه به رمزگشایی یک کتیبه باستانی خواهد بود.
واقعیت این است که نوشتن پیام کامیت (Commit Message) مبهم و نامفهوم، یکی از اصلیترین عوامل اتلاف وقت در تیمهای فنی است.
پیامهای کامیت، شناسنامه و مستندات زنده کدهای شما هستند. در این درس یاد میگیرید که چطور مانند توسعهدهندگان بزرگترین شرکتهای نرمافزاری دنیا، از استاندارد بینالمللی Conventional Commits استفاده کنید تا تاریخچه پروژهتان آنقدر شفاف، خوانا و منظم باشد که هر کسی با یک نگاه، دقیقاً متوجه ساختار و دلیل تغییرات بشود.
چرا پیام کامیت مبهم روند توسعه تیم را مختل میکند؟
وقتی در یک پروژه تیمی یا حتی یک پروژه شخصی بزرگ کار میکنید، کدهایی که مینویسید تنها بخشی از مسئولیت شما به عنوان یک توسعهدهنده است؛ بخش دیگر، توانایی برقراری ارتباط و انتقال دلیل نوشتن آن کدهاست. نوشتن پیامهای مبهمی مانند "fix" ،"changes" یا "update" شاید در لحظه کامیت کردن چند ثانیه در زمان شما صرفهجویی کند، اما در آینده ساعتها وقت تیم را تلف خواهد کرد.
دلایل اصلی که نشان میدهند چرا پیامهای مبهم روند توسعه را مختل میکنند، به شرح زیر است:
۱. تبدیل فرآیند عیبیابی (Debugging) به یک کابوس
در دنیای نرمافزار، بروز خطا و باگ یک امر ناگزیر است. وقتی مشکلی در محیط عملیاتی (Production) رخ میدهد، اولین کار توسعهدهندگان، بررسی تاریخچه کامیتها (Git Log) برای پیدا کردن منشأ خطاست. اگر پیامها مبهم باشند، تیم نمیتواند بفهمد که کدام کامیت، کدام بخش از سیستم را تغییر داده است. در این حالت، برنامهنویسان مجبور میشوند تکتک کامیتها را باز کرده و خط به خط تغییرات کد (Git Diff) را بخوانند تا متوجه شوند در آن لحظه چه اتفاقی افتاده است.
۲. اختلال و فرسودگی در جلسات بررسی کد (Code Review)
قبل از اینکه کدهای شما با شاخه اصلی پروژه ادغام شوند، توسط اعضای ارشد تیم بررسی میشوند. یک پیام کامیت مبهم، هیچ پیشزمینهای به بررسیکننده (Reviewer) نمیدهد. او مجبور است زمان بسیار بیشتری را صرف حدس زدن اهداف شما از تغییرات کند. پیام کامیت شفاف، مانند یک نقشه راهنما عمل میکند و به بررسیکننده میگوید که دقیقاً باید به دنبال چه چیزی باشد.
۳. از بین رفتن مستندات زنده پروژه
کدها تغییر میکنند، جابهجا میشوند یا بازنویسی میشوند، اما تاریخچه کامیتها همیشه ثابت باقی میماند. پیامهای کامیت استاندارد، در واقع مستندات زنده پروژه هستند که تاریخچه تکامل نرمافزار را روایت میکنند. وقتی پیامها بیمعنی باشند، این مستندات ارزشمند از بین میروند و بعد از گذشت چند ماه، حتی خود شما هم به یاد نخواهید آورد که چرا یک بخش خاص از کد را به آن شکل نوشتهاید.
۴. غیرممکن شدن اتوماسیون و ساخت گزارش تغییرات (Changelog)
در تیمهای مدرن، از پیامهای کامیت برای تولید خودکار فایل گزارش تغییرات (CHANGELOG.md) و مدیریت نسخههای نرمافزار (Semantic Versioning) استفاده میشود. ابزارهای خودکار، پیامهای کامیت را اسکن میکنند تا بفهمند چه ویژگیهای جدیدی اضافه شده یا چه باگهایی برطرف شده است. وجود پیامهای مبهم، امکان استفاده از این ابزارهای کارآمد و اتوماتیک را کاملاً از بین میبرد.
آشنایی با استاندارد جهانی Conventional Commits
برای پایان دادن به آشفتگی در تاریخچه پروژه و ایجاد یک زبان مشترک بین توسعهدهندگان، یک استاندارد جهانی به نام Conventional Commits یا «کامیتهای مرسوم» پایهگذاری شد. این استاندارد در واقع یک قرارداد ساده اما بسیار قدرتمند برای نوشتن پیامهای کامیت است که یک ساختار مشخص و قانونمند به متن پیامها میدهد.
وقتی تمام اعضای تیم از این استاندارد پیروی میکنند، تاریخچه گیت پروژه آنقدر منظم میشود که ماشینها و ابزارهای خودکار نیز میتوانند آن را بخوانند و بر اساس آن عمل کنند.
ساختار اصلی یک کامیت مسج استاندارد
بر اساس این استاندارد، هر پیام کامیت از یک ساختار مشخص پیروی میکند که شامل یک بخش اجباری (عنوان) و دو بخش اختیاری (بدنه و پانویس) است. الگوی کلی آن به این صورت تعریف میشود:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
این ساختار از سه جزء اصلی تشکیل شده است:
۱. نوع کامیت (Type)
اولین و مهمترین کلمهای است که در پیام کامیت نوشته میشود. این کلمه مشخص میکند که ماهیت این تغییر چیست. آیا یک قابلیت جدید اضافه شده؟ یک باگ برطرف شده؟ یا فقط کدهای قبلی تمیزتر شدهاند؟ (در بخش بعدی تمام این تگها را بررسی میکنیم). این بخش کاملاً اجباری است.
۲. محدوده کامیت (Scope)
یک بخش اختیاری است که در داخل پرانتز قرار میگیرد و نشان میدهد تغییرات دقیقاً در کدام بخش یا ماژول از پروژه رخ داده است. برای مثال، اگر سیستم پرداخت را تغییر دادهاید، محدوده آن را مشخص میکنید:
feat(payment): add zarinpal gateway
۳. توصیف (Description)
یک خلاصه کوتاه، دقیق و مستقیم از تغییری است که ایجاد کردهاید. این بخش بلافاصله بعد از علامت دو نقطه (:) قرار میگیرد و با یک فاصله (Space) شروع میشود. در این بخش نباید جزییات فنی طولانی بنویسید، بلکه فقط باید بگویید چه کاری انجام شده است.
بخشهای تکمیلی (اختیاری)
برای تغییرات بزرگ و پیچیده، عنوان به تنهایی کافی نیست. به همین دلیل دو بخش دیگر نیز در این استاندارد پیشبینی شده است:
- بدنه (Body): با یک خط فاصله از عنوان جدا میشود. در این بخش میتوانید جزییات بیشتری بنویسید؛ مثلاً اینکه چرا این تغییر لازم بود، راه حل قبلی چه مشکلی داشت و تغییرات فعلی چه تاثیری روی بقیه سیستم میگذارند.
- پانویس (Footer): این بخش هم با یک خط فاصله از بدنه جدا میشود و معمولاً برای ارجاع به کارهای جانبی استفاده میشود. مثلاً اگر این کامیت باعث بسته شدن یک تیکت یا ایشو (Issue) در گیتهاب یا جیرا میشود، شناسه آن را در پانویس مینویسند: Closes #42.
آموزش تگهای اصلی و پرکاربرد
ستون اصلی استاندارد Conventional Commits، تگهایی هستند که در ابتدای پیام قرار میگیرند. این تگها به اعضای تیم و ابزارهای خودکار میگویند که دقیقاً چه نوع تغییری در مخزن پروژه رخ داده است. استفاده درست و دقیق از این تگها، تفاوت میان یک توسعهدهنده تازهکار و یک مهندس نرمافزار حرفهای را مشخص میکند.
در ادامه، تگهای اصلی و پرکاربرد این استاندارد را همراه با کاربرد دقیق و مثالهای واقعی بررسی میکنیم.
تگهای کلیدی و پرکاربرد (اصلی)
این دو تگ مستقیماً با منطق و قابلیتهای برنامه در ارتباط هستند و بیشترین استفاده را در طول روز دارند:
- feat (ویژگی جدید): زمانی از این تگ استفاده میشود که یک قابلیت، کامپوننت یا ابزار کاملاً جدید به پروژه اضافه شده باشد. این تگ مستقیماً باعث ارتقای نسخه فرعی نرمافزار (Minor Version) میشود.
feat(auth): add google oauth2 login support - fix (رفع خطا): هر زمان که یک باگ، خطا، کرش یا رفتار اشتباه را در کدهای پروژه اصلاح میکنید، باید از این تگ استفاده کنید. این تگ نسخه وصله (Patch Version) را بالا میبرد.
fix(cart): resolve total price calculation error on checkout
تگهای ساختاری و بهینهسازی (فنی)
این تگها تغییراتی را پوشش میدهند که تاثیری روی ویژگیهای ظاهری یا امکانات در دسترس کاربر ندارند، اما برای سلامت و کیفیت فنی پروژه حیاتی هستند:
- refactor (بازنویسی کد): این تگ مخصوص زمانی است که بخشی از کد را بازنویسی میکنید تا خواناتر، بهینهتر یا تمیزتر شود، بدون اینکه قابلیتی اضافه کنید یا باگی را برطرف سازید. در واقع منطق برنامه ثابت میماند اما کیفیت کد بالا میرود.
refactor(database): optimize user query execution time - style (تغییرات ظاهری و فرمت): این تگ برای تغییراتی است که هیچ تاثیری روی منطق و اجرای کد ندارند و صرفاً مربوط به ظاهر، فرمتنویسی، رعایت فاصلهها (White-spaces)، گذاشتن یا برداشتن سمیکولنها و مرتبسازی ساختار فایلها بر اساس استانداردهای کدنویسی (مثل PEP 8) هستند.
style: reformat main controller using black linter - test (تستها): هر زمان که تست جدیدی مینویسید، تستهای قدیمی را اصلاح میکنید یا ابزارهای تست واحد (Unit Test) و یکپارچهسازی را به پروژه اضافه میکنید، باید از این تگ استفاده کنید.
test(api): add integration tests for user registration endpoint
تگهای فرعی و نگهداری (جانبی)
این تگها مربوط به کارهای مدیریتی، مستندسازی و ابزارهای توسعه پروژه هستند:
- docs (مستندات): اگر تغییری در فایلهای راهنما (مثل README.md)، اسناد فنی پروژه، مستندات API یا حتی کامنتهای درون کد ایجاد کنید، از این تگ استفاده میشود.
docs: update API endpoints documentation for payment module - chore (کارهای روزمره و فرعی): این تگ برای تغییراتی است که به کدهای اصلی یا فایلهای تست ربطی ندارند، اما برای جلو بردن پروژه لازم هستند؛ کارهایی مثل آپدیت کردن پکیجها و کتابخانهها، تغییر فایلهای تنظیمات محیطی (مثل .gitignore) یا تنظیمات مربوط به ابزارهای ساخت پروژه.
chore: upgrade Django package to latest stable version
یک جمعبندی سریع برای انتخاب تگ درست
برای اینکه در طول روز به سرعت تگ مناسب را انتخاب کنید، این تصویر ذهنی را همراه داشته باشید:
| اگر تغییر شما... | تگ مناسب این است: |
| یک ابزار یا بخش جدید به کاربر میدهد | feat |
| یک مشکل یا رفتار اشتباه را درست میکند | fix |
| کد را بدون تغییر در خروجی تمیزتر میکند | refactor |
| فقط فاصلهها، ظاهر یا فرمت کد را اصلاح میکند | style |
| مستندات و فایلهای راهنما را آپدیت میکند | docs |
| تستهای سیستم را کم یا زیاد میکند | test |
| پکیجها یا فایلهای کانفیگ را دستکاری میکند | chore |
اصول نگارش و قوانین طلایی متن پیام
انتخاب تگ مناسب تنها نیمی از مسیر است؛ نیمه دیگر، شیوه نگارش متنی است که جلوی آن تگ مینویسید. حتی اگر دقیقترین تگ را انتخاب کنید، نوشتن یک عبارت گنگ یا طولانی در بخش توضیحات، ارزش کار را از بین میبرد. برای اینکه پیامهای کامیت شما کاملاً خوانا، دقیق و در سطح استانداردهای بینالمللی باشد، رعایت چند قانون طلایی در نگارش متن الزامی است.
۱. قانون ۵۰ کاراکتر برای خط اول (محدودیت طول عنوان)
خط اول پیام کامیت (Summary) باید مثل تیتر یک روزنامه، کوتاه و ضربتی باشد. حداکثر طول مجاز برای این خط ۵۰ کاراکتر است (هرچند در شرایط خاص تا ۷۲ کاراکتر هم تحمل میشود).
دلیل این محدودیت فنی است؛ ابزارهایی مثل گیتهاب، گیتلب و حتی دستور git log --oneline در ترمینال، عناوین طولانی را قطع میکنند و انتهای آن را به صورت سه نقطه (...) نشان میدهند. اگر نمیتوانید تغییر خود را در ۵۰ کاراکتر خلاصه کنید، یعنی یا پیام شما بیش از حد طولانی است یا کامیت بزرگی ثبت کردهاید که باید به چند کامیت کوچکتر تقسیم شود.
۲. استفاده از فعلهای امری و زمان حال (Imperative Mood)
در زبان انگلیسی، پیام کامیت باید به لحن امری (زمان حال) نوشته شود، نه زمان گذشته. یعنی باید بنویسید add به جای added یا fix به جای fixed.
یک فرمول طلایی برای سنجش درستی لحن پیام وجود دارد؛ متن کامیت شما باید بتواند این جمله را کامل کند:
"If applied, this commit will..." (اگر این کامیت اعمال شود، این تغییر را ایجاد میکند...)
- این کامیت چه کاری انجام میدهد؟ If applied, this commit will add user authentication layout (صحیح)
- این کامیت چه کاری انجام میدهد؟ If applied, this commit will added user authentication layout (غلط)
گیت یک ابزار مدیریت تغییرات است و هر کامیت، دستورالعملی برای تغییر دادن وضعیت پروژه است. به همین دلیل لحن امری بهترین انتخاب است.
۳. شروع با حروف کوچک و حذف نقطه در انتها
در انتهای خط اول پیام کامیت هرگز نباید نقطه (.) بگذارید. قرار ندادن نقطه باعث صرفهجویی در فضای محدود ۵۰ کاراکتری میشود و ظاهر تاریخچه را تمیزتر نگهمیدارد. همچنین بر اساس استاندارد Conventional Commits، توضیحات بعد از علامت دو نقطه (:) بهتر است با حروف کوچک انگلیسی شروع شوند، مگر اینکه کلمه اول یک نام خاص یا مخفف ابزار باشد.
۴. استفاده هوشمندانه از بخش بدنه (Body) برای جزییات پیچیده
اگر تغییری که ایجاد کردهاید پیچیده است و نیاز به توضیح دارد، هرگز آن را در خط اول جا ندهید. خط اول را خالی بگذارید (با زدن دو بار Enter) و در خط سوم، بخش بدنه را شروع کنید.
در بخش بدنه، طول هر خط نباید از ۷۲ کاراکتر بیشتر شود. یک اصل مهم را در بدنه رعایت کنید: به جای توضیح دادن «چه کدی» نوشتهاید، توضیح دهید «چرا» این کار را کردهاید. ماشینها و دستور git diff به خوبی نشان میدهند چه کدهایی کم یا زیاد شدهاند؛ وظیفه شما در پیام کامیت این است که منطق پشت این تصمیم، محدودیتها یا راهحلهای جایگزینی که رد شدهاند را مستند کنید.
fix(api): resolve timeout during large report generation
Upgraded the database cursor allocation to use server-side fetching
instead of loading all records into memory at once. This prevents
the gateway from closing the connection on intensive exports.
ابزارها و اتوماسیون کامیتنویسی
رعایت دستوری و دستی استانداردها در طول زمان و با شلوغ شدن فرآیند توسعه پروژه، معمولاً کمرنگ میشود. انسانها ممکن است فراموش کنند یا به دلیل خستگی، دوباره به نوشتن پیامهای مبهم روی بیاورند. برای حل این مشکل، ابزارها و سیستمهای اتوماسیون وارد عمل میشوند تا این فرآیند را ساده، سریع و غیرقابلدور زدن کنند.
در ادامه، ابزارهای کلیدی برای خودکارسازی و مدیریت پیامهای کامیت را بررسی میکنیم.
۱. ساخت هوشمند پیام با ابزار Commitizen
اگر حفظ کردن تگها و ساختار استاندارد برای شما یا اعضای تیم سخت است، ابزار Commitizen این مشکل را حل میکند. این ابزار به شما اجازه میدهد که به جای نوشتن مستقیم دستور git commit، از دستور git cz در ترمینال استفاده کنید.
پس از اجرای این دستور، یک منوی تعاملی در ترمینال باز میشود که گامبهگام سوالاتی را از شما میپرسد:
- نوع تغییر چیست؟ (انتخاب تگ از میان گزینهها)
- محدوده (Scope) این تغییر کجاست؟
- یک خلاصه کوتاه از تغییر بنویسید.
- آیا این کامیت یک شکست در سازگاری نسخههای قبلی (Breaking Change) دارد؟
در نهایت، این ابزار تمام پاسخهای شما را جمعآوری کرده و یک پیام کامیت کاملاً استاندارد و بدون نقص تحویل گیت میدهد.
۲. فیلتر کردن پیامها با استفاده از Git Hooks و Commitlint
جذابترین بخش اتوماسیون، مجبور کردن سیستم به پذیرش یا رد کامیتها بر اساس استانداردهاست. گیت ابزاری به نام Git Hooks دارد؛ اینها اسکریپتهایی هستند که در مراحل مختلف چرخه گیت (مثل قبل از ثبت کامیت، قبل از پوش کردن و...) به صورت خودکار اجرا میشوند.
با ترکیب ابزار Husky (که مدیریت هوکهای گیت را راحت میکند) و ابزار Commitlint، میتوانید یک سیستم نظارتی بسازید. وظیفه Commitlint این است که متن پیام کامیت شما را درست قبل از ثبت نهایی اسکن کند.
- اگر پیام با ساختار Conventional Commits همخوانی داشته باشد، اجازه ثبت داده میشود.
- اگر کاربر پیامی مثل "fix" یا "up" بنویسد، ابزار جلوی عملیات کامیت را میگیرد، خطای ساختاری را در ترمینال چاپ میکند و تا زمانی که پیام اصلاح نشود، اجازه ثبت هیچ کدی را نمیدهد.
۳. تولید خودکار گزارش تغییرات (Changelog Automation)
وقتی تمام پیامهای کامیت پروژه با ساختار تگهای استاندارد ثبت شده باشند، میتوانید از ابزارهایی مثل Standard Version یا Semantic Release استفاده کنید.
این ابزارها با یک دستور، کل تاریخچه کامیتهای شما را بررسی میکنند و کارهایی را انجام میدهند که انجام دستی آنها ساعتها وقت میگیرد:
- تشخیص میدهند که با توجه به تعداد تگهای feat یا fix، نسخه بعدی نرمافزار باید چقدر ارتقا پیدا کند (مثلاً از نسخه 1.2.0 به 1.3.0 برود).
- یک فایل متنی به نام CHANGELOG.md در ریشه پروژه میسازند یا فایل قبلی را آپدیت میکنند. این فایل شامل یک لیست بسیار تمیز و دستهبندیشده از تمام ویژگیهای جدید و باگهای برطرفشده در نسخه جدید است که مستقیماً از روی پیامهای کامیت شما استخراج شده است.