از شروع برنامه نویسی به شکل جدی متوجه شدم که معمولا تمام باگ ها متعلق به کد خود من هستند و احتمال وجود باگ در نرم افزار هایی (عموما game engineهایی) که از آن ها استفاده می کنم خیلی خیلی کم است. حتی بحث های زیادی با دوستانم که با من کار می کردند و یا در دانشگاه با آن ها صحبت می کردم راجع به این موضوع داشتم. بعضی بچه ها تا برنامه کار نمی کرد گمان می کردند که مشکل در نرم افزار های مورد استفاده است در حالی که معمولا چنین نیست.
احتمال وجود باگ در کامپایلر ها بسیار کمتر از همه نرم افزار های دیگر، بعد از آن در سیستم عامل ها و بعد در کتابخانه های معروف باگ ها بسیار کم هستند. به هر حال مواقعی هستند که در این نرم افزار ها هم باگ پیدا می شود. شگفت انگیز است که در کامپایلر های معروف پر استفاده ترین زبان ها مثل C هم باگ (هر چند خیلی خاص و کوچک) بعد از سال ها استفاده یافت می شود. اما این موضوع نباید دلیل شود که کسی شک کند کامپایل یک حلقه for ساده با اشکال مواجه باشد. من این مورد را دیده ام و بزرگ نمایی نمی کنم. معمولا باگ ها هنگام تولید feature های جدید و یا در بخش هایی از یک بسته نرم افزاری که چندان مورد استفاده قرار نگرفته یافت می شوند و به سرعت هم برطرف می شوند.
زمستان دو سال پیش من در حال انتقال دو بازی آموزشی به کتابخانه نسبتا تازه شبکه یونیتی که به آن uNet می گویند بودم و قراردادی دو ماهه با شرکت صاحب بازی ها در New York داشتیم. بخش مربوط به webGL کتابخانه شبکه حتی کمتر از بخش اصلی آن تست شده بود و در داخل یونیتی هم کسی خیلی آن را تست نمی کرد. این بخش از WebSocket استفاده می کرد و کاملا از بقیه platform ها که بر پایه UDP بودند متفاوت بود. بازی ما به خوبی اجرا می شد ولی هر چند مسابقه یک بار در وسط بازی همه کلاینت ها disconnect می شدند. ما گمان کردیم که مشکل در کد خودمان است و قبل از چک کردن این موضوع میزان مصرف پهنای باند بازیمان را (که FPS و بسیار سریع بود) تا حد توانمان در زمان محدود پایین آوردیم. ما به این کار به دلایل دیگر هم نیاز داشتیم زیرا WebSocket همواره داده ها را reliable ارسال می کند و برای ساخت بازی FPS خیلی مناسب نیست. مشکل کم شد ولی برطرف نشد. پس از چند هفته بررسی و صحبت با یکی از همکارانم به اسم جکی که از مهندسان اصلی EVE Online بوده و ... گمان کردیم که Nagle Algorithm دلیل مشکل است. مهندسان یونیتی سخت پذیرفتند که آن را disable کنند در حالی که برای بازی ها این کار بسیار معمولی است. پس از بررسی های بیشتر لاگ سرور متوجه شدم یونیتی در سرور errorی لاگ می کند و بعد همه قطع می شوند. error را در گوگل جست و جو کردم که به سایز بافر دریافت داده در کتابخانه WebSocket مورد استفاده یونیتی مربوط می شد. پس از ارسال ناموفق چند پکت داده های ما از 4k بزرگتر می شد و باعث buffer overflow می شد. مهندسان یونیتی امکان تغییر سایز بافر را برای ما ضافه کردند تا ما بتوانیم برای هر کلاینت از بافر بزرگتری استفاده کنیم. سایز این بافر هم نمی تواند خیلی بزرگ باشد زیرا باعث موفقیت آمیز شدن نوع ساده ای از حملات DOS می شود. هر سرور در بازی ما حد اکثر 16 بازی کن را در خود جای می داد و به همین دلیل ما می توانستیم بافر هر کس را به سادگی 12k و یا حتی 16k در نظر بگیریم. اگر قرار باشد یک سرور 20000 کلاینت همزمان را در خود جای دهد، هر 10k بافر اضافه به معنی حدود 195MB حافظه اضافه خواهد بود.
به طور کلی وجود باگ در نرم افزارهایی که ما از آن ها استفاده می کنیم بسیار بدتر از وجود باگ در نرم افزار خودمان است. درست است که زحمت برطرف کردن مشکل گردن کس دیگری می افتد ولی زمان انجام این کار و کیفیتش دست ما نخواهد بود و گاهی این موضوع خیلی حیاطی است. در کیس ما ، بازی باید به مدارس بخش بزرگی از آمریکا فروخته می شد و زمان خرید مدارس تا تاریخ معینی بود و اگر ما بازی را در زمان مناسب به دست مدارس نمی رساندیم ، دیگر آماده شدن بازی فایده ای نداشت. بدی مشکل هم این جا بود که مشکل قطعی فقط وقتی بیش از 5-6 بازی کن از جاهای مختلف و نسبتا دور از هم به سرور وصل می شدند اتفاق می افتاد. اگر local تست می کردیم و یا 5 نفر از یک جا به سرور وصل می شدند معمولا در ارسال پکت ها اختلال کمتر بود و این مشکل پیش نمی آمد. به همین دلیل برای تست کردن یک fix باید چندین نفر از جاهای مختلف کلاینت را اجرا می کردند. راه دیگر داشتن یک ابزار load test نسبتا پیچیده بود که وقت تولید آن را نداشتیم. کل زمان پروژه برای من و برای تبدیل از کتابخانه شبکه uLink به uNet دو ماه بود که به علت وجود این باگ حدود سه ماه طول کشید تا کل پروژه جمع شد. اگر باگ در نرم افزار خودمان بود احتمالا سه هفته از این زمان کم می شد. متقاعد کردن مهندسین یونیتی برای امتحان کردن پروژه ما و یافتن باگ کار سختی بود زیرا نیاز به پروژه ای کوچک بود که مشکل را به سادگی نشان دهد. اگر خودم مشکل آن ها را به شکل دقیق پیدا نمی کردم، معلوم نبود که یکی دو ماه بیشتر طول بکشد یا نه. در ضمن پس از آماده شدن فیکس مربوط، review ها در یونیتی بیش از 2 هفته طول کشید و branch این fix با نسخه اصلی merge نمی شد. مهندسی که با ما کار می کرد سعی کرد با ارسال custom build کار ما را سرعت ببخشد ولی این کار هم دارای مشکلاتی بود و نهایتا ما صبر کردیم تا یک patch release با fix مورد نیاز ما بیرون بیاید. در تمام این مدت آرزو می کردن کاش باگ در کد خودم بود. البته پس از آن هم با مهندس شبکه یونیتی دوست شدم و هم با صاحبان پروژه ای که رویش کار می کردم رابطه ام بسیار بهتر شد و خاطره خوبی برای همه به جا ماند.