Dependency Hell — ایسی صورتحال جب پیکیج مینیجر پروجیکٹ میں لائبریری کے ورژن کے تنازعات کو حل نہیں کر سکتا۔ موبائل ڈویلپمنٹ میں، Dependency Hell خاص طور پر تکلیف دہ ہے: Android میں Gradle اور iOS میں CocoaPods/SPM اکثر عبوری تنازعات کا سامنا کرتے ہیں۔ Sonatype (2024) کی رپورٹ کے مطابق، موبائل پروجیکٹ میں براہ راست انحصار کی اوسط تعداد 80 سے تجاوز کر جاتی ہے، اور عبوری — 400+، ہر ایک کو ورژن مطابقت کی ضرورت ہوتی ہے۔
اہم نکات
Dependency Hell ایک اصطلاح ہے جو اس صورتحال کو بیان کرتی ہے جب انحصار کا انتظامی نظام لائبریریوں کے درمیان ورژن کے تنازعہ کو حل نہیں کر سکتا۔ پروجیکٹ کو لائبریری A ورژن 1.x اور لائبریری B ورژن 2.x کی ضرورت ہے، لیکن A کا انحصار C ورژن 1.0 پر ہے، جبکہ B کا انحصار C ورژن 2.0 پر ہے، اور C:1.0 اور C:2.0 غیر مطابقت رکھتے ہیں۔
یہ مسئلہ پیکیج مینیجر والے تمام ماحولیاتی نظاموں میں عام ہے۔ Android میں — support library اور AndroidX کے درمیان Gradle تنازعات۔ iOS میں — Alamofire کے مختلف ورژنوں کے درمیان CocoaPods تنازعات۔ Node.js میں — npm کے peer dependency تنازعات۔ Python میں — pip کی حل ناکامیاں۔
جدید انحصار مینیجرز (npm v7+, Gradle 7+, SwiftPM) نے حل کرنے کے الگورتھم کو بہتر بنایا ہے، لیکن سینکڑوں عبوری انحصاروں کے ساتھ تنازعات کا مکمل خاتمہ ناممکن ہے۔ Dependency Hell «بلڈ خرابی» کے زمرے سے «خطرے کا انتظام» کے زمرے میں منتقل ہو گیا ہے۔
Diamond dependency — کلاسک کیس۔ لائبریری A کا انحصار D:1.0 پر ہے، لائبریری B کا انحصار D:2.0 پر ہے۔ اگر A اور B ایک ساتھ استعمال ہوں، پیکیج مینیجر کو فیصلہ کرنا ہوگا کہ D کا کون سا ورژن انسٹال کرنا ہے۔ زیادہ تر معاملات میں، زیادہ سے زیادہ ورژن (2.0) منتخب کیا جاتا ہے، لیکن اگر A D:2.0 کے ساتھ مطابقت نہیں رکھتا — تنازعہ ناقابل حل ہے۔
Version conflict — ضروریات کا واضح عدم مطابقت۔ A کو Logging >=2.0 چاہیے، B کو Logging <2.0 چاہیے۔ مینیجر دونوں شرائط پوری نہیں کر سکتا۔ Peer dependency conflict — پلگ ان A کو React 17 چاہیے، لیکن پروجیکٹ React 18 استعمال کرتا ہے جس میں تبدیلیاں شامل ہیں۔ npm انتباہ دکھاتا ہے، لیکن انسٹالیشن جاری رہتی ہے — رویہ غیر متوقع ہو جاتا ہے۔
Transitive dependency hell — جب انحصار براہ راست نہیں بلکہ بالواسطہ ہو۔ ڈویلپر نہیں جانتا کہ لائبریری A کا انحصار B پر ہے، اور B کا انحصار C پر ہے۔ Gradle Dependency Tree — انحصار کی پوری زنجیر کو دیکھنے کا آلہ، یہ دکھاتا ہے کہ متنازعہ لائبریری کہاں سے آتی ہے۔
Circular dependency — A کا انحصار B پر ہے، اور B کا انحصار A پر ہے۔ جدید مینیجر (Gradle, npm) بلڈ کے وقت چکری انحصار کو روکتے ہیں۔ حل — ایک مشترکہ ماڈیول C نکالیں جس پر A اور B دونوں کا انحصار ہو، چکر کو توڑتے ہوئے۔
لائبریریوں کی بڑھتی ہوئی تعداد — بنیادی شرط۔ ہر ماڈیول براہ راست اور عبوری انحصار شامل کرتا ہے۔ Jetpack Compose، Firebase، Retrofit اور Coil والے Android پروجیکٹ میں، عبوری انحصار کی تعداد آسانی سے 500 سے تجاوز کر جاتی ہے۔ ہر نئی لائبریری ایک ممکنہ تنازعہ ہے۔
غیر مطابقت پذیر اپ ڈیٹس — ٹیمیں مختلف اوقات میں لائبریریوں کو اپ ڈیٹ کرتی ہیں۔ Backend Jackson کو 2.15 پر اپ ڈیٹ کرتا ہے، Analytics ٹیم 2.12 استعمال کرتی ہے۔ ماڈیولز کو ضم کرتے وقت تنازعہ پیدا ہوتا ہے۔ حل — Gradle BOM فائل یا ورژن کیٹلاگ میں مرکزی ورژن (Bill of Materials)۔
ایک ہی لائبریری کے مختلف ورژن — کلاسک صورتحال: ماڈیول A OkHttp 3.12 استعمال کرتا ہے، ماڈیول B OkHttp 4.0 استعمال کرتا ہے۔ اگر 4.0 میں اپ گریڈ ماڈیول A کو توڑ دیتا ہے، پروجیکٹ دو ورژنوں پر پھنس جاتا ہے، جو Java میں classpath تنازعات یا iOS میں ڈپلیکیٹ علامات کا سبب بن سکتا ہے۔
Gradle Dependency Tree — `gradle dependencies` کمانڈ تنازعات کے اشارے کے ساتھ مکمل انحصار کا درخت دکھاتی ہے۔ حل شدہ ورژن دکھاتا ہے کہ Gradle نے کون سا ورژن منتخب کیا، اور متنازعہ ورژن تیروں سے نشان زد ہیں۔ مثال: `com.squareup.okhttp3:okhttp -> 4.9.3 (*)` — ورژن حل ہو گیا، (*) — تکرار۔
npm ls — Node.js کے لیے اسی طرح کا کمانڈ۔ `--all` جھنڈا پورا درخت دکھاتا ہے۔ Peer dependency تنازعات انتباہات کے ساتھ دکھائے جاتے ہیں۔ SwiftPM Graph — `swift package show-dependencies` iOS پروجیکٹس کے لیے انحصار کا گراف دکھاتا ہے، بشمول شاخیں اور ریویژن۔
Dependency Analysis Plugin — Autonomy کا Gradle پلگ ان جو غیر استعمال شدہ انحصار اور تنازعات تلاش کرتا ہے۔ Ben Manes Versions Plugin — چیک کرتا ہے کہ کون سے انحصار پرانے ہیں اور دستیاب اپ ڈیٹ دکھاتا ہے۔ دونوں اوزار معمول کی مطابقت کی جانچ کو خودکار بناتے ہیں۔
// تنازع: ماڈیول A کو okhttp 3.x چاہی۔، ماڈیول B کو okhttp 4.x چاہی۔
dependencies {
implementation("com.example:module-a:1.0") // -> okhttp 3.12
implementation("com.example:module-b:2.0") // -> okhttp 4.0
}
// حل: ایک مخصوص ورژن مجبور کریں
configurations.all {
resolutionStrategy {
force "com.squareup.okhttp3:okhttp:4.9.3"
}
}
Version Catalog (Gradle 7+) — TOML فائل میں مرکزی ورژن کا اعلان۔ تمام ماڈیول ایک ہی لائبریری ورژن استعمال کرتے ہیں۔ مثال: `libs.versions.toml` فائل میں `okhttp = «4.9.3»` ہے، اور تمام ماڈیول اس کیٹلاگ کا حوالہ دیتے ہیں۔ ماڈیولز کے درمیان ورژن تنازعات ختم ہو جاتے ہیں۔
Bill of Materials (Spring BOM) — Maven کا تصور جہاں مطابقت رکھنے والے لائبریری ورژن متعین کیے جاتے ہیں۔ Google Android ٹیم Jetpack لائبریریوں کے لیے Compose BOM استعمال کرتی ہے۔ BOM استعمال کرکے، آپ کو ضمانت ملتی ہے کہ تمام Compose ورژن ایک دوسرے کے ساتھ مطابقت رکھتے ہیں۔
Renovate اور Dependabot — انحصار کی اپ ڈیٹ کے لیے خودکار PR بنانے والے۔ Renovate مطابقت رکھنے والی اپ ڈیٹس کو گروپ کرتا ہے، Docker امیجز کے ذریعے تبدیلیوں کی جانچ کرتا ہے۔ Dependabot — GitHub کا بلٹ ان حل جو انحصار کو اپ ڈیٹ کرتا ہے اور CI کے ذریعے مطابقت کی جانچ کرتا ہے۔
Semantic Versioning — patch/minor اپ ڈیٹ کے لیے caret `^1.2.3` اور صرف patch کے لیے tilde `~1.2.3` استعمال کریں۔ لیکن semver بھی مطابقت کی ضمانت نہیں دیتا — حقیقی semver کی خلاف ورزیاں 15% معاملات میں ہوتی ہیں (Luxembourg یونیورسٹی کی تحقیق، 2024 کے مطابق)۔ لاک فائلیں عین ورژن کو مقرر کرتی ہیں جس نے ٹیسٹ پاس کیا ہے۔
انحصار کو کم سے کم کرنا — ہر لائبریری جائز ہونی چاہیے۔ اگر آپ 20 لائنوں کے اپنے کوڈ میں فعالیت لاگو کر سکتے ہیں — لائبریری شامل نہ کریں۔ مثال: تاریخ فارمیٹنگ لائبریری (4 عبوری انحصار) کے بجائے، پلیٹ فارم کے بلٹ ان ٹولز استعمال کریں۔ «انحصار بجٹ» کا قاعدہ — فی پروجیکٹ 50 سے زیادہ براہ راست انحصار نہیں۔
باقاعدہ اپ ڈیٹس — انحصار کو سال میں ایک بار نہیں بلکہ چھوٹے قدموں میں اپ ڈیٹ کریں۔ Dependabot ہر اپ ڈیٹ کے لیے PR بناتا ہے۔ CI کو مکمل ٹیسٹ سوٹ چلانا چاہیے۔ DevContainer — ایک متحد ڈویلپمنٹ ماحول جہاں انحصار کے ورژن پروڈکشن سے مماثل ہوتے ہیں، ماحول کے درمیان تنازعات کو ختم کرتے ہوئے۔
اکثر پوچھے گئے سوالات
سب سے پہلے، `gradle dependencies` (Gradle)، `npm ls` (Node.js) یا `swift package show-dependencies` (SwiftPM) چلائیں۔ متنازعہ لائبریری تلاش کریں۔ تین حل: resolutionStrategy کے ذریعے ورژن مجبور کرنا، عبوری انحصار کو خارج کرنا (`exclude group:`)، یا متنازعہ لائبریریوں میں سے ایک کو مطابقت رکھنے والے ورژن میں اپ ڈیٹ کرنا۔
Version Catalog (libs.versions.toml) — تمام لائبریری ورژن کے لیے سچائی کا واحد ذریعہ۔ پروجیکٹ کے تمام ماڈیول ایک کیٹلاگ کا حوالہ دیتے ہیں۔ جب لائبریری اپ ڈیٹ ہوتی ہے، ورژن ایک جگہ تبدیل ہوتا ہے۔ یہ اس صورتحال کو ختم کرتا ہے جہاں دو ماڈیول ایک ہی لائبریری کے مختلف ورژن استعمال کرتے ہیں۔
عبوری انحصار وہ لائبریریاں ہیں جو براہ راست انحصار اپنے ساتھ لاتا ہے۔ ڈویلپر کو اکثر ان کے بارے میں معلوم نہیں ہوتا۔ خطرہ: عبوری انحصار کسی دوسرے براہ راست انحصار سے تنازعہ کر سکتا ہے۔ حل یہ ہے کہ باقاعدگی سے انحصار کے درخت کی جانچ کریں اور صرف کم سے کم عبوری انحصار والی لائبریریاں شامل کریں۔
ضروری نہیں کہ ہر سپرنٹ، لیکن باقاعدگی سے — ہاں۔ سفارش: مہینے میں ایک بار، PR بنانے کے لیے Dependabot یا Renovate چلائیں۔ اہم سیکیورٹی پیچ ایک ہفتے کے اندر اپ ڈیٹ کرنے چاہئیں۔ معمولی اپ ڈیٹس — عام سپرنٹ کے دوران۔ بڑی اپ ڈیٹس کے لیے تبدیلیوں کی علیحدہ تشخیص ضروری ہے۔
غیر معاون لائبریری سیکیورٹی اور مطابقت کا خطرہ ہے۔ حکمت عملی: فعال کمیونٹی والا متبادل تلاش کریں (GitHub ستارے، آخری commit کی تاریخ)، تجرید (Interface/Protocol) کے ذریعے منتقلی کی منصوبہ بندی کریں، 2–3 سپرنٹ میں لائبریری تبدیل کریں۔ اگر کوئی متبادل نہیں ہے — ریپوزٹری کو fork کریں اور ورژن کو ٹیم میں برقرار رکھیں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں