ویرگول
ورودثبت نام
Mehrdad Esmaeilpour
Mehrdad Esmaeilpour
خواندن ۳۶ دقیقه·۱ سال پیش

بدهی فنی؛ از استعاره تا واقعیت

From Unleash
From Unleash


از استعاره

بدهی فنی رو اولین بار وارد کانینگهام (Ward Cunningham) برای قانع کردن کارفرمایی که تو سیستم مالی داشت به عنوان استعاره‌ای برای دلیل نوسازی کردن نرم‌افزار مطرح کرد. «با قرض کردن می‌شه کارها رو سریع‌تر جلو برد اما تا زمانی که بدهیت رو صاف نکنی داری نرخ بهره‌اش رو هم می‌پردازی» با نوسازی کردن نرم‌افزار در مسیر توسعه ما این بدهی رو پرداخت می‌کنیم. بدهی‌ای که بخاطر سرعت بالا و دانش کم اولیه‌مون ایجاد شده.

حل بدهی فنی می‌تونه بهونه‌ای برای نوشتن نرم‌افزار بد باشه. نرم‌افزار باید درک فعلی ما از مساله رو نشون بده. این درک می‌تونه با گذر زمان، اضافه شدن فیچر‌های جدید و … تغییر بکنه. پرداخت بدهی فنی به معنی نوسازی (Refactor) نرم‌افزار موجود برای بازتاب درک فعلی‌مون از مساله‌است.

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

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

بدهی و آن دیگر چیزها

بدهی فنی بحث کیفیت رو در مورد کد که برای استفاده کننده نامرئیه مطرح می‌کنه. معمولا تیم‌ها برای به وجود آوردن بدهی فنی بهانه‌های مختلفی دارن

  • وقت نداریم
  • مدیر محصول، فنی نمی‌فهمه
  • برنامه‌نویس قبلی چیزی بلد نبود
  • بچه‌های UX توقع بی‌جا دارن

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

قانون کانوی (Conway's Law) می‌گه که ساختار و فرهنگ سازمان روی نرم‌افزاری که تولید می‌شه اثرگذاره. «با نحوه چینش تیم بعضی از تصمیم‌های نرم‌افزاری خود‌به‌خود گرفته می‌شن که شبیه نحوه چینش تیم باشن». اگه یه سیستم رو طراحی کنی اما ساختار سازمان رو طراحی نکنی در واقع طراحی سیستم رو درست انجام ندادی.


پیچیدگی ضروری و غیرضروری

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

پیچیدگی ضروری و غیرضروری
پیچیدگی ضروری و غیرضروری

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

پس باز هم سراغ استعاره مالی می‌ریم. بدهی با بهره رو چطور پس می‌دن؟ به مرور!

یکی از روش‌های تشخیص بدهی فنی، نرخ‌ تغییرات و تعداد افرادی هست که روی یه بخش مشخص از کد کار کردن. نرخ تغییرات زیاد معمولا نشون دهنده اینه که یه بخشی از کد مدام باید عوض می‌شده (احتمالا درک‌مون از مساله کامل نبوده). با پیدا کردن این بخش‌های نرم‌افزار در زمان هر تغییر می‌شه بخشی از بدهی فنی اون بخش رو کاهش داد. پرداخت بدهی فنی زمان‌بره اما به استعاره دقت کنیم اگه بهره رو به مرور پرداخت نکنیم در نهایت ورشکست می‌شیم. ورشکستگی تو یه نرم‌افزار با بدهی فنی به این معنیه که دیگه نمیشه به جایی از نرم‌افزار دست زد. پرداخت مداوم بدهی فنی یکی از نشونه‌های Code Stewardship ه که می‌تونه به عنوان فرهنگ جاری تیم بین اعضا رایج بشه.

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

نادیده گرفتن بدهی فنی رو نباید با رها کردن کیفیت اشتباه بگیریم. بدهی فنی وقتی بزرگ بشه روی بهره‌وری تیم و انگیزه‌ اعضاش تاثیر منفی می‌ذاره و منجر به یه حلقه خطرناک می‌شه: «بدهی فنی زیاد بهره‌وری و روحیه تیم رو کاهش می‌ده و همزمان بهره‌وری پایین باعث می‌شه مدیریت فیچر‌های بیشتری رو وارد کار کنه و حل بدهی‌های فنی رو به بعد موکول کنه، این کار باعث بیشتر شدن بدهی فنی می‌شه»


مدیریت، جلوگیری و بازپرداخت بدهی فنی (TS;R)

مدیریت بدهی فنی

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

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

جلوگیری از بدهی فنی

یک روش جلوگیری از بدهی فنی افزایش آگاهی به وجودش تو تیم‌هاست. تیم‌های توسعه باید بدهی‌های فنی نرم‌افزارشون رو بشناسن، جنبه‌های مختلفش رو بررسی کنن و با اثرش روی نرم‌افزار آشنا باشن. بررسی خودکار کیفیت کد، رعایت اصول کد تمیز، Design Smell ها و روش‌های نوسازی باید براشون روتین باشه. برای افزایش آگاهی میشه از دوره‌ها، کنفرانس‌ها و تمرین‌های متمرکز کمک گرفت.

نمونه‌های عملیاتی جلوگیری از زیاد شدن بدهی فنی:

  • ریویو کد
  • ریویو طراحی و معماری
  • ریو تست‌ها

بازپرداخت بدهی فنی

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

۱. شناسایی، مستندسازی و دنبال کردن بدهی

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

۲. اولویت‌بندی بخش‌های بد

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

۳. شکستن بدهی‌ها به بخش‌های کوچک قابل انجام در هر دوره

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

۴. ایجاد انگیزه برای نگهداشت کیفیت

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

۵. پیروی از قانون پیش‌آهنگی

قانون پیش‌آهنگی می‌گه «همیشه زمینی که واردش می‌شی رو تمیزتر از وقتی که واردش شدی ترک کن». تو دنیای نرم‌افزار خوبه که از قانون «کدی که داری روش کار می‌کنی رو تو وضعیت بهتری که تحویل گرفتی، تحویل بده» پیروی بشه. پیروی از این قانون بازپرداخت بدهی فنی رو به روتین تیم تبدیل می‌کنه.

۶. به دنبال موقعیت برای بازپرداخت بدهی فنی‌های بزرگ بودن

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

۷. رفع افقی بدهی به جای عمودی

بدهی‌های بخش‌های مختلف روی همدیگه اثرگذارن. برای مثال بدهی تست و طراحی می‌تونن روی هم اثر بذارن. میشه برای این‌که کد قابل تست کردن باشه بعضی از طراحی‌ها رو تغییر داد یا برعکس. بنابراین بهتره که بدهی مرتبط به یه موضوع به شکل همزمان رفع بشه. یعنی سراغ یک موضوع تو همه جنبه‌ها بریم نه سراغ همه بدهی‌های یه جنبه.

۸. بعضی بدهی‌ها رو رها کنین

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


مدیریت، جلوگیری و بازپرداخت بدهی فنی (TL;DR)

بدهی فنی ابعاد مختلفی داره، بعضی‌هاشون رو می‌شه تو دسته‌های زیر قرار داد:

  • بدهی نیازمندی‌ها (Requirements): نادیده گرفتن نیازمندی‌ها، بررسی ضعیف نیازمندی‌ها و …
  • بدهی طراحی (Design) و معماری (Architecture): مثل: Design Smell ها، پیروی نکردن از قوانین طراحی یا معماری
  • بدهی پیاده‌سازی (Implementation): مثل کد تکراری، Code Smell ها و …
  • بدهی تست (Test): مثل نداشتن تست، Coverage پایین تست‌ها یا طراحی بد تست‌ها
  • بدهی مستندسازی (Documentation): مثل نداشتن مستند از بخش‌ها و تصمیمات مهم سیستم، مستندسازی ضعیف یا مستندات قدیمی
  • بدهی استقرار (Deployment): نداشتن محیط استیج، نداشتن مانیتورینگ و …
  • بدهی اجتماعی (Social) و تیمی: سیلوهای تخصصی و …


۱. بدهی نیازمندی‌ها

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

یکی از روش‌های تشخیص بدهی نیازمندی اینه که نرم‌افزار در نهایت ارزشی رو به مشتری نمی‌رسونه. وقتی ما اولویت‌های پیاده‌سازی‌مون رو به سمت نیازمندی‌هایی ببریم که ارزش کمتری برای مشتری ایجاد می‌کنن، داریم به سمت زیاد کردن بدهی نیازمندی حرکت می‌کنیم. برای مثال، شواهد نشون می‌دن که ۲۰ درصد فیچرهای یه نرم‌افزار نیازمندی ۸۰ درصد از کاربرا رو پوشش می‌ده، یعنی ۸۰ درصد فیچرها عملا بدون استفاده هستن. بدهی نیازمندی معمولا زمانی اتفاق می‌افته که به همه درخواست‌های مشتری‌ها بله بگیم یا نیازمندی‌ها رو بدون تحلیل و بررسی و تصمیم‌گیری وارد کارها کنیم.

نقش بدهی فنی تو فرایند بررسی نیازمندی‌ها چیه؟ دو مدل بدهی نیازمندی داریم:

۱.۱ مهندسی ضعیف نیازمندی‌ها: وقتی برای استخراج نیازمندی‌ها میان‌بر بزنیم، بدهی نیازمندی‌ اتفاق می‌افته. برای مثال، وقتی با همه ذینفع‌های درگیر صحبت نکنیم، نتیجه کارمون بی‌کیفیت یا با اولویت‌بندی اشتباهه. این شکست تو فرایند استخراج نیازمندی‌هاست. اشتباه تو این فرایند می‌تونه بدهی‌های فنی رو تا مراحل نهایی به شکل تصاعدی زیاد کنه. به عنوان مثال ساخت یه وبسایت که با کاربران در ارتباطه بدون درک درست از نیازمندی‌های حریم خصوصی (مثلا GPDR) می‌تونه منجر به توسعه نرم‌افزاری بشه که تو بدهی‌های فنی غرق شده.

۱.۲. نادیده گرفتن نیازمندی‌ها: این مدل بدهی منجر به پیاده‌سازی فیچر‌های اشتباه به خاطر درک نادرست‌مون از نیازمندی‌های مشتری میشه. این شکست تو فرایند توسعه نرم‌افزاره.

پیدا کردن بدهی نیازمندی‌ها

پیدا کردن بدهی تو بخش مهندسی ضعیف نیازمندی‌ها کار سختیه. ابزار خودکاری برای پیدا کردن این نیازمندی‌ها وجود نداره. تو خیلی موارد بخصوص تو قسمت نیازمندی‌های غیرکارکردی، حتی دیتای مناسب و آماده تحلیل هم پیدا نمی‌شه. مثلا تیمی‌ که می‌خواد اپلیکیشنی برای IoT خونه‌ها بسازه باید درک عمیقی از نیازمندی‌ها و محدودیت‌ها قانونی داشته باشه.

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

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

مدیریت بدهی نیازمندی‌ها

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

جلوگیری از بدهی نیازمندی‌ها

برای جلوگیری از زیاد شدن بدهی نیازمندی‌ها باید تو مرحله استخراج نیازمندی‌ها دقیق باشیم و از روش جمع‌سپاری برای جمع‌آوری اطلاعات استفاده کنیم. استخراج نیازمندی‌ها تو روش‌های اخیر توسعه نرم‌افزار معمولا به مدیر محصول یا مدیر پروژه (PM) اختصاص داده می‌شه و انجام درستش نیاز به تمرین و مهارت ارتباطی خوب داره. PM باید بتونه با مشتری و توسعه‌دهنده ارتباط بگیره و زبان مشترک بین‌شون باشه.

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

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

در نهایت PM باید بتونه نیازمندی‌ها رو به زبان قابل درک و مدیریت برای توسعه‌دهنده تبدیل کنه:

  • نیازمندی رو به روشی که توسعه‌دهنده می‌فهمه بنویسه
  • بخش‌هایی که نیاز به پیاده‌سازی نداره رو حذف کنه
  • کارها رو تو ابزار مدیریت کارهای توسعه‌دهنده وارد کنه

در کنار این‌ها PM باید همیشه نیازمندی‌ها رو به‌روز نگه داره. برای مثال اگه PM از DOORS برای مدیریت نیازمندی‌ها استفاده می‌کنه و توسعه‌دهنده از Jira، در این صورت وظیفه همگام کردن DOORS با Jira به عهده PM هست.


۲. بدهی طراحی و معماری

طراحی (Design) به معنی مدل ارتباطی و ساختاربندی در سطح کد و معماری (Architecture) ساختاربندی High-Level هستن. برخلاف بدهی‌های پیاده‌سازی، بدهی‌های طراحی و معماری سخت‌تر قابل تشخیص هستن و معمولا با بررسی تاریخچه پروژه و روند تغییراتش قابل شناسایی‌ان. بدهی طراحی به شکل مستقیم روی موفقیت پروژه در بلند مدت تاثیرگذاره.

پیدا کردن بدهی طراحی

دلایل زیادی برای به وجود اومدن بدهی طراحی وجود داره. برای مثال:

  • رعایت نکردن Separation of Concerns
  • کپی و تغییر: وقتی بخشی از کد از جای دیگه‌ای کپی شده و تغییراتی داخلش ایجاد شده
  • وابستگی‌های در هم تنیده یا توپ بزرگ گلی: وقتی به معماری سیستم توجهی نشده و طراحی‌ها به شکل ضمنی پیش رفتن.
  • تکامل بدون برنامه: وقتی فیچرهای جدید و اصلاح باگ‌ها بدون بررسی تاثیرشون روی عملکرد کلی سیستم و حفظ یکپارچگیش پیاده‌سازی شدن و نتیجه کدهایی هستن که قابلیت نگهداری، خوانایی و تغییر رو از دست دادن.

طراحی خوب، طراحی‌ای هست که انسجام (Cohesion) رو افزایش بده در حالی که در‌هم‌تنیدگی (Coupling) رو کم می‌کنه. هر کاری که این اصل رو نادیده بگیره، بدهی طراحی رو به محصول اضافه می‌کنه.

اندازه‌گیری بدهی طراحی

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

برای عددی کردن بدهی بهتره اول تعریف دقیقی از بدهی طراحی داشته باشیم. بدهی طراحی ۱. گروهی از ماژول‌های مرتبط به هم (معمولا فایل‌های مرتبط به هم) و ۲. مدلی که هزینه نگهداری این ماژول‌ها در طول زمان رو مشخص کنه، هست.

در درجه اول، چطور باید ماژول‌ها (فایل‌ها)ی مرتبط به هم رو پیدا کنیم؟ دو گام ساده برای این‌‌ کار وجود داره. اولین گام بررسی وابستگی‌های استاتیک بین فایل‌هاست. این کار با ابزارهای مختلف مثل Understand قابل انجامه. دومین گام بررسی رشد این وابستگی‌ها در طول زمانه. برای این کار باید بررسی کنیم که آیا دو فایل با هم و همزمان تغییر می‌کنن یا نه. بهترین ابزار برای این‌ کار، ورژن کنترل‌ها هستن.

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

ماتریس ساختار طراحی
ماتریس ساختار طراحی

به این ماتریس اطلاعات زیادی میشه اضافه کرد: تعداد باگ‌های فیکس‌شده که به این فایل‌ها ارتباط دارن، تعداد کامیت‌های مرتبط و … . در نهایت با بررسی این ماتریس و مدل‌ وابستگی کدها میشه مشکلات مشابه رو تشخیص داد و برای رفع‌شون برنامه‌ریزی کرد. بدهی‌های طراحی معمولا به شش ضدالگو طراحی می‌رسن:

۱. اینترفیس ناپایدار (Unstable interface): وقتی یه اینترفیس همزمان با تغییر فایل‌هایی که ازش استفاده کردن، تغییر می‌کنه.

۲. اشکال در ماژولاریتی (Modularity violation): وقتی ماژول‌های مجزا از هم، با هم تغییر می‌کنن.

۳. وراثت ناسالم (Unhealthy inheritance): وقتی یه کلاس پایه به یکی از ساب‌کلاس‌هاش وابسته است یا یه کلاینت به کلاس پایه و ساب‌کلاسش همزمان وابسته‌است.

۴. وابستگی چرخه‌ای (Cyclic dependency): وقتی چند فایل یا ماژول یه گراف به شدت متصل رو ایجاد می‌کنن.

۵. چرخه پکیج (Package cycle): وقتی دو یا بیشتر از دو پکیج به هم وابسته‌ان.

۶. عبور (Crossing): وقتی یه فایل با وابستگی ورودی و خروجی بالا، همزمان با هر تغییر باعث تغییر در فایل‌های دیگه می‌شه.

تحقیقات نشون می‌ده که این ضدالگوها رابطه مستقیمی با باگ‌ها، تغییرات و زمان تغییر دارن.


مدیریت بدهی طراحی

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

جلوگیری از بدهی طراحی

مانیفست اجایل بهمون می‌گه که «بهترین طراحی و معماری از دل تیم‌های خودمدیریت کننده به وجود میان». این جمله شاید برای تیم‌ها و نرم‌افزارهای کوچک (وقتی که تصمیمات طراحی و معماری کم هستن) جواب بده اما برای سیستم‌های بزرگ‌تر این اصل هیچ‌وقت جواب نداده. بررسی‌های مختلف نشون می‌ده که بسیاری از طراحی‌ها -طراحی‌های خوب- از دل تیم و خود به خود به وجود نمیان.

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


۳. بدهی پیاده‌سازی

«کد مثل جوکه، اگه نیازه توضیحش بدی، خوب نیست» – Cory House

تو موارد پرشماری، کدهایی که توسعه‌دهنده چند سال پیش به عنوان Prototype پیاده کرده الان «محصول» ماست. نرم‌افزاری که پر از حفره‌های امنیتیه، نمیشه Scale اش کرد و احتمالا نگهداری ازش خیلی هزینه‌بره. نوشتن کد خوب مثل سالم زندگی کردنه -ورزش کن و به اندازه، غذای خوب بخور- اما درست مثل سالم زندگی کردن، تداوم این کار خیلی سخته و خیلی‌ها کلا رعایتش نمی‌کنن.

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

شناسایی بدهی پیاده‌سازی

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

مدیریت بدهی پیاده‌سازی

برای بیشتر بدهی‌های پیاده‌سازی میشه از ابزارهای بررسی استاتیک کد استفاده کرد. در کنار این ابزارها بهتره هر نرم‌افزار یه روش رو برای پیروی استایل‌ها رو بپذیره و همه توسعه‌دهنده‌ها از اون استفاده کنن.

جلوگیری از بدهی پیاده‌سازی

زبان برنامه‌نویسی باید با توجه به دامنه مساله و نیازمندی‌های نرم‌افزار انتخاب بشه. بعد از انتخاب زبان، برای جلوگیری از بدهی‌های پیاده‌سازی بهتره روی کتابخونه‌ها و پکیج‌های ثانویه‌ای که تو کد استفاده می‌شه حساس بود. ریویو کدها به صورت Peer Review باید وجود داشته باشه و بهتره که حداقل دو نفر جدا از توسعه‌دهنده اصلی این کار رو به عهده بگیرن. برای هر ریویو جدا از بررسی‌های خودکار تغییرات (بررسی استایل و …) باید دامنه تغییر رو کمینه کرد. استفاده از پیام‌های جامع و کامل برای کامیت‌ها هم می‌تونه به جلوگیری از بدهی‌ها کمک کنه. پیام خوب کامیت شامل تغییر و جزییاتش در صورت نیازه. استفاده از پیام‌های کلی مثل Minor change/Minor improvement در نهایت کار رو پیچیده و بدهی رو زیاد می‌کنن. در نهایت، برای هر تغییر باید تست‌های خودکار پیش از انتشار نسخه هم وجود داشته باشه.


۴. بدهی تست

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

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

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

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

برای اندازه‌گیری پیچیدگی یه نرم‌افزار از متریک‌های مختلفی استفاده می‌شه. یکی از معروف‌ترین‌‌هاشون متریک McCabe یا پیچیدگی سایکلومتیک (Cyclomatic complexity) هست که تعداد مسیرهای مستقل یه کد رو اندازه می‌گیره. به عنوان مثال برای این شبه کد (که دو مسیر داره) باید حداقل دو تست وجود داشته باشه.

if (some condition) { do something return } do something else

بدهی تست رو می‌شه تو دسته‌های زیر قرار داد:

  • تست ناکافی: فانکشنالیتی اصلی کد فاقد تسته.
  • تست چیزهای اشتباه: متدها و بخش‌هایی از کد تست می‌شن که دامنه اثرگذاریشون کمه. برای این بخش‌ها هم باید تست وجود داشته باشه اما اولویت کمتری نسبت به بخش‌های اصلی دارن.
  • تست بی‌دلیل: تست‌ برای بخش‌هایی از کد که مطمئنیم درستن. مثلا یه بخش قدیمی کد که قرار نیست تغییر کنه و سال‌هاست تو پروداکشن درست کار کرده.
  • تست‌های چشمک‌زن (Flaky): تست‌هایی که با هر اجرا، نتایج مختلف می‌دن.
  • تست‌ مسیر شاد: تست‌هایی که فقط حالات درست نرم‌افزار رو پوشش می‌دن.
  • تست‌هایی که با نیازمندی‌های یا مستندات منطبق نیستن.

به طور کلی تست‌ها باید این اطمینان رو بهمون بدن که کد فعلی درست کار می‌کنه و تغییرات آینده‌مون، عملکرد فعلی رو خراب نکرده. بدهی تست رو می‌شه با نشانه‌هایی مثل زیاد شدن تعداد باگ‌های گزارش‌شده، زیاد شدن زمان رفع باگ‌ها و به وجود اومدن باگ‌های جدید بعد از رفع باگ‌های فعلی، تشخیص داد. استفاده از ابزارهای بررسی پوشش تست (Test Coverage) هم می‌تونه نیازمون به اضافه کردن تست‌ها رو مشخص کنه.


۵. بدهی استقرار

نود و نه درصد کار، تحویل کاره. – Buddy Hackett

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

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

شناسایی بدهی استقرار

بدهی استقرار رو می‌شه با استفاده از نشانه‌های مختلف شناسایی کرد:

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


مدیریت بدهی استقرار

استراتژی‌های متفاوتی برای مدیریت بدهی استقرار وجود داره:

  • جدا کردن محیط‌های استقرار: وجود سه محیط برای استقرار توصیه می‌شه: «در حال توسعه»، «پیش‌پروداکشن» و «پروداکشن»
  • افزایش قابلیت مشاهده: محیط‌های پیش‌پروداکشن و پروداکشن باید با ابزارها و شاخص‌های مناسب مانیتور بشن. متریک‌های مختلفی مثل مصرف ریسورس‌ها، آپتایم و … می‌تونن به درک درست بودن استقرار کمک کنن. به طور کلی استفاده تنها از لاگ‌ها برای بالا بردن مشاهده‌پذیری سیستم کافی نیست.
  • بررسی مسیر تغییر متریک‌های غیرکارکردی (non-functional): متریک‌هایی مثل مصرف CPU و … باید در زمان استقرار نسبت به نسخه‌های قبل بررسی بشن تا تغییراتی که انتظارش رو نداریم، شناسایی بشن.
  • بررسی متریک‌های کارکردی (functional): متریک‌هایی که مختص نرم‌افزارمون هست (مثل تعداد درخواست‌ها و …) هم باید قبل و بعد هر استقرار بررسی بشن.


جلوگیری از بدهی استقرار

برای جلوگیری از بدهی استقرار استراتژی‌های مختلفی وجود داره:

  • استقرار خودکار تدریجی: تو هر فرایند دستی، امکان خطا وجود داره. یکی از علت‌های اصلی بدهی استقرار اتکا به افراد برای انجام کارهاییه که باید خودکار انجام بشن. خطای انسانی در زمان استقرار می‌تونه اثرات جبران ناپذیر به بار بیاره. به همین دلیل استقرار باید به شکل خودکار انجام بشه. در کنار استقرار خودکار، در صورتی که با اسکیل بزرگ سر و کار داریم باید از روش‌های استقرار تدریجی استفاده کنیم تا در صورت وجود مشکل دامنه اثرگذاریش رو کمینه و امکان بازگردانی به نسخه قبل رو آسون کنیم. استقرار تدریجی حتی تو بروزرسانی سیستم‌های تعبیه شده هم کاربرد داره. مثلا تسلا برای بروزرسانی نرم‌افزار خودروهاش از استقرار تدریجی استفاده می‌کنه.
  • استقرار باید در دل پایپ‌لاین‌های CI قرار بگیره: تغییرات روی نرم‌افزار باید با شرایط از پیش تعیین شده به شکل خودکار وارد محیط‌های مختلف (استیج و پروداکشن) بشن. بهتره تریگر کردن این عملیات به شکل غیرخودکار (با تاثیر تصمیم بیزنسی) انجام بشه.
  • فرایند استقرار باید تعریف مشخص داشته باشه: خودکار کردن فرایند استقرار به معنی رها کردنش نیست. فرایند استقرار باید به طور کامل مستند بشه. تو تعریف فرایند استقرار باید به این جنبه‌ها توجه کنیم و برای هر کدومشون توضیحات صریح و جامع داشته باشیم:
    • چه کامپوننت‌هایی در حال استقرار هستن
    • برای شروع استقرار به چه چیزی نیاز داریم
    • چه متریک‌هایی رو در جریان استقرار مانیتور می‌کنیم
    • چه طور می‌تونیم استقرار رو متوقف کنیم
    • چه طور می‌تونیم به نسخه قبلی برگردیم
  • فیچر‌های جدید دکمه خاموش (Kill Switch) داشته باشن: دکمه خاموش می‌تونه در زمان مشکل فنی یا بیزنسی مورد استفاده قرار بگیره. از دکمه خاموش برای غیرفعال کردن یه فیچر در زمان اجرا استفاده می‌شه. در کنار پیاده‌سازی دکمه خاموش باید همیشه لیست بروزی از همه دکمه‌های خاموش‌مون داشته باشیم. برای سیستم‌های تعبیه شده پیاده‌سازی دکمه خاموش دشواره و باید نگرانی‌های امنیتی در نظر بگیریم.


۶. بدهی مستند‌سازی

مستند نامه عاشقانه‌ایه که به خودمون در آینده می‌نویسیم. – Damain Conway

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

دلایل مستندسازی

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

اگه بعد از یه تصمیم بزرگ چرایی و چگونگی اجراش رو مستند نکنیم یا مستندات نامناسب بسازیم، شانس ایجاد بدهی مستندسازی رو زیاد کردیم. مقاله “A Rational Design Process: How and Why To Fake It” (Parnas and Clements, 1986) هفت ویژگی مستند مناسب رو مطرح کرده. رعایت نکردن هر کدوم از این هفت اصل می‌تونه به بدهی مستندسازی منجر بشه.

  • برای مخاطب بنویسین: سند خوب جوری نوشته می‌شه که مخاطب رو خسته نکنه. اگه برای کاربر نهاییه باید به زبان قابل فهم برای اون باشه.
  • خودتون رو تکرار نکنین.
  • گنگ و مبهم نباشین.
  • از یه روش ساماندهی استفاده کنین.
  • تصمیم‌ها و چرایی‌ها رو ثبت کنین.
  • اسناد رو به روز نگه دارین اما نه خیلی به روز: اسنادی که خیلی به روز هستن احتمالا شامل جزییاتی هستن که بهشون نیازی نداریم. بهتره اسناد تو دوره‌های کنترلی مشخص به روز بشن.
  • مناسب بودن اسناد رو بازبینی کنین.


شناسایی بدهی مستندسازی

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

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

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


چه چیزهایی باید مستند بشه

مدیریت بدهی مستندسازی

فرایند مدیریت بدهی مستندسازی شامل کارهایی هست که ساخت و نگهداری مستندات رو در محلی مشخص آسون می‌کنه.

  • همزمان با توسعه، مستندسازی کنید: مستندات مربوط به تصمیمات حول کد باید در کنار کدها باشه نه تو محلی مجزا. اکثر زبان‌ها از روش‌های کامنت‌گذاری چندخطی پشتیابی می‌کنن. مستندات همراه کد باید در زمان ریویو همراه کدها ریویو بشن و تغییراتشون تو ورژن کنترل‌ها باقی بمونن.
  • دیاگرام‌ها: مثل UMLها هم بهتره تو ورژن کنترل و به صورت متنی قرار بگیرن تا تاریخچه تغییرشون مشخص باشه.
  • کد، تست و مستند رو همزمان با هم بنویسین.


جلوگیری از بدهی مستندسازی

جدا از هفت اصل مطرح شده برای تشخیص این‌که یه مستند مناسب هست یا نه دو روش دیگه وجود دارن که باهاشون میشه مناسب بودن مستند رو تشخیص داد.

  • قابلیت ردیابی: از طریق لینک‌ کردن باگ‌ها، درخواست‌ها و تغییرات مختلف (مثلا از جیرا و گیت‌لب) به مستند میشه مسیر رسیدن به تصمیمات رو هم مشخص کرد.
  • بررسی کیفیت مستندات به بخشی از فرایند کنترل کیفیت نسخه‌ها تبدیل بشه.


۷. بدهی اجتماعی و تیمی

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

تعریف بدهی اجتماعی

مهندسی نرم‌‌افزار به شکل سنتی روی مصنوعات -کدها، فرایندها، ابزار و …- تمرکز می‌کنه، با این حال افراد و تیم‌هاشون، ساختار و ویژگی‌های افراد و تمام چیزهایی که با آدم‌هایی که واقعا با این مصنوعات در اتباطن، اهمیت دارن. ملوین کانوی (Melvin Conway) تو سال ۱۹۶۸ قانونی رو معرفی می‌کنه که حالا به اسم قانون کانوی معروفه: «ساختار و معماری نرم‌‌افزار بازتابی از ساختار سازمانیه که تولیدش کرده.» برای مثال یه کامپایلر COBOL که توسط پنج نفر توسعه داده شده، تو پنج فاز اجرا می‌شه، یا یه کامپایلر ALGOL که سه توسعه‌دهنده داشته تو سه فاز اجرا می‌شه.

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

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

به طور کلی تطابق اجتماعی‌فنی (Sociotechnical Congruence) حالت ایده‌‌آل کار روی یه پروژه است به این معنی که کامپوننت‌هایی که به شدت با هم در ارتباط هستن باید توسط تیم‌هایی که ارتباطات مداوم دارن (و یک‌جا هستن) توسعه داده بشن.

شناسایی بدهی اجتماعی

مشابه Code Smell ها که برای شناسایی اشکالات کد ازشون استفاده می‌کنیم. از مفهوم Community Smells برای شناسایی و اصلاح ساختارها و رفتارهای غیربهینه اجتماعی استفاده می‌کنیم.

همه ما بخشی از شبکه‌های مختلفیم: تو خونه، تو زندگی اجتماعی و تو زندگی کاری‌مون. اگه درباره چگونگی چینش شبکه‌های اجتماعی فکر کنیم و تاثیرش رو در محیط کسب‌و‌کار ببینیم متوجه می‌شیم که بعضی از حالات این شبکه‌ها غیر‌بهینه هستن.

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

بیشتر سازمان‌ها کار بهتری نسبت به این مدل ساده و پیش‌پا افتاده انجام می‌دن. هدف این‌ مثال‌ها این بود که ببینیم ساختار چطور روی بهینه کار کردن اثرگذاره. برای شناسایی بدهی اجتماعی باید ببینیم که کدوم الگوهای اجتماعی مخربن.

  • توسعه طبق اصول (Cookbook Development): توسعه‌دهنده‌هایی که جلوی راه‌های تازه می‌ایستن و روششون رو تغییر نمی‌دن. این ضدالگو می‌تونه منجر به مشکلات ارتباطی بین اعضا یا حتی کاربران و سازمان بشه.
  • پیچیدگی زمانی (Time Warp): یه تغییر تو ساختار سازمان که به اشتباه باعث این تصور می‌شه که ارتباطات زمان کمتری نیازه داره و هماهنگی‌های صریح نیاز نیست. این ضدالگو می‌تونه باعث کیفیت پایین معماری نرم‌افزار، ایجاد Code Smell ها، مشکلات عملیاتی حل نشده و مشتریان ناراضی بشه.
  • فاصله شناختی (Cognitive Distance): فاصله‌ای که بین توسعه‌دهنده‌ها از نظر فیزیکی، فنی، اجتماعی و فرهنگی به دلیل تفاوت‌های پس‌زمینه‌ای به وجود میاد. نیازمندی‌ها به درستی منتقل نمی‌شن. باتجربه‌ها دانششون رو منتشر نمی‌کنن. این ضدالگو می‌تونه باعث زمان هدر رفته، مسخره‌کردن تازه کارا، کد کثیف، هزینه‌های اضافی توسعه، نبود درک متقابل بین بخش‌های مختلف، نبود اعتماد در شبکه و برداشت اشتباه از انتظارات بشه.
  • بیگاری از تازه‌کارا (Newbie Free-Riding): افراد تازه‌کار به حال خودشون رها می‌شن که خودشون بفهمن چه کاری رو چه جوری انجام بدن یا قدیمی‌تر ازشون بیگاری می‌کشن. این ضدالگو باعث فشار کاری بالا، ناراحتی و کم‌انگیزه شدن افراد تازه‌کار می‌شه.
  • فاصله قدرت (Power Distance): افراد قدیمی‌تر (قدرتمند‌تر) نظرات نهایی رو می‌دن. این ضدالگو می‌تونه باعث هزینه‌های اضافی پروژه، ضرر مالی و پروژه‌های شکست خورده بشه.
  • رهایی (Disengagement): رها کردن اصول و روش‌ها یا بی‌اهمیتی به کیفیت که منجر به پروداکشن کردن محصولی که هنوز آماده نیست، میشه. علاوه بر این نادیده گرفتن اطلاعات مستند نرم‌افزار، فرضیات بی‌پشتوانه، از دست رفتن اعتماد مشتری و افزایش هزینه کنترل کیفیت از اثرات دیگه این ضدالگو هستن.
  • اعضای خوک‌صفت (Piggish Members): درخواست‌های بیهوده یا اغراق‌آمیز از دیگران، اغلب به شیوه ای خودپسندانه یا آزاردهنده مطرح می‌شن. افزایش هزینه پروژه، خستگی اعضای تیم و کم شدن روحیه تیم از اثرات این ضدالگو هستن.
  • اختلاف DevOps یا DevOps Clash:اختلافات بین توسعه‌دهندگان و نیروهای عملیاتی منجر به کند شدن سرعت توسعه می‌شه. این ضدالگو اثرات مخربی مثل افزایش هزینه پروژه، کم شدن اعتماد، ناتوانی در ایجاد ارتباط، بسته شدن راه نشر دانش، کم شدن سرعت توسعه، کم شدن سرعت انتشار، بیشتر شدن تعداد حادثه‌ها و غیربهینه بودن فرایند استقرار رو با خودش به همراه داره.
  • درس نگرفتن (Unlearning): یه به‌روش یا تکنولوژی جدید که به دلیل نبودن تمایل به تغییر، اجرایی نمی‌شه. کم شدن انگیزه دانش و استفاده نکردن از به‌روش‌ها از اثرات مخبری این ضدالگو هستن.
  • سیلو (Silo): سیلوهای تخصصی که با سیلوهای دیگه ارتباط نمی‌گیرن یا فقط توسط یکی از اعضا ارتباط می‌گیرن. کم شدن تطابق اجتماعی‌فنی، دید تک‌بعدی و رفتار خودخواهانه از اثرات مخرب این ضدالگو هستن.
  • گرگ تنها (Lone Wolf): افرادی که کارشون رو با کسی در میون نمی‌ذارن، با دیگران مشورت نمی‌کنن و تو ارتباط‌شون به کسی اهمیت نمی‌دن. تغییرات معماری بدون مجوز، نگهداشت ضعیف سیستم و خستگی سایر اعضای تیم از اثرات مخرب این ضدالگو هستن.

بیشتر سازمان‌ها با Time Warp و Cognitive Distance و DevOps Clash دست و پنجه نرم‌ می‌کنن. همچنین تحقیقات نشون می‌ده که بدهی‌های معماری ارتباطی مستقیمی با Smell‌های ارتباطی دارن. (حدود ۸۰ درصد همسبتگی)


مدیریت بدهی اجتماعی

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

  • گروه رو بسازه
  • به گروه کارایی بده
  • به صورت مداوم وضعیت گروه رو رصد و ازش مراقبت کنه

مدیران باید گروه‌های به‌هم‌پیوسته بسازن، ترجیحا از نظر مکانی نزدیک به هم باشن و امکان ارتباط بین‌شون وجود داشته باشه. زمانی که امکان نزدیکی مکانی به صورت فیزیکی وجود نداره باید روش‌ها و ابزارهای ارتباطی الکترونیکی مناسب (چت تصویری، پیامرسان، جلسات روزانه و …) وجود داشته باشه.

در کنار این‌ روش‌ها می‌شه با ابزارهایی مثل CodeFace4Smells یا CodeFace مدل‌های ارتباطی سازمانی و فعالیت‌ افراد رو بررسی و ضدالگوهای ارتباطی سازمان رو با کمک‌شون استخراج کرد اما به طور معمول می‌شه بدون این ابزارها هم ضدالگوهای ارتباطی رو پیدا کرد. روش‌هایی مثل گفتگو‌های یک‌به‌یک دوره‌ای می‌تونن به پیدا کردن این ضدالگوها کمک کنن.


جلوگیری از بدهی اجتماعی

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

  • توسعه طبق اصول (Cookbook Development): برنامه‌های آموزشی برای توسعه‌دهنده‌ها. دعوت از سخنران‌ها برای آموزش. اختصاص زمان مشخص به توسعه‌دهنده‌ها برای رشد فردی.
  • پیچیدگی زمانی (Time Warp): بهبود ساختار تصمیم‌گیری. بهبود همگام‌سازی تصمیمات. داشتن نقشه مشخص از جریان‌های ارتباطی (کی با کی صحبت می‌کنه و کی تصمیم‌ می‌گیره)
  • فاصله شناختی (Cognitive Distance): تیم‌سازی (ناهار مشترک، گفتگو‌های هفتگی و …). استفاده از متخصصان ارتباطی برای آموزش. ارائه‌های منظم بین تیمی درباره کارهایی که انجام می‌دن. اجرای کارگاه و ارائه. اسناد بین‌تیمی.
  • بیگاری از تازه‌کارا (Newbie Free-Riding): برنامه‌های آموزشی منظم. جلسات تک به تک منظم و جلسات روزانه منظم و نظرسنجی درباره خلق‌وخوی افراد تیم.
  • فاصله قدرت (Power Distance): دادن کارهای مهم به افراد تازه‌کار و همراه کردن افراد کهنه‌کار برای آموزش‌شون. ایجاد انگیزه در افراد کهنه‌کار برای آموزش و آنبورد کردن تازه‌کارها.
  • رهایی (Disengagement): به توسعه‌دهنده اجازه داده بشه که زمانی از کارشون رو به حل بدهی‌های فنی بپردازن. تیم‌های متنوع (از نظر فرهنگ و جنسیت) بسازین.
  • اعضای خوک‌صفت (Piggish Members): به این افراد نشون بدین که کارشون چطور بهره‌وری رو کاهش می‌ده. برای تعداد درخواست‌هاشون سقف تعیین کنین که متوجه بشه و میزان‌شون رو کم کنه.
  • اختلاف DevOps یا DevOps Clash: مشابه روش‌های Power Distance و Cognitive Distance.
  • درس نگرفتن (Unlearning): کسایی که دانش زیادی تو یه زمینه دارن رو پیدا کنین و ترغیب‌شون کنین به دیگران یاد بدن. با شاخص‌های مختلف بهتر بودن به‌روش‌ها رو به افراد اثبات کنین.
  • سیلو (Silo): سیلوها باید جلسات منظم دوره‌ای با هم داشته باشن.
  • گرگ تنها (Lone Wolf): این افراد رو با اعضای دیگه برای انجام کار دیگه‌ای همراه کنین. شناسایی‌شون کنین و جلوی رفتارشون رو بگیرین.


تا واقعیت

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


منابع



بدهی فنیtech debtsoftware engineeringمهندسی نرم‌افزارteam management
مهندس نرم افزار، نویسنده، شاعر و خیال پرداز
شاید از این پست‌ها خوشتان بیاید