فلسفۀ اصلی اشکالزدایی (Debugging) و روش درست آن
اشکالزدایی یا دیباگ کردنِ برنامه گاهی برای برخی افراد بسیار دشوار است. این افراد کسانی هستند که معتقدند برای اشکالزدایی یک سیستم به جای اینکه «به آن نگاه کنی» باید به «آن فکر کنی».
برای اینکه منظور از این جمله مشخص شود، مثالی بزنیم. یک وبْسِروری[1] را در نظر بگیرید که 5 درصد از مواقع نمیتواند صفحات را به کاربران تحویل بدهد. واکنش شما به این سؤال چیست؟ میپرسید چرا؟
آیا فوراً چند جواب برای این سؤال مییابید؟ آیا شروع به حدس زدن میکنید؟ در این صورت کارتان اشتباه است.
پاسخ درست به این سؤال این است که «نمیدانم». این پاسخ قدم اول برای موفقیت در اشکالزدایی است.
وقتی شروع به اشکالیابی میکنید، بدانید که واقعاً علت مشکل را نمیدانید.
وسوسهانگیز است که فکر کنیم جواب را از اول میدانیم. البته گاهی میتوان حدس زد و حدسمان درست از آب درمیآید. اما خیلی زیاد اتفاق نمیافتد که حدس اولیهمان درست باشد؛ اما زیاد اتفاق میافتد که آدمها فریب میخورند و فکر میکنند که «حدس زدن مشکل» روش خوبی برای اشکالیابی است. به هر حال بیشتر مواقع ساعتها، روزها، یا هفتهها صرف حدس زدن پاسخ و تلاش برای برطرفی باگ به شیوههای مختلف میکنیم، دریغ از اینکه نتیجهای جز پیچیده شدن کد حاصل شود! درواقع برخی از کدبِیسها[2] مملؤ از راهحلهایی برای باگها هستند که فقط حدس و گمان بودهاند و همین راهحلها عامل اصلی پیچیدگی کدبیس شدهاند.
البته در این مقاله اصل جالبی را با شما در میان میگذاریم. معمولاً اگر اشکالزدایی درست انجام شود، درواقع بخشهایی از سیستم را دور انداختهاید، آن را سادهتر کردهاید، طراحی را بهتر کردهاید؛ یعنی با برطرف کردن اِشکال (باگ) سیستم را با چنین کارهایی ارتقا دادهاید. به این موضوع بعداً میپردازیم اما فعلاً به همین اکتفا کنیم:
در بیشتر مواقع بهترین اشکالزدایی وقتی است که واقعاً موجب حذف بخشی از کد یا ساده شدن سیستم میشود.
اما به خودِ روند اشکالزدایی یا دیباگ کردن بازگردیم؛ برای اشکالزدایی چه باید بکنیم؟ حدس زدن که وقت تلف کردن است، تخیل کردن علل مشکل نیز وقتتلفی است؛ اساساً تخیلِ «ذهنی» هر راهحلی در اولین برخورد با مشکل، اتلاف وقت است. تنها کار ذهنی که باید انجام شود این است که:
یک) به یاد بیاورید که سیستم اگر کار میکرد، چگونه رفتار میکرد.
دو) بفهمید که برای دست یافتن به اطلاعات بیشتر در مورد اشکال، باید کجا را بررسی کنید.
چرا که با این دو کار به مهترین اصل اشکالیابی دست مییابیم:
برای اشکالیابی یا دیباگ کردن تا وقتی که علت مشکل فهمیده میشود، به جمعآوری دادهها بپردازید.
تقریباً همیشه با بررسی یک موضوع، در مورد آن دادههای بیشتری جمع میکنید. در مورد وبسْروری که صفحات وب را تحویل نمیداد، شاید باید به لاگهای[3] آن نگاه میکردید. یا شاید سعی میکردید که مشکل را دوباره ایجاد میکردید تا بتوانید ببینید وقتی مشکل رخ میدهد چه اتفاقی برای سرور میافتد. به همین دلیل است که غالباً افراد درخواست یک «مورد بازتولید[4]» دارند (مجموعهای از مراحل که به شما اجازۀ بازتولید همان مشکل را میدهد) تا بتوانند مشاهده و بررسی کنند وقتی باگ رخ میدهد چه اتفاقی میافتد.
گاهی اولین دادهای که باید جمع کنید، این است که باگ واقعاً چیست. معمولاً گزارشهای باگِ[5] کاربران دادههای ناکافی دارند. مثلاً فرض کنیم که یک کاربر یک باگ را اینگونه گزارش میدهد: «وقتی صفحه را بار میکنم، سرور چیزی برنمیگرداند.» این اطلاعات کافی نیست. چه صفحهای را سعی کردند بار کنند؟ منظورشان از اینکه «سرور چیزی را برنمیگرداند چیست؟» آیا فقط یک صفحۀ خالی است؟ شاید منظور کاربر را «حدس بزنید» اما بیشتر اوقات فرضیات نادرستاند. هرچقدر کاربر شما برنامهنویس یا تکنسینِ کمتجربهتری باشد کمتر میتواند مشکل را بدون پرسش دقیق از او توصیف کند. در این موارد اگر وضعیت اضطراری نیست اولین کاری که بهتر است انجام شود، فرستادن سؤالهای مشخص به فرد برای شفاف کردن گزارش باگ و توقف کار تا دریافت پاسخ است. میتوان تا وقتی که همه چیز وضوح پیدا کند اصلاً کاری نکرد. اگر دست از صبر کردن برداریم و سعی کنیم که مشکل را قبل از درک کامل آن حل کنیم، شاید وقتمان را با جنبههای احتمالی سیستم که شاید اصلاً ربطی به مشکل نداشته باشند تلف کردهایم. بهتر است تا وقتی که کاربر پاسخ میدهد وقتمان را برای کار مفیدی صرف کنیم و سپس وقتی که واقعاً یک گزارش باگ کامل داریم، به بررسی باگ که اکنون آن را شناختیم بپردازیم.
البته نباید برای ناکامل بودن گزارش باگ، با کاربران بیادبانه یا غیردوستانه برخورد کرد. اینکه شما در مورد سیستم بیشتر میدانید و آنها کمتر، دلیل بر این نیست که موجود والاتری هستید که باید به کاربران با تحقیر برخورد کنید و خودتان را باهوشتر و بالاتر از آنها ببینید! بلکه سؤالهایتان را دوستانه و مستقیم بپرسید و فقط به فکر گرفتن اطلاعات باشید. بهندرت میشود که گزارشدهندههای باگ احمق باشند بلکه واقعاً نمیدانند و این بخشی از کار شما است که برای آنها اطلاعات مناسب فراهم کنید. اگر افراد همیشه اطلاعات مناسب ارائه نمیدهند، میتوانید یک پرسشنامه یا فرم بر صفحۀ گزارش باگ درج کنید تا مجبور شوند اطلاعات را بهدرستی گزارش بدهند. نکتۀ مهم کمک کردن به آنهاست تا بتوانند به شما کمک کنند مشکلات را بهآسانی حل کنید.
وقتی که باگ مشخص شد باید برگردید و بخشهای مختلف سیستم را وارسی کنید. اینکه کدام بخشهای سیستم باید بررسی شوند به دانش شما از سیستم بستگی دارد. معمولاً این بخشها، لاگها Logs، نظارتها monitoring، پیامهای خطا Error Messages، روگرفت حافظه Core dump و دیگر خروجیهای سیستم است. اگر این بخشها را ندارید، شاید مجبور شوید نسخۀ جدیدی از سیستم را عرضه کنید که قبل از اشکالیابی کامل سیستم چنین اطلاعاتی را فراهم کند. برطرفی یک باگ کار بسیار سنگینی به نظر میرسد اما درواقعیت معمولاً وقتی یک نسخۀ جدیدی که اطلاعات بهتری فراهم میکند ارائه میشود، سریعتر از زمانی صورت میگیرد که سیستم را بالاوپایین کنید و اطلاعات را حدس بزنید. این دلیل خوب دیگری برای عرضههای مرتب و سریع محصول است؛ بهتر است نسخۀ جدیدی داشته باشید که اطلاعات موردنیازِ اشکالزدایی را سریعتر فراهم میکند. گاهی میتوانید ساختار جدید سیستم خود را به کاربری که با مشکل مواجه است نیز بدهید تا برای گرفتن اطلاعات موردنیاز خودتان میانبر بزنید.
گفته شد که باید به خاطر بسپارید که سیستم سالم، چطور کار میکند. زیرا اصل دیگری برای اشکالزدایی وجود دارد:
برای اشکالزدایی به مقایسۀ دادههایی که از سیستم مشکلدار دارید با دادههایی که سیستم سالم باید داشته باشد بپردازید.
وقتی که پیامی را در یک لاگ میبینید، آیا یک پیام طبیعی است یا واقعاً یک خطا است؟ شاید در لاگ گفته شده “Warning: all the user data is missing” (هشدار: تمام دادههای کاربر از دست میرود). این به نظر یک خطا[6] است اما وبْسرور شما هربار که شروع به کار میکند این خطا را نشان میدهد. باید بدانید که یک وبْسرور سالم این کار را میکند. باید رفتار یا خروجیای را بیابید که یک سیستم سالم از خود نشان نمیدهد. به علاوه باید معنای این پیامها را بtهمید. شاید وبْسرور، پایگاهدادههای کاربرانیِ دارد که از آن استفاده نمیکنید و به همین دلیل این هشدار را دریافت میکنید؛ زیرا قصد شما تمام «دادههای کاربریای» است که از دست میروند.
بلاخره نشانی را پیدا میکنید که یک سیستم سالم انجام نمیدهد. اما وقتی به آن نگاه میکنید نباید فوراً علت مشکل را حدس بزنید. مثلاً شاید پیامی در لاگ باشد که میگوید «خطا: حشرات دارند تمام کلوچههای شما را میخورند.» شاید برای «برطرفی» این رفتار، پیام را پاک کنید. اگر پیام را پاک کنید رفتار سیستم طبیعی میشود؟ خیر! باگ اصلی همچنان باقی میماند. این کار احمقانه است اما افراد گاهی برای برطرفی یک باگ چنین کارهایی را میکنند. سعی نمیکنند که علت اصلی مشکل را بیابند بلکه آن را با راهحلهای نامؤثری لاپوشانی میکنند؛ این راهحلها تا ابد در کدبیس میمانند و پیچیدگی بیشتری برای کسانی که از آن پس با کد کار میکند ایجاد میکنند.
حتی اگر برطرف کردن مشکلی باگ را برطرف میکند، نمیتوان گفت آن مشکل قطعاً علت ریشهای باگ بوده است. شاید تقریباً درست باشد؛ اما درست این است که بگوییم: اگر مطمئن شویم که با برطرف کردن یک مشکل، باگ هرگز رخ نمیدهد، علت اصلی باگ، آن مشکل بوده است. باز هم این گفته قطعی نیست؛ «برطرف کردن» درجاتی دارد. یک باگ را میتوان «کاملتر» یا «سطحیتر» برطرف کرد، بستگی دارد که مایلید چقدر راهکار شما «عمیق» باشد و چقدر میتوانید برای آن وقت صرف کنید. پرواضح است که معمولاً وقتی علت درست یک مشکل را یافتید، آن را میفهمید و آنموقع میتوانید بگویید که باگ برطرف شده است. اما مبادا برای از بین بردن علل، علائم را حذف کنید!
وقتی علت را بیابید، شکی نیست که برطرفش میکنید. اگر مراحل قبلی را درست انجام دادید، آسانترین بخش کار برطرف کردن علت است.
بنابراین از مجموع آنچه ذکر شد میتوانیم چهار مرحله برای اشکالزدایی یا دیباگ کردن در نظر بگیریم:
یک) آشنا بودن با کارکرد سیستم سالم.
دوم) فهمیدن اینکه فعلاً علت مشکل را نمیدانید.
سوم) بررسی دادهها تا وقتی که علل مشکل را دریابید.
چهارم) برطرف کردن علل نه علائم.
بهنظر ساده میرسد اما همیشه شاهدیم که افراد از این فرمولها تعدی میکنند. بیشتر برنامهنویسها وقتی با یک باگ روبهرو میشوند، مایلند بنشینند و در مورد آن فکر کنند یا در مورد علت مشکل فرضیهسازی کنند؛ این کارها فقط حدس و گمان هستند. خوب است که با دیگرانی که شاید در مورد سیستم اطلاعاتی داشته باشند صحبت کرد و با آنها مشورت کرد که از کجا دادهها را برای اشکالیابی بررسی کنیم. اما اگر همه باهم بنشینند و حدس بزنند که باگ چه میتواند باشد رجحانی بر تنها نشستن و گمانهزنی ندارد، جز اینکه با همکاران حرفی میزنیم و حتماً اگر از آنها خوشمان بیاید، خوش میگذرد. اینگونه علاوه بر هدر شدن زمان خودمان، زمان افراد دیگری نیز هدر میشود.
بنابراین وقت افراد را هدر نکنیم و پیچیدگی بیشتر از نیاز کدبیس ایجاد نکنیم. این روش اشکالیابی کارکرد مناسبی دارد. همهجا مناسب است، در هر کدبیسی، با هر سیستمی. گاهی مرحلۀ «جمعآوری دادهها» واقعاً دشوار است، خصوصاً وقتی نمیتوان باگها را بازتولید کرد. اما حتی در این بدترین حالت، با بررسی کد و تلاش برای مشاهدۀ باگ در آن، یا کشیدن نموداری از رفتار سیستم برای درک مشکل در رفتار سیستم میتوان به جمعآوری دادهها پرداخت. این کار را اگر مجبور شدید، آخرین راهحل خود برای جمعآوری دادهها در نظر بگیرید، از حدس زدن مشکل یا این دید غلط که مشکل را میدانید بهتر است.
گاهی وقتی در حال کاووش دادهها مناسب برای یافتن مشکل هستید، انگار جادویی میشود و باگ برطرف میشود. خودتان امتحان کنید تا باور کنید! حداقل برای تفریح بد نیست!
[1] Webserver
[2] Codebases
[3] Log
[4] Reproduction Case
[5] Bug files
[6] Error