هدف هر فرایند توسعه فقط این است: دولوپر بتواند کد خوب بنویسد و دیپلوی کند. اگر فرایندی این را ممکن نمیکند — یا بدتر، مانع میشود — بکشیدش. ارزیابی همهچیز با همین جمله است؛ بقیه تئاتر مدیریت است.
در فصلهای قبل از جلسات و flow state و محیطی گفتیم که تیم در آن احساس امنیت کند. حالا نوبت خودِ کد است: نسخه، شاخه، Pull Request، استاندارد، دیپلوی. مت استافر در Tighten صدها ریپو را با همین قواعد جلو برده؛ صریح است و تعارف ندارد.
ضدالگوهایی که وانمود میکنند کمکاند
بیشتر فرایندهایی که «برای بهرهوری» فروخته میشوند، در واقع میکرومدیریت بیزنس را به قیمت تولید واقعی میخرند:
- استندآپ بیشازحد — حتی استندآپ روزانه
- جلسهٔ برنامهریزی ۲۰ نفره برای یک فیچر
- اجبار به وصل کردن هر کامیت به تیکت Jira
- ثبتساعت افراطی و ردیابی دقیق زمان
جایگزین Tighten ساده است: افراد و تیمهای کوچک، تکههای کوچک کد، تست، دیپلوی، ارسال به production، تکرار. همین حلقه، ارزش را به کاربر میرساند. هر چیز دیگری باید در خدمت همین حلقه باشد.
Git — حتی اگر تنها کار میکنی
اگر هنوز Git نداری، با دیدن فرقش مات میشوی — نه فقط در تیم بزرگ؛ حتی تنهایی. Git امروز نماد version control است چون منعطفترین، تواناترین و پشتیبانیشدهترین سیستم موجود است. دلیلی برای چیز دیگری نیست و مقایسهای هم در کار نیست.
سیستمهای قدیمیتر هرچه کار بیشتر میشد سنگینتر میشدند؛ اگر اتصال به سرور مرکزی قطع میشد گیر میکردی چون کپی محلی نداشتی. Git کپی کامل را روی ماشین خودت دارد، سبک است و فقط diff را نگه میدارد. مت میگوید امروز نوشتن کد بدون Git را تصور هم نمیکند.
میزبان: فقط GitHub
برای ابزار مدرن باید یک میزبان متمرکز انتخاب کنی. مت تعارف را کنار میگذارد: **تنها انتخاب معقول GitHub است.**
Bitbucket رقیب بزرگ است و مال Atlassian — همان سازندهٔ Jira. به باور مت تنها دلیل استفاده از Bitbucket همین وابستگی به Jira است، که خودش مسئله است. جدا از Jira هم Bitbucket را «آشغال محض» میداند: دست نزن.
GitLab چیزهایی برای عرضه دارد، اما در تجربهٔ کاربری و developer experience برای تیم مولد مدرن به GitHub نزدیک هم نمیشود. چند دلاری که با ابزار نصفه ذخیره میکنی، در کار اضافه میسوزد.
شاهد دمدست: گردش Pull Request. GitHub همان Pull Request مدرنی را اختراع کرد که امروز همه میشناسند. قبلش مفهومی بود به نام request pull — دستی، و هیچ شباهتی به چیزی که حالا میبینی نداشت. GitLab و Bitbucket در بهترین حالت تقلید ضعیفی کردهاند؛ GitHub سالبهسال همان تجربه را تراشیده.
شاخهبندی Tighten: سادهتر از Git Flow و GitHub Flow
اولین سؤال تیمها معمولاً استراتژی شاخه است. Git Flow هست، GitHub Flow هست، و دهها مدل دیگر. چیزی که Tighten حداقل در صدها کدبیس استفاده کرده این است:
- **main** هویت اپ است — همان چیزی که با CD خودکار به production میرود.
- **develop** (یا staging؛ اسمش را تیم انتخاب میکند) محل کار فعال است؛ ممکن است تند و شکننده باشد. خودکار به staging میرود.
- از develop شاخهٔ **فیچر** میزنی: یک فیچر، عمرش روز تا حداکثر چند هفته. Staging جدا برای هر فیچر اختیاری است؛ یک staging روی develop هم کافی است.
- فیچر را به develop مرج میکنی؛ مرج develop به main یعنی رفتن از staging به prod.
- **hotfix** از main میآید. درستش میکنی، به main برمیگردانی، بعد به develop — یا با مرج همان شاخه به هر دو، یا با مرج hotfix به main و بعد بالا آوردن main به develop.
شبیه GitHub Flow است ولی کمی سادهتر؛ و GitHub Flow خودش از Git Flow سادهتر است. بیشتر تیمها به پیچیدگی هیچکدام نیاز ندارند. این پیشفرض محکمی است؛ اگر پروژهٔ خاصی چیزی کم داشت، همان پروژه را عوض کن — نه اینکه از روز اول Git Flow را به کل سازمان تحمیل کنی.
کامیت: یک کار معنادار، وجه امری
شرکتهای کمی کامیت خوب را جدی میگیرند؛ آنهایی هم که جدی میگیرند معمولاً مجبورت میکنند کامیت را به تیکت Jira وصل کنی — که مت طرفدارش نیست. کامیت خوب بعداً کدبیس را قابلفهم میکند.
یک کامیت خوب یک تکه کار تمامشده است. پیامش تقریباً دستور است: چه میکند. کامیتهای پیشفرض خود Git وجه امری دارند — «I did this» نیستند؛ «do that thing»اند. تاریخچه باید مثل فهرست دستورالعمل خوانده شود: این را بکن، بعد آن را، بعد آن را.
نمونههای بد و خوب:
# بد — آیندهات (یا خریدار محصولت) چیزی از تاریخچه نمیفهمد
WIP
fixed stuff
updates
I added a rate column# خوب — وجه امری، یک کار معنادار
Add rate column to dashboard
Allow users to log in with GitHub OAuth
Fix rounding on invoice taxجنبشی هست که همهٔ کامیتها را WIP میگذارد. تا وقتی هیچکس نخواهد بفهمد چرا یا کی چیزی عوض شد، مشکلی نیست. آن آدم آینده ممکن است خودت باشی، همکارت، یا کسی که میخواهد ابزار را بخرد. حتی اگر امروز به تاریخچه نیاز نداری، برای آن روز بنویس. مت خودش گهگاه WIP نوشته؛ باز هم میگوید ارزشش را دارد که وقت بگذاری و پیام درست بنویسی.
اندازه: آنقدر بزرگ که یک تغییر معنادار در رفتار کد باشد — نه دو خطی که بدون بقیه هیچ نمیکنند. آنقدر کوچک که هیچوقت نخواهی فقط بخشی از همان کامیت را rollback کنی. تست هر کامیت لازم نیست سبز باشد؛ ولی کامیت باید یک واحد معنادار باشد. Git اجازهٔ rollback جزئی میدهد؛ این فقط خطکش درونی مت است.
مدرسهقدیمیها گاهی یک فیچر کامل را یک کامیت میکنند. اگر آنطور کار میکنی، باشد. مت ترجیح میدهد کامیتها تکههای متمایز یک کل باشند و Pull Request آن کل را جمع کند.
برای عمق بیشتر: مقالهٔ کریس بیمز، *How to Write a Git Commit Message*.
Pull Request و قانون four eyes
مفهوم request pull از قبل از Git بوده: «این تغییر را نگاه کن.» Pull Request این را به دستهٔ تغییرات قابلتوضیح، قابلبازبینی و قابلاعمال تبدیل کرد.
**هر کدی که شرکت مینویسد باید حداقل دو بار دیده شود قبل از زنده شدن.** مت به این میگوید four eyes: یا دو نفر pair programming میکنند، یا یکی مینویسد و یکی ریویو میکند. PR فقط برای دیگری نیست؛ خودت هم قبل از نهاییکردن، کد خودت را دوباره میخوانی.
PR خوب میگوید کد چیست و چه میکند — اغلب با اسکرینشات یا GIF. فضا میدهد که پیادهسازی قبل از live شدن نقد شود. پست وبلاگ Tighten با عنوان *Building our Pull Request* را مت حیاتی میداند.
قانون ادغام: هر Pull Request باید بدون شکستن اپ قابلمرج باشد. هر کامیت لزوماً اینطور نیست (هرچند ایدهآل است)؛ هر PR باید باشد: یک کار مشخص، قابل ارزیابی، قابل اعمال. در شاخهبندی درست، PR پایان یک شاخهٔ فیچر کوتاهعمر است — تا شاخهٔ طولانی از develop فاصله نگیرد و مرج روزبهروز دردناکتر نشود.
خطکش کلی مت: PR باید **روزها باز بماند نه هفتهها**، و **دهها کامیت داشته باشد نه صدها**. همیشه ممکن نیست؛ ولی هرچه بزرگتر و طولانیتر، مرج سختتر و نامحتملتر. اینها روی هم تلمبار میشوند تا دیگر هیچچیز دیپلوی نشود.
نمونهٔ قالب — کوتاه، what و why، تصویر، و اینکه بدون شکستن اپ قابلمرج است:
## What
ستون نرخ را به داشبورد ادمین اضافه میکند.
## Why
مشتری باید نرخ را بدون خروج از داشبورد ببیند؛ قبلاً فقط در گزارش شبانه بود.
## Screenshots / GIF
قبل و بعد، یا یک حلقهٔ کوتاه از جریان.
## Merge
- [ ] بدون این تغییر اپ نمیشکند
- [ ] تست مرتبط سبز است
- [ ] شاخه از develop جلوتر نرفته؛ rebase تازه استاستاندارد کدنویسی از روز اول — کشتن bikeshedding
قبل از ابزار دیپلوی، استاندارد مهندسی را انتخاب کن. وگرنه گرفتار bikeshedding میشوی: وقتکشی سر جای براکت، تب در برابر فاصله، معماری موردعلاقه، پکیج A بهجای B — بهجای ساختن چیزی که کاربر میخواهد.
حداقل هر تیم باید استاندارد زبان بکاند، فریمورک، و فریمورک اصلی جاوااسکریپت (اگر دارد) را داشته باشد.
برای تیم Laravel: **Pint** از جعبه کافی است. در Tighten ابزاری دارند به نام **Duster** که Pint و چند ابزار دیگر را با هم یک mega-linter میکند. پیشفرضش استاندارد سالهای Tighten است؛ با فایل کانفیگ Duster میتوانی برای تیم یا همان ریپو عوضش کنی.
وقتی استاندارد انتخاب شد، دولوپر یاد میگیرد، محلی مینویسد، و در stack محلی و ریموت قفلش میکند. نباید سینیور در Pull Request وقتش را صرف lint کند. CI/CD هر لغزشی را قبل از چشم انسان میگیرد.
SaaS مشترک: دیپلوی خودکار، staging شبیه prod
هل دادن به staging و prod باید تا حد ممکن آسان باشد — ترجیحاً فقط با کامیت به شاخهٔ مشخص. سرور staging را تا جای ممکن شبیه prod بساز و دیپلوی هر دو را خودکار کن. یادت باشد: main با prod جفت است، develop با staging.
Tighten از ابزارهای Laravel استفاده میکند: **Forge** و **Envoyer** و حالا **Cloud**. هر ابزاری که دوست داری، بهشرطی که دیپلوی دستی نخواهد. از push-to-deploy دستی تا جایی که میتوانی فرار کن.
production را اگر ممکن است **حداقل هفتهای یکبار** دیپلوی کن. هرچه فاصلهٔ دیپلویها بیشتر، هزینه و ریسک هر بار بیشتر.
دو سرویس سومشخص دیگر روی staging و prod:
1. **ردیاب خطا** — نوتیف را به یک کانال مشترک Slack بفرست. همه ببینند. روی staging **و** prod نصب کن. 2. **صندوق نامهٔ متمرکز** — ایمیل یکی از سختترین چیزها برای تست روی staging است. **Mailtrap** ایمیل staging را در یک کلاینت واقعیشکل نشان میدهد. غیر فنیها و مشتری هم میتوانند پیشنمایش ببینند.
paper cut: زخمهای کوچک که تیم را میکُشند
paper cut یعنی آزار جزئی که تکی بد نیست، ولی جمع اثرش روی سرعت و خوشحالی دولوپر خیلی بزرگ است.
**تست کند.** اگر سوئیت محلی بیشتر از یک دقیقه طول بکشد، اجرا نمیشود. ایدهآل این است که بعد از هر تغییر معنادار تست بدوند. حتی اگر روزمره زیرمجموعه میدوند، با هر پوش باید سوئیت کامل سبز شود. اگر هشت دقیقه طول بکشد، هشت دقیقه از دست نرفته — **پانزده تا بیست دقیقه** طول میکشد تا دوباره به جریان کار برگردند. یا حواسپرت میشوند یا تست را کمتر میدوند؛ هر دو فاجعه است. در CI/CD هم paper cut است: آدم معمولاً جلوی CI نمینشیند، ولی وقتی باید قبل از آماده شدن PR یا قبل از زنده شدن باگ فوری صبر کند، یک ساعت انتظار قابل قبول نیست.
**PR و شاخهٔ طولانی.** قبلاً گفتیم چرا مرج سریع مهم است؛ اینجا فقط این را اضافه کن که شاخه و PR بلندعمر از رایجترین paper cutهاست و ساعتها را در مقیاس هفته میسوزاند.
**وابستگی تاریخگذشته.** بعداً دربارهٔ بدهی فنی بیشتر میگوییم. اینجا همین بس که dependency کهنه فقط امنیت نیست: به فیچر جدید دسترسی نداری، همیشه باید بفهمی سند کدام نسخه است، بین نسخهٔ خودت و سیستم دیگر پل مینویسی، و اگر چیزی را میخواهی که «در راه است» شیم موقت میسازی — که هم ساختنش هزینه است هم یادت ماندن برای حذفش.
AI: برای کار تکراری، نه بهجای دولوپر
مت عمداً بخش طولانی دربارهٔ AI ضبط نمیکند: تا ویدیو منتشر شود همهچیز عوض شده. آنچه پایدار است این است:
AI برای کار rote خوب است؛ برای کار سطح جونیور خوب است. اگر میتوانی دستور صریح بدهی و خروجی را ریویو کنی، مثل یک جونیور مفید است. مت برای قالبها و کارهایی که بلد است ولی حوصلهٔ تایپ ندارد از آن استفاده میکند.
جایگزین فکر خلاق توسعه نیست. مخصوصاً به تو که تیم را رهبری میکنی: **نمیتوانی دولوپر را با AI عوض کنی.** شاید سود یک جونیور را بگیری؛ شاید وقت دیگران را کمتر تلف کنی؛ نیاز به آدم واقعی از بین نمیرود.
و هزینهٔ محیطی را صنعت تا حالا شانهخالی کرده؛ تا ابد نمیشود. مثل هر ابزار: مفید است، هزینه دارد — در گردش کار و در جهان. وقت استفاده را باید انتخاب کنی.
جزئیات استک و تیم و صنعت تو فرق میکند. این درس خط پایه است: محیط و فرایندی بچین که کدنویس بتواند کد عالی بنویسد و بفرستد. اگر چیزی در این راه نیست، مال تو نیست.
به زبان ساده
هر فرایندی که نوشتن و دیپلوی کد خوب را سخت کند، دور ریختنی است. GitHub، شاخهٔ کوتاه، چهارچشم، CI/CD.
مثال واقعی
main → پروداکشن، develop → استیجینگ، هاتفیکس از main. Duster استایل را میگیرد. تست محلی بالای یک دقیقه یعنی دیگر اجرا نمیشود. هوش مصنوعی جای دولوپر را نمیگیرد.