لوگوی شاگرد

شاگرد

آموزش بی حد و مرز، آینده بی نهایت

ورود

کد تمیز یا تحویل سریع؟ ۴۴۰ میلیون دلار در ۴۵ دقیقه

کد تمیز یا تحویل سریع؟ ۴۴۰ میلیون دلار در ۴۵ دقیقه
مم
• 8 دقیقه مطالعه

اول اوت ۲۰۱۲، ساعت ۹:۳۰ صبح، بازار بورس آمریکا باز شد. تا ساعت ۱۰:۱۵، یکی از بزرگ‌ترین شرکت‌های معامله‌گری آمریکا ، Knight Capital ، ۴۴۰ میلیون دلار ضرر کرده بود. این عدد، بیشتر از کل ارزش بازار خود شرکت (حدود ۳۶۵ میلیون دلار) بود. شرکت، عملاً قبل از ناهار، ورشکسته شده بود.

دلیلش نه یه هک بود، نه یه بحران اقتصادی. دلیلش یه تکه کد بود که هشت سال پیش نوشته شده بود و قرار بود مرده باشه.

آنچه واقعاً اتفاق افتاد

طبق گزارش رسمی کمیسیون بورس آمریکا (SEC)، ماجرا از یه قابلیت قدیمی به اسم «Power Peg» شروع شد ، یه نوع سفارش معاملاتی که مال حدود سال ۲۰۰۳ بود و دیگه استفاده نمی‌شد. مشکل اینجا بود که کد این قابلیت هیچ‌وقت واقعاً حذف نشده بود؛ فقط غیرفعال شده بود و همچنان تو سرورهای Knight، خاموش و ساکت خوابیده بود.

fcc1

بعدها، برای پشتیبانی از یه برنامه‌ی جدید بورس نیویورک، Knight یه قابلیت جدید نوشت. یه مهندس، برای اینکه فلگ (سوئیچ) جدید نسازه، تصمیم گرفت از همون فلگ قدیمی Power Peg دوباره استفاده کنه ، یه تصمیم به‌ظاهر منطقی و سریع.

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

صبح ۱ اوت، وقتی سفارش‌هایی با اون فلگ دوباره‌استفاده‌شده به سرور هشتم رسیدن، اون سرور به‌جای اجرای کد جدید، کد قدیمی و مرده‌ی Power Peg رو بیدار کرد ، الگوریتمی که بی‌وقفه و بدون هیچ کنترل ریسکی می‌خرید و می‌فروخت. تو ۴۵ دقیقه، حدود ۴ میلیون معامله روی ۱۵۴ سهام انجام شد. سیستم هیچ محدودیت تجمعی نداشت که جلوش رو بگیره.

جالب‌ترین جزئیات: سیستم داخلی Knight، قبل از باز شدن بازار، ۹۷ ایمیل خودکار با موضوع «Power Peg غیرفعال شده» فرستاده بود ، یعنی هشدار وجود داشت، اما کسی جدی‌شون نگرفت.

نتیجه: SEC شرکت رو ۱۲ میلیون دلار جریمه کرد، و Knight ظرف چند روز، برای بقا مجبور شد یه نجات‌دهنده‌ی مالی ۴۰۰ میلیون دلاری بپذیره و بعدش هم توسط شرکت دیگه‌ای خریداری شد.

fcc4

این چه ربطی به «کد تمیز یا تحویل سریع» داره؟

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

  • کد مرده‌ای که هیچ‌وقت پاک نشد («بعداً پاکش می‌کنیم»)

  • فلگ دوباره‌استفاده‌شده (راه میان‌بر به‌جای ساخت یه فلگ جدید)

  • نبود رویه‌ی استقرار مکتوب (سرعت به‌جای فرایند)

  • نبود محدودیت خودکار ریسک (چیزی که با کمی وقت اضافی قابل‌پیاده‌سازی بود)

هر کدوم از این تصمیم‌ها، تو لحظه‌ی خودش «سریع‌تر» و «راحت‌تر» بود. مجموعشون یه بمب ساعتی ساخت.

بدهی فنی دقیقاً چیه؟ (و چرا معنای اصلیش اشتباه فهمیده می‌شه)

اصطلاح «بدهی فنی» رو وارد کانینگهام (Ward Cunningham)، برنامه‌نویس آمریکایی، در سال ۱۹۹۲ ابداع کرد. تشبیهش ساده‌ست: وقتی برای تحویل سریع‌تر، یه راه‌حل ساده‌تر (و کمتر ایده‌آل) انتخاب می‌کنید، مثل اینه که پول قرض گرفته باشید ، فعلاً به هدفتون می‌رسید، ولی «بهره» هم روی گردنتون می‌مونه: هر تغییر بعدی، سخت‌تر و گرون‌تر می‌شه، تا وقتی که «بدهی» رو با بازنویسی کد پرداخت کنید.

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

fcc7

ربع‌های بدهی فنی: همه‌ی بدهی‌ها بد نیستن

مارتین فاولر (Martin Fowler)، یکی از شناخته‌شده‌ترین متخصصان مهندسی نرم‌افزار، در سال ۲۰۰۹ یه چارچوب ساده برای طبقه‌بندی بدهی فنی پیشنهاد کرد: ربع‌های بدهی فنی. این چارچوب، بدهی رو بر دو محور طبقه‌بندی می‌کنه: «عمدی در برابر ناخواسته» و «محتاطانه در برابر بی‌ملاحظه»:

  • عمدی + بی‌ملاحظه: «ما وقت نداریم طراحی کنیم» ، تصمیم آگاهانه به نادیده گرفتن کیفیت، بدون هیچ برنامه‌ای برای جبرانش. این خطرناک‌ترین حالته.

  • عمدی + محتاطانه: «باید همین الان منتشر کنیم و عواقبش رو بعداً مدیریت کنیم» ، یه تصمیم استراتژیک آگاهانه، با درک هزینه‌ها و برنامه‌ی مشخص برای پرداخت بدهی. این حالت، گاهی کاملاً منطقی و حتی درسته (مثلاً برای رسیدن به بازار قبل از رقبا).

  • ناخواسته + بی‌ملاحظه: «لایه‌بندی معماری چیه؟» ، بدهی ناشی از ندونستن اصول اولیه.

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

پیام این چارچوب برای دوراهی «تمیز یا سریع» چیه؟ اینکه سوال درست، «تمیز یا سریع؟» نیست، بلکه اینه: «آیا این تصمیم عمدیه، محتاطانه‌ست، و برنامه‌ای برای پرداخت بدهیش دارم؟» ماجرای Knight Capital، بیشتر یه نمونه از ربع «بی‌ملاحظه» بود: نه یه تصمیم آگاهانه با برنامه‌ی جبران، بلکه انباشت تصمیم‌های کوچیکی که هیچ‌کس مسئول پرداختشون نبود.

پس چطور تصمیم بگیریم؟

با توجه به همه‌ی این‌ها، چند سوال عملی که می‌تونه کمک کنه:

  • آیا این «بدهی» رو ثبت می‌کنم؟ بدهی‌ای که مستند نشده، فراموش می‌شه و فراموش‌شدن، دقیقاً همون چیزیه که کد مرده‌ی Knight رو به بمب تبدیل کرد.

  • اگه بشکنه، چقدر گرون تموم می‌شه؟ کد یه صفحه‌ی وبلاگ با کد یه سیستم مالی، سطح ریسک کاملاً متفاوتی دارن و سطح «قابل‌قبول» بدهی هم باید متفاوت باشه.

  • آیا کد مرده رو پاک می‌کنم، یا فقط غیرفعالش می‌کنم؟ غیرفعال‌سازی، ارزونه؛ حذف واقعی، ایمن‌تره.

  • آیا فرایند استقرارم، حتی برای «تغییرات کوچیک»، بازبینی و تأیید داره؟ ماجرای Knight نشون داد تو سیستم‌های مهم، «فقط یه کپی ساده» هم می‌تونه فاجعه بسازه.

fcc5

درس‌هایی که مهندس‌ها از این ماجرا گرفتن

بعد از این رویداد، تحلیل‌گران و مهندس‌های زیادی سعی کردن ریشه‌ها رو بررسی کنن. چند درس عملی که تقریباً در همه‌ی این تحلیل‌ها تکرار می‌شه:

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

  • هیچ‌وقت فلگ قدیمی رو برای معنی جدید دوباره‌استفاده نکنید. فلگ‌ها باید یه معنی ثابت داشته باشن؛ دوباره‌استفاده کردنشون، یه دام مخفیه که تو ماه‌ها یا سال‌های بعد، کسی که تاریخچه رو نمی‌دونه، توش می‌افته.

  • استقرار باید خودکار باشه، نه دستی. وقتی کپی کردن کد روی سرورها به دست یه آدم سپرده می‌شه، فراموش کردن یه سرور از هشت‌تا فقط یه سوال زمانه. ابزارهای استقرار خودکار، این دسته از خطاها رو تقریباً حذف می‌کنن.

  • هشدارها باید به یه نفر مشخص برسن و پاسخ‌گو داشته باشن. ۹۷ ایمیل هشدار، وقتی به هیچ آدم مسئولی نرسه، عملاً وجود نداره.

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

بدهی رو در عمل چطور مدیریت کنیم؟

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

۱. «قانون پیشاهنگ» (Boy Scout Rule). ایده‌ی ساده‌ست: هر وقت به یه فایل دست می‌زنید، اون رو کمی تمیزتر از چیزی که پیداش کردید ترک کنید. لازم نیست کل پروژه رو بازنویسی کنید؛ فقط هر بار یه بهبود کوچیک. این کار، بدون نیاز به وقت جداگانه، به‌مرور بدهی رو کم می‌کنه.

۲. زمان اختصاصی برای بدهی. بعضی تیم‌ها یه سهم ثابت از هر اسپرینت (مثلاً ۱۰ تا ۲۰ درصد) رو به کارهای مربوط به بدهی فنی اختصاص می‌دن ، مثل حذف کد مرده، به‌روزرسانی وابستگی‌ها، یا بازنویسی بخش‌های مشکل‌دار. مهم اینه که این زمان از قبل و رسمی تعیین بشه، نه اینکه هر بار با «فشار ددلاین» قربانی بشه.

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

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

fcc6

چطور با مدیر غیرفنی درباره‌ی بدهی حرف بزنیم؟

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

راه‌حل اینه که به‌جای زبان فنی، به زبان ریسک و هزینه‌ی کسب‌وکار صحبت کنید. به‌جای گفتن «این بخش کد خیلی کثیفه»، بگید: «اگه این بخش رو الان درست نکنیم، اضافه کردن قابلیت بعدی که برای مشتری‌ها مهمه، احتمالاً دو برابر زمان می‌بره» یا «این بخش تو صورت خرابی، ممکنه کل سیستم پرداخت رو از کار بندازه». مثال Knight Capital هم دقیقاً یه ابزار خوب برای همین گفتگوست: یه مثال واقعی که نشون می‌ده وقتی ریسک‌های به‌ظاهر کوچیک نادیده گرفته می‌شن، هزینه‌ش می‌تونه از کل ارزش شرکت هم بیشتر بشه.

حرف آخر

Knight Capital یه مثال شدیده ، بیشتر بدهی‌های فنی، به‌جای مرگ ناگهانی در ۴۵ دقیقه، آروم آروم سرعت تیم رو کم می‌کنن و هزینه‌ی هر تغییر رو بالا می‌برن. اما این ماجرا یه درس مهم داره: تصمیم‌های «سریع» و «کوچیک»، وقتی مستند نشن و هیچ‌کس مسئول پرداختشون نباشه، می‌تونن در یه لحظه‌ی نامناسب، همه‌چی رو بشکنن. دوراهی «تمیز یا سریع»، جواب کلی و یکسان نداره ، اما یه قانون ساده داره: هر بار که سرعت رو انتخاب می‌کنید، حداقل مطمئن بشید که می‌دونید دقیقاً چی رو دارید قرض می‌گیرید، و کِی قراره پسش بدید.