یه داستان واقعی از دیباگ کردن یه باگ که تقصیر من نبود ولی خب... من درستش کردم (یا بدترش کردم، بسته به این که کِی قصه رو تعریف کنم)
خب بذارید از اول بگم. داشتم یه سرویس می ساختم که خیلی بدرد بخور بود و میخواستم تو سایت بذارم تا کاربرا استفاده کنن، داشت خوب پیش می رفت.
یه جایی از کار به این رسیدم که اگه وب هوک پروایدر پیامکی یه لحظه دیر کنه یا قطع بشه، خودم یه Fallback داشته باشی که مستقیم بریم از API خودشون بپرسی «ببخشید پیامکی برامون اومده؟». ساده به نظر می رسید. یه تاریخ شروع، یه تاریخ پایان، بفرست، جواب بگیر، تموم.
اینجا بود که ماجرا شروع شد
یه API تستی نوشتم برای تست APIهای پروایدر که کار می کرد. واقعاً کار می کرد. تست می زدم، پیامک ها می اومدن، همه چی روبه راه بود. بعد یه لحظه با خودم گفتم «صبر کن ببینم، من همه جای کد از DateTime.Now استفاده کردم، این که اصلاً درست نیست! باید UTC باشه که!»
می دونید دیگه، همون حس معلم گونه که میاد سراغ آدم وسط کدنویسی و میگه «تو که مهندسی، درستش کن».
پس نشستم همه جا رو تمیز کردم. DateTime.Now شد DateTime.UtcNow. احساس خوبی داشتم. انگار یه چیزی رو نجات داده باشم. یه commit قشنگ با پیام «fix: use UTC consistently» زدم و رفتم تست کنم.
نتیجه؟ هیچی. صفر. لیست پیامک ها خالی خالی خالی.
اول رفتم سراغ همه ی گزینه های منطقی
فکر کردم شاید توکن اشتباهه. چک کردم، نه درسته. فکر کردم شاید پارامتر Page یادم رفته. نه، اونم بود. حتی رفتم لاگ گرفتم از پاسخ خام سرور که ببینم دقیقاً چی برمی گرده — دیدم یه جا با کد خطای عجیب غریبی مواجه شدم که می گفت شماره صفحه غیرمجازه (که اون یکی داستان جدا بود، یه تست موقت داشتم که پارامتر Page رو دستی حذف کرده بودم، بگذریم).
بعد از حل اون، دوباره برگشتم سر جای اول: خالی. هیچی نمی اومد. حتی وقتی بازه رو گذاشتم سه روز عقب تر، بازم هیچی!
اینجا بود که با خودم گفتم «صبر کن، این که قبلاً کار می کرد. چی عوض شده؟»
و جواب اینجا بود...
هیچی عوض نشده بود جز همون کاری که فکر می کردم «درستش» کردم.
نشستم یکی از همون تایم استمپ هایی که پروایدر واقعاً برام برگردونده بود رو باز کردم ببینم دقیقاً مال چه ساعتیه. و... تابلو شد. سرور پروایدر داشت ساعت محلی ایران رو مستقیم به عنوان Epoch حساب می کرد. بدون تبدیل timezone. انگار که تایم زون اصلاً وجود نداره.
یعنی وقتی من با اون کد قبلی (که ساعت محلی رو گرفته بودم و فقط برچسب UTC روش زده بودم، بدون تبدیل واقعی) باهاش صحبت می کردم، عملاً داشتم همون زبونی حرف می زدم که اون بلده. و وقتی «درستش» کردم و واقعاً UTC واقعی فرستادم، دیگه با هم یکی نبودیم — سه ساعت و نیم اختلاف افتاده بود و طبیعتاً هیچ پیامکی توی اون بازه پیدا نمی شد.
خلاصه، یه API که اسم پارامترش رو گذاشته «TimeStamp» ولی در واقع فقط داشته ساعت روی دیوار دفترشون رو بهم پس می داده البته فقط در قالب Timestamp یعمی از Utc استفاده نمیکرده و زمان لوکال رو حساب میکرده حالا این کجا مشکل پیش میاره؟اگرمن سرورم رو بذارم روی سرورهای کانادا دیگه اطلاعاتی که میخوام از این پروایدر واکشی کنم کوچکترین اعتباری نداره چون چندین ساعت اختلاف زمانی داریم٬ باید چکار کنم؟ باید بیام تمام جاهایی که مربوط به زمان هست رو با تایم زون ایران ذخیره کنم و دستی بهش بگم تایم زون من اینه و این باعص میشه مثلا وقتی کاربر لاگ فعالیتش رو میبینه یهو میبینه ساعت 3 صبح لاگین داشته!!!! بابا من ساعت 3 صبح که خواب بودم کی لاگین کرده!؟
این میشه که وقتی نرم افزار شمیم رو باز میکنیم یارانه یک نفر دیگه رو نشون میده (البته اگر بتونید اول لاگین کنید٬ نرم افزار بانک رو نصب میکنی میبینی نصفش یا کار نمیکنه یا اگر کار میکنه ناقصه٬ میبینی نرم افزار برق من رو نصب میکنی برنامه برق رو میزنه ولی به چپ نگاه میکنه پاس میده به راست! (خوب شد یادم اومد٬ در زمان دولت قبل از دولت رئیس جمهور شهید که تو شهرستان دو تا دو ساعته برق میرفت و هر چند روز هم یک بار به بهانه اینکه بالاخره برقه دیگه خرابی داره یکی دو ساعت هم اونطوری قطع میشد و جالب اینجا بود که بعد از تابستون دیگه خرابی نداشتیم! در همان زمان سه بار کولر دفتر کارم موتورش سوخت٬ کولر خونه دو بار خراب شد٬ بماند که چقدر گوشی ها و مودم و از این دست دستگاه ها خراب نشدن به صورت کامل ولی نبسانات برق به قدری بود که باطری این دستگاه ها دیگه عملکرد قدیم رو ندارند٬ یخچال-فریزر سامسونگ اصل دو تیکه٬ فریزرش سوخت و …).
بعد شرکت عزیزی از تهران از بنده خواستند اول مشاوره بدم برا ساخت دو سه پروژه ملی٬ عرض میکنم ملی یعنی واقعا برای مردم قرار بود ساخته بشه و یوزر خیلی زیادی داشت و من معماری و ابزارها و هر چیزی لازم بود رو براشون لیست کردم و در نهایت فهمیدم یک مگا پروژه که فقط دو سه تا از زیر پروژه هاش رو بگم: شبکه اجتماعی٬ مدیریت پروژه٬ خیریه و … بود رو با NestJS زدن!!! من از دل این مگا پروژه حداقل 8 پروژه بیرون کشیدم٬ حالا حرف اونها چی بود؟ این که چیزی نیست دو سه ماهه با دو سه نفر نیرو انجام بدید دیگه٬ بنده که عرض کردم شرمنده کار من نیست با این شرایط (آخه ادعا این بود که تیمی داریم که این کارو میکنن) عرض کردم بدید به همون تیم دیگه مصف مبلغ پیشنهادی من رو هم دارن به شما پیشنهاد میدن٬ و خروجی چی میشه؟ نرم افزار شاد٬ روبیکا٬ شمیم٬ برق من٬ …
یا در 99 درصد خدماتی که میگیریم یک لیست بلند بالا از یک سری عدد که کد خطا هست با یک سری متن فارسی که محتوای خطا هست به ما داده میشه٬ آقا این چه کاریه!؟ خطا داری؟ معلومه که نرم افزار یه جاهایی باید به خطا بخوره حتی عمدی٬ تو خطا رو Throw کن برای من و میخوای خیلی حرفه ای باشی از کلاس Exception ارث بری کن و فقط اسمی که برای خطا گذاشتی با توضیحش رو به من بده٬ هندل کردن و پیام متناسب دادن کار منه که اینور داستان کار میکنم نه اینکه تو هرچی دوست داری تو خطا بنویسی!
حرف آخرم
بعضی وقتا «درست ترین» کار، درست ترین کار نیست. باید ببینی طرف مقابلت (چه آدم باشه چه یه API) چه زبونی بلده، و باهاش همون جوری حرف بزنی که بفهمه. من رفتم کار رو طبق اصول مهندسی نرم افزار پیش بردم، ولی طرف حساب کتابش با من فرق داشت (یک درصد هم احتمال بدیم که مجبور بوده و کاری از دستش بر نمیومده و این بهترین انتخاب بوده٬ حداقل تو سندی که به عنوان راهنما میدی ذکر کن برادر/خواهر من).
من هم همه جا رو به DateTime.Nowبرگردوندم٬ یه کامنتم گذاشتم بالای اون تابع که «این عمدیه، لطفاً هوس نکن درستش کنی»، و رفتم سراغ کار بعدی.
خب همینم بود. یه باگ ساده که یه بعدازظهر ازم گرفت، ولی حداقل حالا می دونم اگه یه API فارسی دیدم که اسم فیلدش TimeStamp هست، اول باهاش مثل یه آدم مشکوک رفتار کنم، نه مثل یه استاندارد جهانی.
خلاصه ش اینکه٬ من سندی داشتم که توش نوشته بود برای دریافت اطلاعات و فیلتر بر اساس زمان باید Timstamp
What was the problem?