باید assertهات رو درست کنی
خلاصهٔ کاملتر
لوریس کرو (Loris Cro) در این مقاله به یه باور رایج حمله میکنه: اینکه «خاموشکردن assertها در پروداکشن یه تکنیک معموله». او میگه این کار درسته که رایجه، ولی یه روال بهشدت بده. بحث از اونجا شروع شد که در زبان Zig، تابع std.debug.assert چطور کار میکنه.
اول، یه assert یه واقعیت رو به برنامه اضافه میکنه؛ مثلاً «این آرگومان هیچوقت null نیست» یا «این عدد هیچوقت زوج نیست». assertها برای بیان صریح پیششرطها و ناورداها (invariants) به کار میرن و بهگفتهی نویسنده، اگه assert خوبی انتخاب کنی بهتر از یونیتتست از تو در برابر اشتباه محافظت میکنه — مخصوصاً وقتی کد رو fuzz کنی. شعار او اینه: «یه assert از هزار یونیتتست ارزشمندتره»:
assert(my_arg != null);
assert(my_num % 2 != 0);در Zig، assertها بر پایهی unreachable (مسیرهای نامعتبر کد) ساخته شدن. Zig حالتهای build مختلفی داره: حالتهای checked (مثل Debug و ReleaseSafe) موقع تریپشدن assert تضمین میکنن برنامه با panic کرش کنه، ولی حالتهای unchecked (مثل ReleaseFast) دچار «رفتار غیرقانونیِ کنترلنشده» میشن، یعنی برنامه بدرفتاری میکنه. در عوض، همین unchecked بودن کلی بهینهسازی قدرتمند رو ممکن میکنه که بازیهای ویدیویی و برنامههای بلادرنگ بهش تکیه دارن. یه نکتهی مهم اینه که برخلاف C/C++، assert در Zig ماکرو نیست بلکه یه تابع عادیه؛ پس آرگومانهاش همیشه ارزیابی میشن و میتونی بدون ترس عبارتهای دارای side-effect رو توش بذاری.
نویسنده میگه سه کار میشه با assert کرد: ۱) نگهداشتنش بهعنوان چک زماناجرا که موقع خطا panic میکنه؛ ۲) استفاده ازش برای بهینهسازی (به قیمت بدرفتاری اگه assert غلط باشه)؛ ۳) کاملاً خاموشکردنش. گزینهی سوم به نظر او جبرانناپذیر بده، چون بدترین حالتِ دو گزینهی دیگهست. خاموشکردن assert یعنی وقتی یه شرطِ فرضاًغیرممکن واقعاً اتفاق بیفته، برنامه بهجای کرش به کارش ادامه میده — یعنی زیر فرضهای غلط میچرخه، که خودش یه نوع بدرفتاریه. او یادآوری میکنه که نقض یه assert در زماناجرا، طبق تعریف انحراف از مشخصاته و میتونه برنامه رو به یه «ماشین عجیب» (weird machine) تبدیل کنه؛ مثل SQL injection که بدون نیاز به undefined behavior هم رخ میده.
ولی دلیل بزرگتر، چیزیه که او اسمش رو «خودفریبی» (gaslighting yourself) میذاره. اگه assertها رو در پروداکشن خاموش کنی، هیچوقت کشف نمیکنی که کدوم assertت غلطه، و همینطور کد بیشتری مینویسی که بهاشتباه روی همون assertهای غلط تکیه داره و مشکل رو بدتر میکنه. ممکنه یه نفر بعداً کدی زیر یه assertِ هیچوقتتریپنشده اضافه کنه و دقیقاً همونجا یه آسیبپذیریِ قابلبهرهبرداری وارد بشه بدون اینکه کسی بفهمه. جمعبندی نویسنده اینه که بسته به زمینه، گاهی ترجیح عملکرد بر ریسک درسته (مثل Ghostty که ReleaseFast منتشر میکنه) و گاهی باید assertها همیشه روشن بمونن (مثل دیتابیس مالی TigerBeetle)، ولی خاموشکردنِ همیشگیِ assert در پروداکشن از هر دو بدتره؛ راهی جز این نیست که assertهات رو درست کنی و دنبال درستیِ کامل برنامه باشی.
نکات کلیدی:
- assert یه واقعیت/پیششرط رو بیان میکنه و بهگفتهی نویسنده از هزار یونیتتست ارزشمندتره
- سه کار با assert: نگهداشتن بهعنوان چک، استفاده برای بهینهسازی، یا خاموشکردن؛ سومی بدترینه
- خاموشکردن assert یعنی برنامه زیر فرضهای غلط میچرخه و میتونه به آسیبپذیری منجر بشه
- مشکل اصلی «خودفریبی»ه: assertهای غلط رو هیچوقت کشف نمیکنی و کد بیشتری روشون بنا میکنی




