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% ক্ষেত্রে ঘটে (University of 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 স্টার, শেষ কমিটের তারিখ), অ্যাবস্ট্রাকশনের (Interface/Protocol) মাধ্যমে মাইগ্রেশন পরিকল্পনা করুন, 2–3 স্প্রিন্টে লাইব্রেরি প্রতিস্থাপন করুন। যদি কোনো বিকল্প না থাকে — রিপোজিটরি ফর্ক করুন এবং টিমের মধ্যে সংস্করণ বজায় রাখুন।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন