فرض کنیم که میخواهید یک خودروی گرانقیمت بخرید اما از عهدهٔ هزینهٔ خرید آن برنمیآیید و در چنین شرایطی سؤال اینجا است که چه راهکاری باید اتخاذ کرد؟ به طور کلی میتوان گفت که یک راهکار این است که وام بگیرید، خودرو را بخرید و سپس به مرور زمان اقساط وام خود را بپردازید که هرچند در این صورت با احتساب بهرهای که به وام تعلق میگیرد نهایتاً پولی بیش از قیمت واقعی خودرو پرداخت خواهید کرد، اما اگر در پرداخت اقساط تأخیر نکنید و مشمول جریمهٔ دیرکرد نشوید، پرداخت درصدی بیشتر از قیمت واقعی خودرو منطقی و گاهی اجتنابناپذیر به نظر میرسد. با این حال کاملاً واضح است که تفاوت زیادی بین وام گرفتن برای خرید مثلاً یک پراید و زیر بار وام رفتن برای خرید یک پورشه وجود دارد به طوری که برای بسیاری از افراد، مثلاً دو میلیارد تومان وام گرفتن برای خرید خودرو اصلاً منطقی نیست زیرا اساساً هدف از وام گرفتن این است که مشکل مالی ما را موقتاً حل کند نَه اینکه ما را به پرتگاه ورشکستگی بکشاند. حال با این پسزمینهٔ ذهنی، در ادامه قصد داریم به مبحث مهمی در توسعهٔ نرمافزار تحت عنوان Technical Debt (بدهی فنی) بپردازیم که از برخی جهات مشابه مثال فوقالذکر است.
بدهی فنی چیست؟
Technical Debt که تحت عناوین دیگری همچون Design Debt و Code Debt نیز از آن یاد میشود، اصطلاحی در صنعت توسعهٔ نرمافزار است که طی آن دولوپر به جای صَرف زمان زیاد و یافتن درستترین و بهترین راهحل در حین کدنویسی، یک راهحل آسان و فوری را برای حل موقت یک مسئله به کار میگیرد و در عوض متعهد میگردد که در آینده و پس از تکمیل پروژه، زمانی را برای یافتن راهحلی اصولیتر و حتی تغییر بخشی از کدها جهت جایگزین نمودن راهحل درست با راهحل موقت صرف نماید که این رویکرد را شاید بتوان چیزی شبیه بهرهٔ وام در نظر گرفت.
این راهکار از دید بسیاری دولوپرها و مدیران پروژه روشی نادرست و حتی بد در نظر گرفته میشود اما واقعیت آن است که بدهی فنی دقیقاً مانند گرفتن وام است به طوری که با گرفتن این وام، گرهٔ فرآیند توسعهٔ نرمافزار موقتاً باز شده و کار پیش میرود و از طرف دیگر گاهی واقعاً چارهای جز زیر بار بدهی فنی رفتن نیست اما در عین حال باید برای به حداقل رساندن بهرهٔ این بدهی تمام تلاش خود را به کار گیریم. Martin Fowler، برنامهنویس مطرح بریتانیایی، در این زمینه میگوید:
تمام مدتی که دارین کد میزنین، در واقع در حال یادگیری هستین و حتی ممکنه بعد از یک سال کار مداوم روی یک پروژه، تازه به رویکرد و طراحی مناسب اون پروژه دست پیدا کنین.
دیدگاه سنتی اینگونه است که یک بار کار را انجام دهید و آن را به بهترین روش و مناسبترین الگوی ممکن تکمیل نمایید اما اجرای این دیدگاه در توسعهٔ نرمافزار، حداقل در پروژههای بزرگ، امکانپذیر نیست چرا که برای یافتن بهترین روش باید با تمام الزامات پروژه آشنایی داشت و از طرف دیگر هم برای آگاهی از تمام الزامات پروژه و تشخیص الگوی مناسب، باید آن را به طور کامل انجام داد.
چگونه خود را همسو با نیازهای پروژه نماییم؟
در صنعت توسعهٔ نرمافزار، غالباً نیازهای یک اپلیکیشن یا نرمافزار به انحاء مختلفی دستخوش تغییر میشوند که در چنین مواقعی یک استراتژی مناسب این است که پروسهٔ کدنویسی را به صورت سریع شروع کنید و همچنان که پیش میروید و الزامات بیشتر و بیشتری برای شما آشکار میشوند، قادر خواهید بود تا سورسکد را ریفکتور کرده تا بتوانید پاسخگوی نیازهای جدید باشید. در چنین مواقعی، به راحتی میتوان با مد نظر دادن پلتفرمی که برای آن مشغول به کدنویسی هستید، دید روشنی از ساختار و شکل و شمایل کدی که باید زده شود داشته باشید و در نهایت قادر خواهید بود تا سورسکد قبلی را در راستای ساختار جدید، ریفکتور نمایید (شاید در نگاه اول به نظر برسد که اولین باری که اقدام به ریفکتورینگ میکنید مشکل برطرف شود، اما گاهی اوقات اصلاً اینگونه نیست و از همین روی نیاز است تا بارها و بارها با فیدبکی از کاربران نرمافزار میگیرید دست به تغییر سورسکد بزنید.)
تا اینجا مشخص شد که در ابتدای پروژه دولوپر نمیداند که چه طرح و ساختاری برای پروژهاش مناسب است و اینکه آیا در آینده این ساختار قابلیت سازگاری با سایر الزامات پروژه را دارا است یا خیر که در چنین موقعیتی است که Technical Debt به داد دولوپر میرسد و به او کمک میکند تا ریفکتور نمودن کدها را تا زمانی که الگوی مناسبی را بیابد، به تأخیر بیندازد (توجه داشته باشید که در واژهٔ انگلیسی Debt، حرف b تلفظ نمیشود.)
Abstraction تا کجا باید ادامه یابد؟
یک Task (کار) را میتوان بارها تکرار کرد و آن را اصلاح نمود اما با گذر زمان میزان پرفرومنس کاهش مییابد و صرف زمان برای انجام اصلاحات بیشتر دیگر منطقی نخواهد بود. از سوی دیگر، وجود Abstraction (انتزاع) بیش از حد در سورسکد نیز میتواند عواقب ناخوشایندی داشته باشد و به طور کلی میتوان گفت که مثلاً نوشتن یک فانکشن تکراری در چند ماژول مختلف به مراتب بهتر از نوشتن یک اَبسترکشن نادرست است.
از همین روی، صرفاً زمانی به تغییر و بهبود معماری موجود بپردازید که الزامات جدید پروژه چارهای جز تغییر آن باقی نگذاشته باشد و مادامی که با چنین الزامی مواجه نشدهاید، زمان خود را برای طراحی مجدد و یا بهبود طراحی موجود صرف نکنید بلکه در عوض سعی کنید کدهای بهینهای بنویسید که در صورت لزوم بتوان به راحتی آنها را تغییر داده و اصلاح نمود به این معنی که اگر اصلاً نیازی به اَبسترکشن نیست، هرگز کد خود را پیچیده نکنید مضاف بر اینکه تستهای کاملاً واضحی برای کدهای خود نوشته و مستند نمودن کدبیس را هم فراموش نکنید.
چه موقع بدهی فنی را پرداخت کنیم؟
اگر بر روی هستهٔ سیستم کار میکنید که تمام الزامات آن مشخص است و در آینده تغییر نخواهد کرد، میتوانید بدون ایجاد هرگونه بدهی فنی، طراحی کاملی را انجام دهید اما اگر همچنان که پیش میروید الزامات کار تغییر میکنند، بهتر است که از اهمیت استفاده از بدهی فنی را نادیده نگیرید و بدین ترتیب در شروع تَسک بیش از حد وقت نگذارید و هرچه سریعتر و با رعایت حداقلها آن را پیش ببرید و در عوض بهبود آن را به پایان تَسک موکول کنید (یعنی به زمانی موکول کنید که به جای اندیشیدن به الگو و طراحی مناسب، آن را عیناً مشاهده کنید و پروژه تکمیل شده باشد.) اما به خاطر داشته باشید که پیش از شروع یک ماژول جدید، بدهی فنی خود را نادیده نگرفته و در پایان هر تَسک، قسط وام خود (بدهی فنی) را ادا کنید و در تَسکهای تکمیلشده هیچ قرضی بر جای نگذارید.
یکی از مشکلات رایج مرتبط با بدهی فنی این است که دولوپرها به جای اینکه از این قرض تنها برای تسهیل تشخیص الگوها در آینده بهره ببرند، بدون برطرف نمودن بدهی فنی قبلی، به سراغ بدهی فنی بعدی و بعدی میروند و به این ترتیب تعداد زیادی بدهی فنی حلنشده در کدهای خود بر جای میگذارند و این وضعیت همه چیز را در آینده بدتر و پیچیدهتر میکند در حالی که قرار بود این قرضها کار را سادهتر کنند! به خاطر داشته باشید که هیچ چیز نمیتواند بهانهای برای نوشتن کدهای بد باشد و اگر خود شما بدهیهای فنی خود را برطرف نکنید، هیچ دولوپر دیگری هم نه وقت و نه حوصلهٔ این کار را نخواهد داشت (نکتهٔ دیگر اینکه تمیزی کد را برای پیش بردن سریع پروژه فدا نکنید و همواره در کدنویسی سعی کنید روشی را در پیش بگیرید که با جلو رفتن پروژه، راه برای ریفکتورینگ کدهای پیشین و هماهنگ نمودن آن با نیازهای جدید باز باشد.)
در یک کلام، تنها با استفاده از بدهی فنی میتوانید پروژه را بخش به بخش پیش ببرید بدون اینکه مجبور باشید در آغاز کار زمان زیادی را برای اندیشیدن به تمام جوانب موضوع صرف کنید. این نکته را هم فراموش نکنیم اگرچه این روش در ظاهر رویکرد خوبی به نظر میرسد، اما برای همه قابلاستفاده نیست چرا که برای هرچه اثربخشتر بودن این استراتژی، تمام دولوپرهایی که بر روی یک کدبیس کار میکنند باید از لحاظ مهارتی همسطح باشند چرا که در غیر این صورت درک اهداف پشت کدها و تصمیمگیری درست برای آنها بسیار دشوار خواهد شد.
در عین حال، در دنیای واقعی کمتر تیمی را میتوان یافت که همهٔ اعضای آن کاملاً همسطح باشند که در چنین مواقعی است که Pair Programming و Mob Programming میتوانند به یاری تیم آمده و دیدگاه، مهارت و دانش اعضا را به یکدیگر نزدیک نمایند (Pair programming روشی است که در آن دو دولوپر به صورت همزمان پشت یک سیستم نشسته و با رعایت قوانین خاصی با هم بر روی یک تَسک کار میکنند که برای کسب اطلاعات بیشتر در این خصوص، میتوانید مقالات مرتبط را با برچسب #برنامهنویسی دونفره را در سکان آکادمی دنبال نمایید. Mob Programming هم روشی است که طی آن همهٔ اعضای تیم شامل دولوپرها، طراحان، تسترها و ... همزمان پشت یک کامپیوتر نشسته و بر اساس قوانین خاص این روش بر روی تَسک مشخصی کار میکنند.)
سخن پایانی
قرض گرفتن همیشه هم بد نیست. بسیاری از افراد بدون قرض گرفتن پول شاید هیچوقت نتوانند خانه یا ماشین بخرند که این موضوع در مورد Technical Debt هم صادق است. واقعیت امر آن است که قبل از انجام پروژه هرگز نمیتوان به تمام جوانب آن اِشراف داشت و همهٔ آنها را همزمان با کدنویسی اِعمال کرد و این در حالی است که بدهی فنی این امکان را به دولوپرها میدهد تا بخش به بخش پروژه را پیش ببرند و سپس با ریفکتورینگ الزامات آتی نرمافزار را در آن بگنجانند. روی همین حساب، همواره سعی کنید پروژه را به صورت تَسک به تَسک انجام دهید و با نوشتن تستهای مناسب، کیفیت و درستی کدهای هر ماژول را بسنجید چرا که در غیر این صورت اصلاً نمیتوانید با سهولت به ریفکتورینگ کدها بپردازید.
همچنین مطمئن شوید که تنها برای هماهنگ نمودن الگوریتمهای قدیمی با نیازهای جدید اقدام به ریفکتوریگ میکنید و نه مثلاً به خاطر حدس و گمانهای خود در مورد الزاماتی که هنوز پیش نیامدهاند به علاوه اینکه اگر میخواهید دولوپری موفق به حساب آیید، همواره به خاطر داشته باشید که وظیفهٔ شما فقط کدنویسی نیست بلکه باید بکوشید تا کمی احساس هم چاشنی به این دنیای بیاحساس منطقی (توسعهٔ نرمافزار) بیفرایید.