چرا باگها فقط در محیط production ظاهر میشوند

در این مقاله میخوانید
- ۱. دادهٔ واقعی با دادهٔ تست فرق دارد
- ۲. مقیاس رفتار را عوض میکند
- ۳. شبکهٔ واقعی کند، ناپایدار و غیرقابل پیشبینی است
- ۴. دستگاه و مرورگر واقعی
- ۵. کد production همان کد شما نیست
- ۶. کاربران کارهایی میکنند که شما نمیکنید
- ۷. زمان و مکان هم متغیرند
- یک مثال واقعی از این فاصله
- چطور فاصله را کم کنیم
- چطور بفهمیم چقدر از این فاصله باقی مانده
- جمعبندی
- منابع و مطالعهٔ بیشتر
جملهٔ «روی سیستم من که کار میکند» آنقدر تکرار شده که به شوخی تبدیل شده. ولی پشتش یک واقعیت جدی هست: محیط توسعه و محیط production دو دنیای متفاوتاند، و بیشتر باگها دقیقاً در فاصلهٔ بین این دو زندگی میکنند.
این مقاله دربارهٔ آن فاصله است — چه چیزهایی واقعاً فرق میکنند، و چطور میشود کوچکش کرد. برای تعریف دقیق باگ و انواعش، باگ نرمافزاری چیست؟ را ببینید.
۱. دادهٔ واقعی با دادهٔ تست فرق دارد
مهمترین دلیل، و معمولاً کمتوجهترین. دادهٔ تست شما تمیز، کامل و معقول است. دادهٔ واقعی اینطور نیست:
- کاربری که نام خانوادگی ندارد.
- محصولی با عنوان ۳۰۰ کاراکتری.
- حسابی که هفت سال است ساخته شده و ساختار قدیمی دارد.
- شماره تلفنی که با فاصله و خط تیره ذخیره شده.
- کاربری که هیچ سفارشی ندارد — حالتی که در دیتابیس تست شما اصلاً وجود ندارد.
هر فرضی که دربارهٔ شکل داده کردهاید، در production توسط کاربری نقض میشود. این تنها جایی است که دادهٔ واقعی، بیرحمانه، همهٔ فرضیات نانوشتهٔ کد شما را آزمایش میکند. رایجترین نشانهاش در جاوااسکریپت، خطای Cannot read properties of undefined است.
۲. مقیاس رفتار را عوض میکند
کدی که با ۱۰ رکورد کار میکند، ممکن است با ۱۰ هزار رکورد فقط کند شود — یا کاملاً بشکند. حلقهای تودرتو که در تست نامحسوس است، در production چند ثانیه مرورگر را قفل میکند.
و مهمتر از کندی: همزمانی. در محیط توسعه شما تنها کاربر هستید. در production دهها نفر همزمان همان عملیات را انجام میدهند و شرایطی پیش میآید که هرگز روی دستگاه شما ممکن نبود — دو درخواست همزمان روی یک رکورد، یا دو تب باز از یک کاربر.
۳. شبکهٔ واقعی کند، ناپایدار و غیرقابل پیشبینی است
روی دستگاه شما، سرور روی localhost است و پاسخ در چند میلیثانیه میآید. برای کاربر واقعی:
- پاسخ ممکن است چند ثانیه طول بکشد.
- ممکن است اصلاً نیاید.
- ممکن است نیمهکاره قطع شود.
- ترتیب رسیدن دو درخواست همزمان تضمینشده نیست.
این آخری منبع دستهای از باگهاست که فقط در production دیده میشوند: کدی که فرض کرده درخواست اول همیشه زودتر از دوم برمیگردد. روی شبکهٔ سریع محلی، این فرض تقریباً همیشه درست است. روی شبکهٔ موبایل، نه.
راه عملی برای دیدن اینها پیش از کاربر: در ابزار توسعهٔ مرورگر، سرعت شبکه را روی حالت کند تنظیم کنید و همان مسیرها را دوباره طی کنید. بسیاری از این باگها بلافاصله خودشان را نشان میدهند.
۴. دستگاه و مرورگر واقعی
شما روی یک لپتاپ قوی، با آخرین نسخهٔ کروم، روی صفحهٔ بزرگ کار میکنید. کاربران شما اینطور نیستند. گوشیهای میانرده با حافظهٔ محدود، مرورگرهایی که چند نسخه عقباند، صفحههای کوچک، و — نکتهای که اغلب فراموش میشود — افزونههای مرورگر که DOM صفحه را دستکاری میکنند و گاهی مستقیماً با کد شما تداخل پیدا میکنند.
در بازار ایران یک عامل اضافه هم هست: بخشی از کاربران از طریق پروکسی یا تحریمشکن به سایت وصل میشوند، که هم تأخیر را بالا میبرد و هم گاهی درخواستها را بههم میریزد.
۵. کد production همان کد شما نیست
کدی که منتشر میشود فشرده، ترکیبشده و بهینهشده است. در بیشتر موارد این فرآیند بیخطر است، ولی نه همیشه:
- نام متغیرها و توابع تغییر میکنند — کدی که به نام تابع متکی است میشکند.
- متغیرهای محیطی متفاوتاند؛ آدرسی که در توسعه درست بود، در production نیست.
- حالت
developmentفریمورکها هشدارهای مفیدی میدهد که در نسخهٔ production حذف میشوند. - کش مرورگر و CDN باعث میشوند بعضی کاربران نسخهٔ قدیمی فایل را داشته باشند و ترکیبی از نسخهٔ جدید و قدیم اجرا شود.
این آخری علت بسیاری از باگهای «عجیب» بعد از انتشار است: کاربر HTML جدید را گرفته ولی جاوااسکریپت قدیمی هنوز در کشش است.
۶. کاربران کارهایی میکنند که شما نمیکنید
شما مسیر درست را میروید، چون خودتان طراحیاش کردهاید. کاربران:
- روی دکمه سه بار پشت سر هم میزنند.
- وسط فرم دکمهٔ بازگشت مرورگر را میزنند.
- لینک را در تب جدید باز میکنند و در هر دو تب کار میکنند.
- صفحه را یک هفته باز میگذارند و بعد ادامه میدهند — با توکنی که منقضی شده.
- متن را از جای دیگری کپی میکنند، همراه با کاراکترهای نامرئی.
هیچکدام از اینها در تستهای شما نیست، چون تستها را کسی نوشته که میداند سیستم چطور کار میکند.
۷. زمان و مکان هم متغیرند
عاملی که کمتر به آن فکر میشود و باگهای عجیبی میسازد.
منطقهٔ زمانی. سرور شما احتمالاً روی UTC است، مرورگر کاربر روی وقت تهران. تاریخی که ذخیره میشود و تاریخی که نمایش داده میشود میتوانند یک روز فرق کنند — و این تفاوت فقط برای رویدادهایی که نزدیک نیمهشب رخ میدهند خودش را نشان میدهد، یعنی حدود چهار درصد مواقع. بهاندازهای نادر که در تست دیده نشود و بهاندازهای رایج که کاربران متوجه شوند.
تقویم. در محصولات فارسی، تبدیل تاریخ شمسی و میلادی منبع همیشگی باگ است — مخصوصاً در سال کبیسه و در روزهای ابتدا و انتهای ماه.
گذشت زمان. کدی که امروز کار میکند ممکن است فردا نکند: توکنی که منقضی میشود، گواهی SSL که تمام میشود، یا کشی که پر میشود. این خانواده از باگها در هیچ تستی دیده نمیشوند چون تستها در یک لحظه اجرا میشوند و بعد تمام.
یک مثال واقعی از این فاصله
سناریویی که در عمل بارها تکرار میشود و هر شش عامل بالا را کنار هم نشان میدهد:
تیمی صفحهٔ تسویهحساب را بازطراحی میکند. همهچیز روی سیستم توسعهدهندهها کار میکند، در staging هم تأیید میشود، و منتشر میشود. سه روز بعد پشتیبانی میگوید «چند نفر میگویند دکمهٔ پرداخت کار نمیکند» — ولی وقتی خودشان تست میکنند، کار میکند.
ماجرا این بوده: کد جدید فرض کرده بود آدرس کاربر همیشه استان دارد. کاربرانی که حسابشان را چند سال پیش ساخته بودند، در ساختار قدیمی آدرس، فیلد استان نداشتند. برای آنها متغیر undefined میشد، خطا رخ میداد، و دکمه بیاثر میماند — بدون هیچ پیامی روی صفحه.
چرا در تست دیده نشد؟ چون دیتابیس تست، دادههای تازهساخته داشت. چرا کاربران زیادی گزارش ندادند؟ چون بیشترشان فرض کردند مشکل از اینترنتشان است و بعداً دوباره امتحان میکنند. و چرا سه روز طول کشید؟ چون هیچ سیستمی آن خطا را ثبت نمیکرد.
هر سه سؤال یک نتیجه دارند: مشکل نه در کیفیت کد بود و نه در دقت تیم. مشکل در فاصلهٔ بین وقوع و اطلاع بود.
چطور فاصله را کم کنیم
محیط staging نزدیک به واقعیت بسازید. اگر staging شما با دیتابیس خالی و شبکهٔ محلی کار میکند، چیز زیادی اثبات نمیکند. کپی بینامشدهای از دادهٔ واقعی، بهمراتب مفیدتر است.
با حالتهای مرزی تست کنید. کاربر جدید بدون داده، فهرست خالی، متن خیلی بلند، عدد صفر، و شبکهٔ کند.
انتشار تدریجی کنید. اگر نسخهٔ جدید را اول برای درصد کوچکی از کاربران منتشر کنید، باگ را با ۵ درصد کاربران کشف میکنید نه با ۱۰۰ درصد.
Source map تولید کنید، وگرنه هر خطایی که در production ببینید غیرقابل ردیابی است.
و مهمترین: بپذیرید که این فاصله هرگز صفر نمیشود. هیچ محیط تستی نمیتواند تنوع دادهٔ واقعی، شبکهٔ واقعی و رفتار انسانهای واقعی را بازسازی کند. پس بهجای تلاش برای حذف کامل باگهای production، روی کوتاه کردن زمان کشفشان سرمایهگذاری کنید.
چطور بفهمیم چقدر از این فاصله باقی مانده
وقتی پذیرفتید که فاصله صفر نمیشود، سؤال بعدی این است که چطور اندازهاش بگیرید. سه عدد کافی است:
- فاصلهٔ انتشار تا کشف. باگ چند ساعت یا چند روز پس از انتشار دیده شد؟ اگر این عدد بزرگ است، مشکل شما کیفیت کد نیست — دیدهبانی است.
- سهم باگهایی که کاربر گزارششان کرده، نه سیستم. اگر بیشتر باگها از طریق پشتیبانی میآیند، یعنی سیستم رصدتان یا وجود ندارد یا نویزش آنقدر زیاد است که کسی نگاهش نمیکند.
- درصد کاربرانی که دستکم یک خطا دیدهاند. صادقانهترین معیار سلامت محصول، و معمولاً عددی که تیمها را غافلگیر میکند.
هر سه عدد یک چیز مشترک دارند: هیچکدام را بدون ثبت خودکار خطا نمیتوانید حساب کنید. تا وقتی تنها منبع اطلاعتان تماس کاربر عصبانی است، حتی نمیدانید در چه وضعیتی هستید.
جمعبندی
باگها «فقط در production ظاهر نمیشوند» — آنها همیشه در کد بودهاند و production تنها جایی است که شرایط لازم برای بروزشان فراهم میشود: دادهٔ نامنتظر، شبکهٔ بد، دستگاه ضعیف، و کاربری که مسیر پیشبینینشدهای میرود.
تیمی که این را پذیرفته، انرژیاش را صرف تلاش برای انتشار بینقص نمیکند؛ صرف این میکند که وقتی باگ بروز کرد، ظرف چند دقیقه ببیندش — نه چند هفته بعد، از زبان کاربری عصبانی.
منابع و مطالعهٔ بیشتر
- Dev/prod parity — The Twelve-Factor App — اصل نزدیک نگه داشتن محیط توسعه و production
- مرجع زبانهٔ Network در Chrome DevTools — از جمله شبیهسازی شبکهٔ کند
- Canary Release — مارتین فاولر — انتشار تدریجی برای بخش کوچکی از کاربران
باگماگ را رایگان امتحان کنید
خطاهای سایتتان را خودکار ثبت کنید و بگذارید کاربران با یک کلیک باگ گزارش دهند.
شروع رایگان