ڈیپلائمنٹ کے بعد پروڈکشن میں نظر آنے والی مفید ترین غلطیوں میں سے ایک بظاہر بہت معمولی تھی:
Failed to load script:
/_next/static/chunks/9253.647385b4be0958e4.js
یہ اسی سلسلے میں آئی جس میں تجزیاتی سروسز کی ناکامیاں، اشتہاری اسکرپٹس، عمومی Script error. پیغامات اور ویڈیو چلنے میں رکاوٹیں بھی تھیں۔ ان میں سے زیادہ تر محض شور تھا۔ یہ واقعہ مختلف تھا، کیونکہ جو وسیلہ لوڈ نہیں ہوا وہ میرے اپنے Next.js ایپ کا حصہ تھا۔ اگر براؤزر واقعی اسے حاصل نہ کر پاتا تو صفحے کا کوئی حصہ کام کرنا بند کر سکتا تھا۔
لیکن اس لاگ سے مجھے یہ معلوم نہیں ہوا کہ چنک کیوں ناکام ہوا۔ عارضی نیٹ ورک مسئلہ ہو سکتا تھا، پراکسی یا CDN ناکام ہو سکتا تھا، فائل واقعی غائب ہو سکتی تھی، یا کوئی پرانا صفحہ پچھلی ڈیپلائمنٹ کا چنک مانگ رہا ہو جبکہ سرور پہلے ہی اس بلڈ کو بدل چکا ہو۔
آخری صورت کو کم اہم سمجھ لینا آسان ہے، کیونکہ نئی ڈیپلائمنٹ خود مکمل طور پر درست ہو سکتی ہے۔ ہر نیا صارف نیا ورژن صحیح طور پر لوڈ کر سکتا ہے، جبکہ گھنٹوں سے کھلا ٹیب خاموشی سے پرانا ورژن کا کلائنٹ بنا رہتا ہے۔
یہ مضمون اسی مطابقتی خلا کے بارے میں ہے: ڈیپلائمنٹ کے بعد پرانے Next.js ٹیب کیوں ٹوٹ سکتے ہیں، باسی HTML اور غائب /_next/static وسائل ورژنز میں عدم مطابقت کیسے پیدا کرتے ہیں، حد سے زیادہ صفائی مسئلہ کیوں بڑھاتی ہے، اور میں ڈیپلائمنٹ، وسائل محفوظ رکھنے، نگرانی اور بحالی کو کیسے ترتیب دوں گا تاکہ کامیاب ریلیز پہلے سے ایپ کھولے ہوئے صارفین کو خراب صفحے کے ساتھ نہ چھوڑے۔
پہلا سبق: ہر اسکرپٹ ناکامی کو ڈیپلائمنٹ کی خرابی نہ سمجھیں
اصل غلطیوں کے سلسلے میں بالکل مختلف نوعیت کی ناکامیاں اکٹھی تھیں۔ تیسرے فریق کے تجزیاتی اور اشتہاری اسکرپٹس مواد روکنے والے اوزار، DNS فلٹر، رازداری کی خصوصیات، علاقائی فلٹرنگ، اینٹی وائرس یا صارف کے نیٹ ورک کی وجہ سے بلاک ہو سکتے ہیں۔ ویڈیو کا play() وعدہ بعد میں آنے والی pause() کال سے منقطع ہو سکتا ہے، حالانکہ ایپ خود درست ہو۔ عمومی مختلف اصل سے آنے والا Script error. اکثر تشخیص کے لیے بہت کم معلومات دیتا ہے۔
لیکن اپنے Next.js ایپ کا چنک لوڈ نہ ہونا الگ ترجیح رکھتا ہے۔ مفید حد یہ نہیں کہ “JavaScript میں خرابی ہے یا نہیں”، بلکہ تقریباً یہ ہے:
تیسرے فریق کا وسیلہ ناکام
-> عموماً نگرانی یا اختیاری فعالیت متاثر
اپنا /_next/static/*.js ناکام
-> ایپ کا ضروری کوڈ دستیاب نہ ہو سکتا ہے
یہ فرق اس لیے اہم ہے کہ حد سے زیادہ شور پیدا کرنے والا رپورٹر ان ناکامیوں کو چھپا سکتا ہے جو واقعی خراب صفحات سے جڑی ہوں۔ میرے معاملے میں اہم واقعہ /_next/static/chunks/9253.647385b4be0958e4.js کی درخواست تھی۔ لاگ نے ثابت کیا کہ اپنے نظام کا اسکرپٹ لوڈ نہیں ہوا، لیکن یہ ثابت نہیں کیا کہ وجہ ڈیپلائمنٹ کے بعد ورژنز میں عدم مطابقت تھی۔
میں ثبوت کی یہ حد جان بوجھ کر واضح رکھتا ہوں: ممکنہ وجہ، ثابت شدہ وجہ نہیں ہوتی۔
کھلا ہوا براؤزر ٹیب عملاً پرانی ریلیز کا کلائنٹ ہوتا ہے
جس ذہنی تصور نے میرے لیے مسئلہ واضح کیا وہ سادہ ہے: ڈیپلائمنٹ کے بعد ایک ہی وقت میں ایپ کی ایک سے زیادہ ورژنز فعال رہ سکتی ہیں۔
فرض کریں ریلیز A صبح 10:00 بجے فعال ہے۔ صارف صفحہ کھولتا ہے اور اس راستے کے لیے درکار HTML اور JavaScript حاصل کرتا ہے۔ 10:30 پر ریلیز B اسے بدل دیتی ہے۔ نئے صارفین کو B ملتی ہے، لیکن پرانا ٹیب صرف سرور بدل جانے سے خود بخود B نہیں بن جاتا۔
اس ٹیب میں اب بھی یہ چیزیں ہو سکتی ہیں:
- ریلیز A سے لوڈ ہوا JavaScript رن ٹائم؛
- ریلیز A کے بنائے ہوئے راستوں اور چنکس کے حوالے؛
- ریلیز A میں پہلے سے حاصل کیا گیا نیویگیشن ڈیٹا؛
- ریلیز A کے دوران بنی React حالت؛
- A کے وہ تقسیم شدہ ماڈیول جو پہلے ہی ڈاؤن لوڈ ہو چکے ہیں؛
- A کے ایسے ماڈیولز کے حوالے جو ابھی ڈاؤن لوڈ نہیں ہوئے۔
آخری نکتہ وہ جگہ ہے جہاں خرابی سامنے آتی ہے۔
اگر صفحے کو مستقبل میں درکار ہر چنک پہلے ہی براؤزر کیش میں موجود ہو تو صارف شاید کچھ محسوس کیے بغیر کام جاری رکھے۔ لیکن جدید Next.js ایپس کوڈ تقسیم کرتی ہیں۔ راستہ بدلنا، dynamic import، کوئی موڈل، ایڈیٹر یا بعد میں استعمال ہونے والی خصوصیت ایک نئی JavaScript فائل مانگ سکتی ہے۔ تب پرانا رن ٹائم ایسے وسیلے کا URL مانگتا ہے جو ریلیز A میں درست تھا۔
اگر سرور کے پاس وہ وسیلہ اب بھی موجود ہے تو سب کچھ چل سکتا ہے۔ اگر ڈیپلائمنٹ نے اسے حذف کر دیا تو پرانا کلائنٹ 404 حاصل کر سکتا ہے، چاہے ریلیز B خود بالکل درست ہو۔
مواد کے ہیش والے چنکس طویل مدت تک کیش ہونے کے لیے بنائے جاتے ہیں
Next.js واقعی ناقابلِ تبدیلی وسائل کے لیے جان بوجھ کر طویل مدتی کیشنگ استعمال کرتا ہے۔ اس کی موجودہ ذاتی میزبانی دستاویزات کے مطابق فائل نام میں SHA ہیش رکھنے والے ناقابلِ تبدیلی وسائل ایک سال جیسی پالیسی کے ساتھ پیش کیے جاتے ہیں:
Cache-Control: public, max-age=31536000, immutable
یہ پالیسی معقول ہے، کیونکہ مواد بدلنے پر URL بھی بدل جاتا ہے۔ مواد سے اخذ کیے گئے ہیش والی فائل کو ہر درخواست پر دوبارہ جانچنے کی ضرورت نہیں۔ اگر اگلا بلڈ مختلف بائٹس بناتا ہے تو اسے مختلف وسیلہ URL بنانا چاہیے۔
اہم نتیجہ آسانی سے نظر انداز ہو جاتا ہے: پرانا URL اس وقت تک معنی رکھتا ہے جب تک کوئی پرانا دستاویز یا رن ٹائم اس کا حوالہ دے سکتا ہے۔
براؤزر کا ہیش شدہ وسیلہ ایک سال تک کیش کر سکنا اس وقت مدد نہیں کرتا جب اس نے مخصوص فائل ڈیپلائمنٹ سے پہلے کبھی ڈاؤن لوڈ ہی نہ کی ہو اور اصل سرور اسے پہلے ہی حذف کر چکا ہو۔
اسی لیے “ہماری جامد فائلیں ناقابلِ تبدیلی ہیں” اور “ہم پچھلی ریلیز کی جامد فائلیں فوراً حذف کر سکتے ہیں” ایک ہی بات نہیں۔ ناقابلِ تبدیلی ہونا پرانے وسائل کو محفوظ رکھنا بے ضرر بناتا ہے؛ یہ پرانے کلائنٹس کو انہیں مانگنے سے نہیں روکتا۔
موجودہ Next.js ذاتی میزبانی رہنما واضح طور پر بتاتا ہے کہ کئی سرورز یا مرحلہ وار ڈیپلائمنٹ کے دوران JavaScript یا CSS وسائل کا غائب ہونا ورژنز میں عدم مطابقت کی ایک علامت ہو سکتا ہے۔ یہی مسئلہ اس وقت بھی پیدا ہو سکتا ہے جب اختلاف دو بیک وقت فعال سرورز کے بجائے پرانے ٹیب اور نئی ڈیپلائمنٹ والے اصل سرور کے درمیان ہو۔
ورژنز کئی مختلف طریقوں سے الگ ہو سکتی ہیں
“کیش کا مسئلہ” تشخیص کے لیے بہت مبہم عبارت ہے۔ میں کم از کم چار الگ طریقے دیکھتا ہوں، کیونکہ ہر ایک کا حل مختلف ہے۔
1. پرانا ٹیب ایسا وسیلہ مانگتا ہے جو ڈیپلائمنٹ سے پہلے لوڈ نہیں ہوا تھا
یہ طویل عرصے تک کھلے ٹیب کی کلاسیکی صورت ہے۔ دستاویز اور رن ٹائم ریلیز A سے آئے۔ ریلیز B سرور کی فائلیں بدل دیتی ہے۔ بعد میں صارف کوئی ایسا عمل کرتا ہے جو A کے سست لوڈ ہونے والے چنک کو چلاتا ہے۔ اگر A کا وسیلہ حذف ہو چکا ہو تو درخواست ناکام ہو جاتی ہے۔
2. باسی HTML ایسے چنکس کی طرف اشارہ کرتا ہے جو اب موجود نہیں
CDN، ریورس پراکسی، سروس ورکر، براؤزر کیش یا جامد ہوسٹنگ کی تہہ توقع سے زیادہ دیر تک پرانا HTML رکھ سکتی ہے۔ اس HTML میں ریلیز A کے حوالے ہو سکتے ہیں، جبکہ اصل سرور پر صرف B موجود ہو۔
یہ صورت خاص طور پر خطرناک ہے جب HTML کو غلطی سے طویل immutable پالیسی دے دی جائے۔ ہیش شدہ JavaScript اور HTML کو ایک ہی قسم کا کیش شے نہیں سمجھنا چاہیے۔ چنک ناقابلِ تبدیلی ہو سکتا ہے کیونکہ اس کا URL مواد سے ورژن ہوتا ہے؛ HTML طے کرتا ہے کہ کون سے چنک URLs ایک دوسرے کے ساتھ تعلق رکھتے ہیں۔
3. مرحلہ وار یا کثیر مثالی ڈیپلائمنٹ مختلف ریلیزز اکٹھی پیش کرتی ہے
لوڈ بیلنسر کے پیچھے Next.js کی دو مثالیں تصور کریں۔ ایک پہلے ہی ریلیز B پر ہے اور دوسری ابھی A پر۔ دستاویز ایک ریلیز سے آ سکتی ہے، جبکہ بعد کی نیویگیشن درخواست دوسری ریلیز تک پہنچ سکتی ہے۔ موجودہ Next.js دستاویزات اسے ورژنز میں عدم مطابقت کہتی ہیں اور بتاتی ہیں کہ اس سے وسائل غائب ہو سکتے ہیں، سرور افعال میں عدم مطابقت پیدا ہو سکتی ہے اور نیویگیشن ناکام ہو سکتی ہے۔
محفوظ عمومی طریقہ یہ ہے کہ ایک بار بلڈ کیا جائے اور ایک ہی ڈیپلائمنٹ میں شامل ہر مثال پر وہی بلڈ آرٹیفیکٹ چلایا جائے۔ Next.js کی ذاتی میزبانی دستاویزات بھی ہر نقل کو الگ الگ بلڈ کرنے کے بجائے تمام کنٹینرز میں ایک ہی بلڈ اور یکساں بلڈ شناخت استعمال کرنے کی سفارش کرتی ہیں۔
4. ڈیپلائمنٹ خود غلط ترتیب سے شائع ہوتی ہے
طویل عرصے سے کھلے ٹیب نہ بھی ہوں تو غیر اٹامک اپ لوڈ عارضی طور پر ناممکن حالت بنا سکتا ہے:
نیا HTML دکھائی دے رہا ہے
+
نئی چنک فائلیں ابھی دستیاب نہیں
یا اس کے برعکس:
پرانا HTML اب بھی دستیاب ہے
+
پرانے چنک پہلے ہی حذف ہو چکے ہیں
چند لمحوں کی کھڑکی بھی کافی ہے۔ صارف کو صرف ایک بار اسی وقت پہنچنا ہوتا ہے۔
خطرناک ڈیپلائمنٹ طریقہ: “سب کچھ بدل دو اور پرانی شاخ حذف کر دو”
سادہ ڈیپلائمنٹ اسکرپٹ اکثر کچھ اس طرح شروع ہوتا ہے:
build
rsync --delete new-output/ production/
restart
یہ اس لیے دلکش ہے کہ پروڈکشن ڈائریکٹری ہمیشہ تازہ ترین بلڈ سے بالکل میل کھاتی ہے۔ لیکن طویل عرصے سے کھلے کلائنٹس کے لیے یہ نقصان دہ ہے۔
ہیش شدہ جامد وسائل کے ساتھ ڈائریکٹری کو صرف ایک ریلیز تک صاف رکھنے سے براؤزر کو بہت کم فائدہ ہوتا ہے۔ پرانی فائلیں نئی فائلوں سے نہیں ٹکراتیں، کیونکہ ان کے URLs مختلف ہوتے ہیں۔ انہیں حذف کرنے کا بنیادی فائدہ ڈسک کی جگہ بچانا ہے، مگر اس کے بدلے پرانے کلائنٹ کا ہر اب بھی درست حوالہ ممکنہ 404 بن جاتا ہے۔
اب میں پرانے چنکس کو کچرا نہیں بلکہ ڈیپلائمنٹ مطابقت کا مواد سمجھتا ہوں۔
اس کا مطلب ہر بلڈ ہمیشہ کے لیے رکھنا نہیں۔ مطلب صرف یہ ہے کہ صفائی ایک الگ محفوظ رکھنے کی مدت پالیسی ہونی چاہیے، نئی ریلیز شائع کرنے کا اتفاقی ضمنی اثر نہیں۔
پرانے وسائل محفوظ رکھنا مفید ہے، مگر کوئی محدود مدت مکمل حل نہیں
عملی ذاتی میزبانی والا نظام میں پرانے /_next/static وسائل کچھ رعایتی مدت تک رکھے جا سکتے ہیں۔ درست مدت استعمال کے انداز پر منحصر ہے۔ ایسا صفحہ جسے لوگ دو منٹ پڑھ کر بند کر دیتے ہیں، اس ایپ سے مختلف خطرہ رکھتا ہے جو پورا دن کھلی رہتی ہے۔
کم از کم محفوظ رکھنے کی مدت مدت کے بارے میں سوچنے کا ایک مفید طریقہ یہ ہے:
محفوظ رکھنے کی مدت >=
HTML کے باسی رہنے کی متوقع مدت
+ طویل عرصے تک کھلے ٹیب کی حقیقت پسندانہ مدت
+ پچھلی ریلیز پر واپسی کی مدت
+ ڈیپلائمنٹ کے پھیلاؤ کا اضافی وقت
یہ ریاضیاتی ضمانت نہیں۔ براؤزر ٹیب ہفتوں تک کھلا رہ سکتا ہے۔ گھنٹوں کی کوئی محدود تعداد پرانے کلائنٹس کی ناکامی کو ناممکن نہیں بناتی۔
اسی لیے میں تہہ دار ڈیزائن کو ترجیح دیتا ہوں:
- پچھلے ناقابلِ تبدیلی وسائل اتنی دیر رکھیں کہ معمول کی پرانی نشستیں چلتی رہیں؛
- ورژنز میں عدم مطابقت پہچانیں تاکہ کلائنٹ موجودہ ریلیز پر جا سکے؛
- اگر وسیلہ واقعی دستیاب نہ ہو تو محفوظ، صرف ایک بار ہونے والی دوبارہ لوڈنگ یا صارف کو واضح بحالی کا راستہ دیں؛
- اپنے غائب چنکس کی نگرانی کریں تاکہ محفوظ رکھنے کی مدت مدت حقیقی شواہد کے مطابق طے کی جا سکے۔
وسائل محفوظ رکھنے والی تہہ زیادہ تر ناکامیاں روکتی ہے۔ بحالی کی تہہ ان باقی نایاب حالات کو سنبھالتی ہے جنہیں کوئی محدود محفوظ رکھنے کی مدت مدت مکمل طور پر ختم نہیں کر سکتی۔
پرانے چنکس کو صرف عمر دیکھ کر حذف نہ کریں
“سات دن سے پرانی ہر فائل حذف کر دو” جیسی سادہ صفائی بھی غلط ہو سکتی ہے۔ موجودہ ریلیز کسی پرانی ہیش شدہ فائل کو دوبارہ استعمال کر سکتی ہے جس کا ترمیمی وقت پرانا ہے، کیونکہ اس کا مواد بدلا ہی نہیں۔
زیادہ مضبوط صفائی وہ ہے جو ریلیز کو سمجھتی ہو:
- مطابقتی مدت کے اندر آنے والی ہر ریلیز کے وسائل کی فہرستیں یا وسائل کی فہرستیں محفوظ رکھیں؛
- ان ریلیزز کے تمام حوالہ شدہ وسائل کے راستوں کا مشترک مجموعہ بنائیں؛
- اس محفوظ مجموعے میں موجود کچھ بھی حذف نہ کریں؛
- بغیر حوالہ وسائل کو بھی ایک اضافی رعایتی مدت کے بعد ہی حذف کریں۔
اگر چھوٹی ڈیپلائمنٹ کے لیے یہ نظام حد سے زیادہ پیچیدہ ہے تو جان بوجھ کر کچھ بڑا جامد وسائل کا فولڈر رکھنا اکثر نایاب کلائنٹ خرابی ڈھونڈنے سے سستا ہے۔ ہیش شدہ فائلیں اس کے لیے موزوں ہیں، کیونکہ یکساں مواد قدرتی طور پر مستحکم URLs دوبارہ استعمال کرتا ہے، یا کم از کم ایک ہی ہیش شدہ نام کے نیچے غیر متعلق مواد کو نہیں مٹا سکتا۔
جس اصول سے میں بچتا ہوں وہ سادہ ہے: مشترک /_next/static شاخ پر --delete کو اسی کارروائی کا حصہ نہ بنائیں جو نئی ریلیز کو فعال کرتی ہے۔
Next.js میں اب ورژنز میں عدم مطابقت سے واضح تحفظ ہے، مگر یہ پرانے وسائل کا ذخیرہ نہیں
موجودہ Next.js ورژنز میں عدم مطابقت سے تحفظ کے لیے deploymentId کی حمایت کرتا ہے۔ ترتیب کچھ یوں ہو سکتی ہے:
// next.config.js
const nextConfig = {
deploymentId: process.env.DEPLOYMENT_VERSION,
}
module.exports = nextConfig
موجودہ Next.js کی deploymentId دستاویزات کے مطابق اسے ترتیب دینے سے فریم ورک کے زیرِ انتظام جامد وسائل کے URLs میں ?dpl=<deploymentId> شامل ہوتا ہے، کلائنٹ کی نیویگیشن درخواستیں ڈیپلائمنٹ کی معلومات لے جاتی ہیں، اور سرور جواب میں اپنی ڈیپلائمنٹ شناخت بتاتا ہے۔ نیویگیشن کے دوران Next.js کو عدم مطابقت ملے تو وہ غیر موافق ڈیٹا کے ساتھ نرم نیویگیشن جاری رکھنے کے بجائے مکمل نیویگیشن کر سکتا ہے۔
?dpl=<deploymentId>
x-deployment-id
x-nextjs-deployment-id
data-dpl-id
یہ مفید ہے، مگر اس خصوصیت کو اس سے زیادہ طاقت نہیں دینی چاہیے جتنی حقیقت میں ہے۔ دستاویزات صاف کہتی ہیں کہ Next.js آنے والے ?dpl= پیرامیٹر کو ورژن کے مطابق درخواست کو کسی پرانی سرور ورژن تک پہنچانے کے لیے استعمال نہیں کرتا۔ یہ پیرامیٹر کیش توڑنے کے لیے ہے۔ اگر ذاتی میزبانی والا اصل سرور پرانی فائل جسمانی طور پر حذف کر چکا ہے تو استعلامی پیرامیٹر اسے دوبارہ پیدا نہیں کرتا۔
اس لیے میں deploymentId کو عدم مطابقت کی شناخت اور بحالی کا ذریعہ سمجھتا ہوں، درست ڈیپلائمنٹ نظم یا پرانے وسائل محفوظ رکھنے کا متبادل نہیں۔
ایسے پلیٹ فارم جو ڈیپلائمنٹ سے آگاہ راستہ بندی دیتے ہیں، مزید آگے جا سکتے ہیں۔ مثال کے طور پر Vercel کی ورژن عدم مطابقت سے تحفظ کی دستاویزات ورژن تثبیت بیان کرتی ہیں، جس سے فریم ورک کی درخواستیں اسی ڈیپلائمنٹ تک پہنچ سکتی ہیں جس نے کلائنٹ کو خدمت دی تھی۔ یہ پلیٹ فارم کی صلاحیت ہے؛ میں عام Nginx یا CDN سیٹ اپ میں اسے خود بخود موجود نہیں سمجھتا۔
بلڈ شناخت اور ڈیپلائمنٹ شناخت متعلقہ مگر مختلف مسائل حل کرتے ہیں
Next.js، next build کے دوران بلڈ شناخت بھی بناتا ہے۔ اگر کئی کنٹینرز ایک ہی ڈیپلائمنٹ پیش کرنے والے ہیں تو صرف اس وجہ سے خاموشی سے مختلف بلڈز نہیں بننے چاہییں کہ ہر سرور نے اپنا الگ بلڈ چلایا۔
متعین بلڈ شناخت کو کسی ریلیز شناخت، مثلاً Git کمٹ، سے جوڑا جا سکتا ہے:
// next.config.js
const nextConfig = {
generateBuildId: async () => process.env.GIT_SHA,
deploymentId: process.env.GIT_SHA,
}
module.exports = nextConfig
یہ مثال صرف وضاحت کے لیے ہے، میرے پروڈکشن کوڈ سے نقل نہیں۔ بنیادی معماری اصول یہ ہے کہ ایک منطقی ریلیز کا ایک ہی مربوط بلڈ آرٹیفیکٹ اور ایک ہی ڈیپلائمنٹ شناخت ہونی چاہیے، تمام پیش کرنے والے مثالات پر۔
generateBuildId Next.js بلڈ کی شناخت کرتا ہے۔ deploymentId خاص طور پر ورژنز میں عدم مطابقت سے تحفظ اور کیش توڑنے کے لیے دستاویزی ہے۔ دونوں متعلق ہیں، مگر ناموں کو مترادف سمجھنا تشخیص مشکل بناتا ہے۔
میں نئی دستاویز پر ٹریفک منتقل کرنے سے پہلے وسائل شائع کروں گا
زیادہ محفوظ ڈیپلائمنٹ ترتیب جان بوجھ کر غیر متوازن ہے۔ نئے ناقابلِ تبدیلی وسائل کسی کے حوالہ دینے سے پہلے موجود ہو سکتے ہیں۔ نیا HTML ایسے وسائل کا حوالہ نہیں دینا چاہیے جو ابھی دستیاب نہ ہوں۔
تصوری طور پر میں یہ ترتیب چاہتا ہوں:
1. ریلیز B کو ایک بار بلڈ کریں
2. B کے /_next/static وسائل اپ لوڈ کریں
3. تصدیق کریں کہ مطلوبہ وسائل واقعی حاصل کیے جا سکتے ہیں
4. B کا سرور/رن ٹائم شروع یا تیار کریں
5. B کا صحت جانچیں
6. نئی دستاویزات کی ٹریفک اٹامک طریقے سے B پر منتقل کریں
7. A کے جامد وسائل دستیاب رکھیں
8. B کی نگرانی کریں
9. پرانے وسائل بعد میں صاف کریں
اگر ایپ جامد برآمد ہے تو یہی اصول لاگو ہوتا ہے: پہلے ورژن شدہ وسائل اپ لوڈ کریں، پھر وہ HTML شائع کریں جو انہیں حوالہ دیتا ہے۔ اگر یہ ریورس پراکسی کے پیچھے SSR ہے تو نیا سرور تیار کریں اور صرف صحت مند ہونے کے بعد ٹریفک منتقل کریں۔
واپسی بھی اسی اصول کی معکوس ہونی چاہیے۔ پچھلی ریلیز کی ڈائریکٹری اور اس کے جامد وسائل محفوظ ہوں تو ایپ کو پچھلی ریلیز پر لانا پرانی فائلیں دوبارہ جوڑنے کے بغیر ممکن رہتا ہے۔
لیکن اس سے ہر پچھلی ریلیز پر واپسی محفوظ نہیں ہو جاتا۔ ڈیٹابیس منتقلی یا غیر موافق پس منظر نظام معاہدہ پرانی ایپ ورژن کو چلنے سے روک سکتا ہے، چاہے اس کی JavaScript اب بھی موجود ہو۔ جامد وسائل محفوظ رکھنا صرف جامد مطابقت کا مسئلہ حل کرتا ہے، پورے نظام کی ہر ریلیز مطابقت کا نہیں۔
سادہ ذاتی میزبانی کے لیے مشترک ناقابلِ تبدیلی وسائل کی ڈائریکٹری مؤثر ہے
Nginx پر مبنی چھوٹی ڈیپلائمنٹ میں ایک سیدھا طریقہ یہ ہے کہ موجودہ ایپ ریلیز کو مشترک جامد وسائل کے ذخیرے سے الگ رکھا جائے۔
مثالی ڈائریکٹری ترتیب یوں ہو سکتی ہے:
/srv/app/releases/2026-08-13-a/
/srv/app/releases/2026-08-13-b/
/srv/app/current -> /srv/app/releases/2026-08-13-b/
/srv/app/shared/_next/static/...
ہر ڈیپلائمنٹ اپنی نئی /_next/static فائلیں مشترک ڈائریکٹری میں شامل کرے، اور پچھلی محفوظ ریلیزز کی فائلیں حذف نہ کرے۔ Nginx اس راستے کو ناقابلِ تبدیلی کیش پالیسی کے ساتھ پیش کر سکتا ہے:
location ^~ /_next/static/ {
root /srv/app/shared;
add_header Cache-Control "public, max-age=31536000, immutable";
}
یہ ترتیب صرف مثال ہے، میرے استعمال کردہ عین Nginx سیٹ اپ کا دعویٰ نہیں۔ حقیقی ڈیپلائمنٹ میں اجازتیں، MIME اقسام، کمپریشن کی مختلف شکلیں، CDN کا رویہ اور بلڈ آؤٹ پٹ کی اصل ترتیب سب دیکھنا پڑتا ہے۔
اہم بات معماری کی ہے: موجودہ ریلیز کی طرف بدلنے والا اشارہ اور ورژن شدہ وسائل کا زیادہ تر صرف بڑھنے والا ذخیرہ، دونوں کی زندگی کا دور مختلف ہے۔
HTML کو ہیش شدہ چنکس سے مختلف کیش پالیسی چاہیے
مسئلہ دوبارہ پیدا کرنے کا آسان ترین طریقہ HTML کو ایسے کیش کرنا ہے جیسے وہ مواد کے ہیش والا وسیلہ ہو۔
متحرک طور پر رینڈر ہونے والے Next.js صفحات میں فریم ورک عموماً صارف کے لیے مخصوص متحرک آؤٹ پٹ پر ایسی جواب قواعد استعمال کرتا ہے جو اسے دوبارہ استعمال ہونے سے روکتی ہیں۔ جامد اور ISR صفحات کی پالیسیاں مختلف ہیں اور CDN انہیں جائز طور پر کیش کر سکتا ہے۔ Nginx سے پیش ہونے والی جامد برآمد مزید اس بات پر منحصر ہوتی ہے کہ منتظم نے کون سے ہیڈرز مقرر کیے ہیں۔
اسی لیے میں “پوری ویب سائٹ” کے لیے ایک ہی کیش قاعدہ استعمال نہیں کرتا۔ میں شے کی قسم کے لحاظ سے سوچتا ہوں:
ہیش شدہ /_next/static وسیلہ
طویل max-age
immutable
محفوظ رکھنا مناسب
HTML / راستے کی دستاویز
نئی ریلیز تک منتقل ہو سکنی چاہیے
پالیسی رینڈرنگ ماڈل پر منحصر ہے
اپنے حوالہ شدہ وسائل سے زیادہ دیر زندہ نہیں رہنی چاہیے
RSC / نیویگیشن / API ڈیٹا
مطابقت اور تازگی کے الگ اصول
اگر CDN شامل ہو تو کیش کے ڈیزائن کے مطابق ڈیپلائمنٹ کے بعد نئی دستاویز کے راستے کو کیش سے ہٹانا ضروری ہو سکتا ہے۔ لیکن صرف نئی ریلیز آنے کی وجہ سے پرانے ہیش شدہ چنکس کو کیش سے ہٹانا عموماً الٹا نقصان دیتا ہے: اگر اصل سرور نے بھی انہیں حذف کر دیا ہو تو وہ آخری نقل بھی ختم ہو جاتی ہے جو پرانے کلائنٹ کو بچا سکتی تھی۔
Next.js کا Next.js کی CDN کیشنگ رہنما یہاں مفید ہے، کیونکہ وہ صفحہ کیشنگ کو /_next/static وسائل کی ایک سالہ ناقابلِ تبدیلی پالیسی سے الگ کرتا ہے۔
خودکار دوبارہ لوڈ بحالی کا ذریعہ ہے، بنیادی ڈیپلائمنٹ حکمتِ عملی نہیں
چنک ناکامی پر عام جواب ہوتا ہے: “صفحہ دوبارہ لوڈ کر دو۔” یہ اکثر کام کرتا ہے، کیونکہ مکمل نیویگیشن موجودہ دستاویز حاصل کرتی ہے جو موجودہ بلڈ کا حوالہ دیتا ہے۔
لیکن ہر اسکرپٹ خرابی پر اندھا دھند دوبارہ لوڈ نئی مشکلات بناتا ہے:
- تیسرے فریق کے اسکرپٹ کی خرابی بے فائدہ دوبارہ لوڈ کر سکتی ہے؛
- حقیقی سرور بندش لامتناہی دوبارہ لوڈ چکر بنا سکتی ہے؛
- غیر محفوظ فارم میں صارف کی معلومات ضائع ہو سکتی ہیں؛
- مکمل نیویگیشن پر React جزو کی حالت ختم ہو جاتی ہے؛
- وہی خراب ڈیپلائمنٹ دوبارہ ناکام ہو سکتی ہے۔
موجودہ Next.js دستاویزات بھی خبردار کرتی ہیں کہ ورژنز میں عدم مطابقت سے بحالی کے لیے مکمل نیویگیشن useState جیسی جزوی حالت کھو سکتی ہے، جبکہ URL یا مستقل براؤزر ذخیرہ میں محفوظ حالت باقی رہ سکتی ہے۔
اگر میں کلائنٹ پر بحالی شامل کروں تو اسے محدود اور صرف ایک بار ہونے والا رکھوں گا۔ ایک وضاحتی نفاذ یوں ہو سکتا ہے:
const RECOVERY_KEY = 'next-chunk-recovery-attempted'
function isOwnNextAsset(url: string) {
try {
const parsed = new URL(url, window.location.href)
return (
parsed.origin === window.location.origin &&
parsed.pathname.startsWith('/_next/static/')
)
} catch {
return false
}
}
window.addEventListener(
'error',
(event) => {
const target = event.target
if (!(target instanceof HTMLScriptElement)) return
if (!isOwnNextAsset(target.src)) return
reportChunkFailure({
page: window.location.href,
asset: target.src,
})
if (sessionStorage.getItem(RECOVERY_KEY)) return
sessionStorage.setItem(RECOVERY_KEY, '1')
window.location.reload()
},
true,
)
یہ جان بوجھ کر صرف مثال ہے۔ پروڈکشن نفاذ میں طرزنامہ چنکس، فریم ورک کی معلوم خرابی صورتیں، ایسے صارف بہاؤ جہاں دوبارہ لوڈ نقصان دہ ہو، اور کامیاب لوڈ کے بعد بحالی نشان صاف کرنے کا طریقہ بھی دیکھنا چاہیے۔
ایڈیٹر، ادائیگی کے عمل یا طویل فارم میں میں جبری صفحہ تازہ کرنا کے بجائے ایسا بینر ترجیح دے سکتا ہوں: “نیا ورژن دستیاب ہے؛ اپنا کام محفوظ کریں اور پھر صفحہ دوبارہ لوڈ کریں۔”
نگرانی کے ڈیٹا سے معلوم ہونا چاہیے کہ مسئلہ واقعی ورژنز میں عدم مطابقت ہے یا نہیں
صرف “اسکرپٹ لوڈ نہیں ہوا” کہنا کافی نہیں۔ حذف شدہ پرانے چنک کو اتفاقی نیٹ ورک خرابی سے الگ کرنے کے لیے مجھے ڈیپلائمنٹ سے متعلق سیاق چاہیے۔
مفید فیلڈز میں یہ شامل ہیں:
- ناکام وسیلے کا URL؛
- موجودہ صفحے کا URL؛
- کیا وسیلہ اپنے نظام کا ہے یا تیسرے فریق کا؛
- کلائنٹ کو نظر آنے والی ریلیز یا ڈیپلائمنٹ شناخت؛
- براؤزر اور آپریٹنگ سسٹم؛
navigator.onLineبطور کمزور اشارہ، رابطے کا ثبوت نہیں؛- صفحہ کھلنے کے بعد گزرا وقت؛
- کیا خرابی ڈیپلائمنٹ کے فوراً بعد ہوئی؛
- کیا یہ پہلی بحالی کوشش تھی؛
- HTTP حالت، جب اسے سرور کی جانب دیکھا جا سکے؛
- اصل سرور یا پراکسی پر اس درخواست کو پیش کرنے والی موجودہ ریلیز۔
اس کے بعد نمونے کہیں زیادہ معنی خیز ہو جاتے ہیں۔
اگر مختلف نیٹ ورکس پر بہت سے صارفین پرانے ہیش شدہ چنک URLs مانگیں اور اصل سرور ریلیز کے فوراً بعد 404 واپس کرے تو پرانے وسائل کے غائب ہونے کی وضاحت مضبوط ہو جاتی ہے۔ اگر صرف ایک صارف کو HTTP جواب کے بغیر نیٹ ورک سطح کی ناکامی ملے تو ورژنز میں عدم مطابقت کہیں کم یقینی ہے۔ اگر چنک 200 کے ساتھ غلط MIME قسم یا HTML خرابی صفحہ واپس کرے تو مسئلہ راستہ بندی یا پراکسی ترتیب میں ہے، صرف محفوظ رکھنے کی مدت میں نہیں۔
میں اپنے چنک کی ناکامیوں پر تیسرے فریق کے وسائل سے الگ انتباہ بھی لگاؤں گا۔ یہی نگرانی تبدیلی میرے اصل لاگز سے سب سے براہِ راست ثابت ہوتی ہے: معنی خیز اشارہ براؤزر کے بہت سے غیر متعلق شور میں ملا ہوا تھا۔
خرابی دوبارہ پیدا کرنے کا ٹیسٹ سادہ ہے، مگر پرانا ٹیب کھلا رہنا چاہیے
عام ریلیز ٹیسٹنگ میں یہ خرابی آسانی سے چھوٹ جاتی ہے، کیونکہ انجینئر ڈیپلائمنٹ کے فوراً بعد صفحہ صفحہ تازہ کرنا کر دیتے ہیں۔ صفحہ تازہ کرنا وہی حالت ختم کر دیتا ہے جسے ہم جانچنا چاہتے ہیں۔
بہتر دستی ٹیسٹ یوں ہے:
- ریلیز A ڈیپلائے کریں؛
- براؤزر کیش فعال رکھتے ہوئے پروڈکشن جیسا ٹیب کھولیں؛
- ایپ کا صرف کچھ حصہ استعمال کریں تاکہ چند راستے یا دیر سے لوڈ ہونے والی خصوصیات ابھی لوڈ نہ ہوں؛
- وہ ٹیب کھلا چھوڑ دیں؛
- ریلیز B ڈیپلائے کریں؛
- پرانے ٹیب کو صفحہ تازہ کرنا نہ کریں؛
- کوئی ایسا راستہ یا متحرک خصوصیت چلائیں جسے پہلے لوڈ نہ ہوا کوڈ درکار ہو؛
- DevTools میں Network اور Console دیکھیں؛
- جانچیں کہ پرانے وسیلہ URLs اب بھی 200 واپس کرتے ہیں یا نہیں؛
- جانچیں کہ ورژنز میں عدم مطابقت کی شناخت مناسب وقت پر کنٹرول شدہ مکمل نیویگیشن کرتی ہے یا نہیں۔
میں یہی ٹیسٹ سامنے CDN کے ساتھ، مرحلہ وار ڈیپلائمنٹ کے دوران دو سرور مثالات کے ساتھ، اور مقررہ محفوظ رکھنے کی مدت مدت ختم ہونے کے بعد بھی دہراؤں گا۔
ایک باریک ٹیسٹنگ غلطی DevTools میں ہر چیز کے لیے “Disable cache” فعال کرنا ہے۔ کچھ تشخیص میں یہ مفید ہو سکتا ہے، مگر براؤزر کا رویہ بدل دیتا ہے۔ طویل عرصے تک کھلے ٹیب کا منظرنامہ حقیقت پسندانہ کیشنگ کے ساتھ بھی آزمانا چاہیے، کیونکہ براؤزر کیش خود نظام کا حصہ ہے۔
ہر چنک ناکامی پرانی فائلیں رکھنے سے حل نہیں ہوتی
محفوظ رکھنے کی مدت کی طاقت یہی ہے کہ وہ ایک محدود طریقۂ ناکامی حل کرتی ہے۔ اسے ہر مسئلے کی عمومی وضاحت نہیں بننا چاہیے۔
اپنے نظام کا چنک ان وجوہات سے ناکام ہو سکتا ہے:
- درخواست سرور تک پہنچی ہی نہ ہو؛
- رابطہ منقطع ہو گیا ہو؛
- براؤزر توسیع نے اسے روک دیا ہو؛
- CDN کنارے کا سرور پر عارضی خرابی ہوئی ہو؛
- Nginx نے راستہ غلط بھیجا ہو؛
- سرور نے JavaScript کے بجائے HTML خرابی دستاویز واپس کیا ہو؛
- کمپریشن یا مواد کی انکوڈنگ خراب ہوا ہو؛
- فائل کی اجازتیں غلط ہوں؛
- جزوی ڈیپلائمنٹ نے چنک اپ لوڈ ہی نہ کیا ہو؛
- فائل موجود تھی مگر بہت جلد حذف کر دی گئی ہو؛
- کلائنٹ اور سرور غیر موافق ڈیپلائمنٹس پر ہوں۔
جوابی کوڈ اور وقت اہم ہیں۔ ہر ریلیز کے بعد پرانے مواد کے ہیش والا URL پر بار بار 404 آنا، ایک موبائل نیٹ ورک پر ERR_CONNECTION_RESET آنے سے بالکل مختلف کہانی بتاتا ہے۔
اسی لیے میں اپنے اصل واقعے کو یوں دوبارہ نہیں لکھوں گا کہ “میں نے ثابت کر دیا کہ باسی HTML نے سائٹ توڑی تھی۔” میں نے یہ ثابت نہیں کیا۔ میں نے اپنے نظام کے ایک حقیقی چنک کی ناکامی دیکھی اور ورژنز میں عدم مطابقت کو ایک سنجیدہ طریقۂ ناکامی سمجھا جسے ڈیزائن کے ذریعے کم کرنا چاہیے۔
محفوظ ترین ڈیپلائمنٹ پرانے کلائنٹس کو بھی ریلیز سطح کا حصہ سمجھتی ہے
گہری غلطی یہ سمجھنا ہے کہ ڈیپلائمنٹ ایک لمحے میں ورژن A کو B سے بدل دیتی ہے۔
سرور پر علامتی ربط یا کنٹینر منتظم یہی دکھا سکتا ہے۔ نیٹ ورک پر پرانے CDN اشیا باقی رہ سکتے ہیں۔ براؤزر میں A کے دستاویز B فعال ہونے کے کافی دیر بعد تک چل سکتے ہیں۔ مرحلہ وار ریلیز میں دونوں سرور ورژنز ایک ساتھ فعال ہو سکتی ہیں۔ پچھلی ریلیز پر واپسی کے دوران B غائب اور A دوبارہ موجودہ ہو سکتی ہے۔
اس لیے اصل ریلیز سطح ایک نقطہ نہیں، وقت کا وقفہ ہے۔
Next.js ایپ کے لیے میرے ڈیپلائمنٹ اصول اب اسی خیال پر مبنی ہیں:
- ہر منطقی ریلیز کے لیے ایک بار بلڈ کریں۔ نقول کو خاموشی سے الگ بلڈ آؤٹ پٹس نہ بنانے دیں۔
- ناقابلِ تبدیلی وسائل ان کے حوالوں سے پہلے شائع کریں۔
- پرانے ہیش شدہ وسائل جان بوجھ کر مقرر مطابقتی مدت تک رکھیں۔
- بدلنے والے HTML کو ہیش شدہ چنکس جیسی کیش پالیسی نہ دیں۔
- جب ڈیپلائمنٹ ماڈل میں ورژنز میں عدم مطابقت ممکن ہو تو
deploymentIdاستعمال کریں۔ - پلیٹ فارم کی سطح پر عدم مطابقت سے تحفظ صرف وہاں استعمال کریں جہاں ہوسٹنگ پلیٹ فارم واقعی ورژن سے آگاہ راستہ بندی دیتا ہو۔
- بحالی کو ایک بار تک محدود اور صارف کی حالت سے آگاہ رکھیں۔
- اپنے چنکس کی ناکامیوں کو الگ پروڈکشن اشارے کے طور پر مانیٹر کریں۔
- پرانا ٹیب کھلا رکھ کر ڈیپلائمنٹ ٹیسٹ کریں۔
- پرانے وسائل بعد میں صاف کریں، نئی ریلیز فعال کرتے وقت نہیں۔
وہ اصول جو میں اب استعمال کرتا ہوں
سبز بلڈ اور تازہ صفحے کا درست کھلنا یہ ثابت نہیں کرتا کہ ڈیپلائمنٹ پہلے سے موجود صارفین کے لیے بھی محفوظ ہے۔
پرانا ٹیب بے کار ملبہ نہیں۔ وہ حقیقی کلائنٹ ہے جو واقعی پچھلی ریلیز چلا رہا ہے۔
جب میں نے ڈیپلائمنٹ کو اس زاویے سے دیکھنا شروع کیا تو چنک مسئلہ کم پراسرار لگا۔ مواد کا ہیش وسیلے کو مستحکم شناخت دیتا ہے۔ طویل کیشنگ اس شناخت کو مؤثر بناتی ہے۔ مگر ڈیپلائمنٹ کو اس شناخت کا کافی دیر احترام کرنا چاہیے، یا کلائنٹ کو قابو میں نیا ورژن تک منتقل ہونے کا راستہ دینا چاہیے۔
مجھے ہر پرانی ریلیز ہمیشہ زندہ نہیں رکھنی۔ مجھے صرف یہ چاہیے کہ نظام اس مدت کو برداشت کرے جس میں پرانے کلائنٹس اور نئے سرور جائز طور پر ایک ساتھ موجود ہوتے ہیں۔
اب میرے لیے اہم ڈیپلائمنٹ معاہدہ یہ ہے: نئے صارفین کو نئی ریلیز ملے، پرانے صارفین وہ فائلیں نہ کھوئیں جنہیں ان کی موجودہ ریلیز اب بھی مانگنا جانتی ہے، اور باقی رہ جانے والا ورژن اختلاف خراب صفحے کے بجائے جان بوجھ کر بنائے گئے بحالی راستے پر ختم ہو۔