اصطلاح Shotgun Surgery چیه و چطور میشه جلوشو گرفت!

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

مدیر محصول و یا مدیر غیرمحصول! (مدیری که اصلا نمیدونه محصول چیه ?) وارد اتاق میشه و میگه ما میخوایم فلان اتفاق توی سایت بیفته. برای مثال کاربر که میاد فلان فرم رو پر میکنه بر اساس اینکه چه چیزی پر کرده فلان پروسه رو طی کنه و فلان مراحل رو بره جلو (حالا قراره یه سری چیزاش هم تغییر کنه که لیستش و دیزاینش رو فلانی برات میفرسته!!!! ?)

این سناریو اگر برای ۱۰۰٪ شماها اتفاق نیفتاده احتمال خیلی زیاد برای ۹۹.۹۹۹۹۹۹۹۹۹٪ تون اتفاق افتاده. همچین که خودتو به نشدین زدی و چند بار با حالت پیمان معادی توی فیلم "ابد و یک روز" مستقیم و خیره نگاهش میکنی بالاخره مقاومت رو میزاری کنار و میگی اوکی باشه،‌ این تغییرات رو میدم ولی خیلی چیزا عوض میشه و یه سری چیزا بهم میریزه!!!

جوری که به مدیر محصول نگاه میکنی!
جوری که به مدیر محصول نگاه میکنی!


ولی خودتم میدونی مقاومت بی‌فایده است! همچین که قانع شدی که اینا برای بیزنس مهمه و مشتریا همچین چیزی خواستن کم کم ازت خداحافظی میکنه و میگه دمت گرم! ?

حالا شروع میکنی به گشتن توی کد و همونطور که چایی رو هورت میکشی به این فکر میکنی که اگر فلان تغییر رو توی فلان قسمت از کد بدی نزدیک به ۴۰ تا جای دیگه هم تغییر میکنه که هیچ. تازه دیزاین دیتابیست رو هم باید تغییر بدی البته اون ۴۰ تا جا خیلی تغییرات اساسی نداره‌ها ولی خب کلی سوراخ سمبه هست که باید چک کنی ?. همچین که به خودت و پروژه بد و بیراه میگی اعصابت خرده چرا پارسال که مدیرت اوکی داده بود بعدا پروژه راه افتاد براش وقت بزار و تست بنویس چرا هنوز وقتش نشده که هیچ انگار این پروژه هرگز زنگ تفریح نداشته. فیچر پشت فیچر، نیاز مارکتینگی پشت نیاز مارکتینگی! پس ما کی این بدهی‌های فنی رو بدیم!!!

حقیقت اینه که این اتفاق اجتناب ناپذیره. هر کاری کنی اون روز میاد و اون شتر دم خونه ات میخوابه اما جالبه بدونی که یه اصطلاحی برای این قضیه داریم به اسم Shotgun Surgery.

داستان از این قراره که وقتی با یه تفنگ شاتگان تیر بخوری (خدای ناکرده!) یه گلوله به همراه مقداری زیادی ترکش توی بدنت فرو میره و دراوردن اون ترکش‌ها مکافاته ☠️.

حالا ربطش به کد اینه که گاهی برای اینکه یه تغییر بدی مجبوری صدجای دیگه رو هم تغییرهای ریز بدی که ممکنه کلی ازت وقت بگیره و از طرفی ممکنه تضمینی نباشه که دوباره اتفاق نیفته!

برای جلوگیری از این کار قطعا خیلی کارها باید انجام داد و طبیعتا یه مقاله ساده ممکن نیست این قضیه رو به طور کامل شرح بده ولی طبیعتا برای اینکه تعداد اون ترکش‌ها کمتر بشه یه نکته رو توی این مطلب توضیح میدم و اونم رعایت اصل Single Responsibility Principle هستش.

طبق تعریفی که آنکل باب توی کتاب خودش آورده یه همچین چیزی رو میگه:

"A class should have one, and only one, reason to change."

شاید پیش خودتون بگید یه زبونی مثل Go که چیزی مثل Class نداره آیا این قضیه در موردش صدق میکنه یا نه؟‌ باید بگم که آره. صدق میکنه SRP میتونه در سطح Struct, Function, Interface و حتی Package تعریف بشه.

برای مثال فکر کنید یه استراکتی به اسم Calculator داریم که وظیفه اش محاسبه یه سری موارد هستش و در نهایت میاد و این دیتاها رو به خروجی میفرسته.

طبیعتا خیلی سریع متوجه میشید که این کد فوق العاده ناجوره دلیل اصلیش هم همینه که داره دو تا کار میکنه. اول محاسبه یه سری چیزها و دوم ذخیره‌اش توی فایل متنی. ممکنه نیاز باشه در آینده به یه دیتابیس نوشته بشه یا به یه فایل اکسل یا ایمیل بشه یا پیامک بشه (بله من این کار رو قبلا انجام دادم!! ?) در کل این پکیج باید تنها و تنها یه کاری رو انجام بده و اونم محاسبه فاکتور هستش. پس به این صورت تغییرش میدیم:

خب حالا یه متد جداگانه به اسم Total داره مقدار رو حساب میکنه و همین تا حدودی باعث میشه کد تمیزتر بشه برای مثال اگر قرار باشه ۹٪ مالیات بر ارزش افزوده حساب بشه میشه توی این متد اضافه اش کرد. (این اضافه کردن ۹٪ خودش کلی ماجرا داره ولی علی الحساب میشه همینجا یه مقدار ۹٪ بهش اضافه کرد)

فرض کنیم این پکیج قراره که دیتا رو سیو هم بکنه ولی از طرفی هم نمیخوایم در آینده shotgun surgery برامون پیش بیاد و اگر قراره اون مکانیزم ذخیره تغییر کنه ما اذیت شیم پس میایم و یه تغییر جدید میدیم:

همونطور که میبینید یه interface اضافه کردیم و این یه قرارداد، قاعده و قانون ? کلی ایجاد میکنه که اگر به هر چیزی که بخواید مقدار total رو سیو بکنید میتونید براش یه saver بنویسید و اون میتونه دیتابیس باشه یا پیامک یا هر چیز دیگه ای. برای مثال:

یه نکته ای که در مورد interfaceها بسیار مهمه اینه که تا حد ممکن متدهای interface کم و به اندازه باشه و از نوشتن fat interface خودداری کنید چون خودش باعث بروز مشکلاتی مثل زیرپا گذاشتن اصل liskov substitution principle میشه که در آینده بهش میپردازیم.



منابع:‌