npm, Cargo, Composer এবং pip সবাই একই semver shorthand ভিন্নভাবে ব্যাখ্যা করে, এবং আমি যে তেরোটি range expression পরীক্ষা করেছি তার মধ্যে মাত্র দুটি চারটি টুলের ক্ষেত্রেই একইভাবে কাজ করে।
আমি প্রতিটি ম্যানেজারের জন্য আলাদা আলাদা parser লিখেছি, তাদের অফিসিয়াল specs অনুসরণ করেছি এবং তারপর প্রতিটি range-কে উনিশটি version number-এর একটি matrix-এর বিপরীতে চালিয়েছি। ১৩ × ১৯ গ্রিডটি (২৪৭টি cell) একটি খণ্ডিত চিত্র তুলে ধরেছে: মাত্র ১৭টি cell প্রতিটি টুল থেকে একই “yes” বা “no” উত্তর দিয়েছে, এবং সেই ১৭টির বেশিরভাগই ছিল সর্বসম্মত প্রত্যাখ্যান। সংক্ষেপে বলতে গেলে, টুলগুলো “yes”-এর চেয়ে “no”-তে অনেক বেশি একমত হয়।
কেন টুলগুলোর মধ্যে পার্থক্য দেখা যায়
চারটি ম্যানেজারই Semantic Versioning অনুসরণ করার দাবি করে, তবুও প্রতিটি তাদের নিজস্ব shorthand নিয়ম যোগ করে।
- Caret (^) এবং dot-x – npm caret সমর্থন করে; pip caret এবং dot-x উভয়ই উপেক্ষা করে। শুধুমাত্র এটিই npm এবং pip-এর মধ্যে ৪৯টি cell-এ মতপার্থক্য তৈরি করে।
- Bare versions – Cargo একটি সাধারণ
1.2.3-কে caret range হিসেবে গণ্য করে, যার অর্থ হলো “1.x-এর সাথে সামঞ্জস্যপূর্ণ”। npm এবং Composer একই string-কে একটি exact match হিসেবে পড়ে, যা শুধুমাত্র1.2.3-কেই গ্রহণ করে। - Tilde (~) – Cargo-র tilde minor version-কে নির্দিষ্ট করে দেয় (
~1.2মিলে যায়1.2.*-এর সাথে কিন্তু1.3.0-এর সাথে নয়)। Composer-র tilde major version-কে নির্দিষ্ট করে দেয় (~1.2মিলে যায়1.*-এর সাথে)। যে কোনো version-এর ক্ষেত্রে এই দুটি ব্যাখ্যা ভিন্ন হয়ে যায় যেখানে minor component পরিবর্তিত হয়।
Pre-release হ্যান্ডলিং আরও একটি বিস্ময় যোগ করে। ^1.2.3-এর মতো একটি range 2.0.0-rc.1-কে অন্তর্ভুক্ত করে না, যদিও এর numeric অংশটি upper bound-এর চেয়ে কম। নিয়মটি হলো: pre-releases গুলো বাদ দেওয়া হয় যদি না range-টিতে স্পষ্টভাবে কোনো pre-release identifier উল্লেখ করা থাকে।
টুলগুলোর মধ্যে ব্যবহারের একমাত্র নিরাপদ সিনট্যাক্স
পরীক্ষাটি দেখায় যে স্পষ্ট inequality ranges—যেমন >=1.2.0 <2.0.0—npm, Cargo, Composer এবং pip-এ একইভাবে কাজ করে। প্রতিটি ম্যানেজার এই দুটি boundary-কে আক্ষরিক numeric limit হিসেবে বিবেচনা করে, যেখানে কোনো লুকানো caret বা tilde semantics থাকে না।
আপনার যদি “যেকোনো 1.x version যা অন্তত 1.2” প্রকাশ করার প্রয়োজন হয়, তবে সেটি সম্পূর্ণভাবে লিখে ফেলুন। এতে কয়েকটা অতিরিক্ত ক্যারেক্টার খরচ হবে; কিন্তু এটি কোড যেখানেই চলুক না কেন একটি অনুমেয় (predictable) resolution নিশ্চিত করবে।
কখন shorthand ব্যবহার করা যুক্তিযুক্ত
একক-ভাষার প্রজেক্টের জন্য shorthand এখনও আকর্ষণীয়। একটি বিশুদ্ধ npm ecosystem-এর মধ্যে, ^1.2.3 সংক্ষেপে “ভবিষ্যতের minor এবং patch release-এর সাথে সামঞ্জস্যপূর্ণ” বিষয়টিকে প্রকাশ করে। Cargo-র caret বা Composer-র tilde-এর ক্ষেত্রেও একই কথা প্রযোজ্য যখন আপনি জানেন যে ব্যবহারকারী টুলটি পরিবর্তিত হবে না। বিপদ তখনই দেখা দেয় যখন একই manifest বিভিন্ন ভাষার সীমানা ছাড়িয়ে পুনরায় ব্যবহার করা হয়, অথবা যখন একটি CI job একাধিক ecosystem থেকে dependencies টেনে আনে।
সারসংক্ষেপ
ক্রস-ল্যাঙ্গুয়েজ সামঞ্জস্যতার জন্য semver shorthand-এর ওপর নির্ভর করা একটি জুয়া খেলার মতো। একটি version constraint সব জায়গায় একই অর্থ বহন করছে তা নিশ্চিত করার একমাত্র নির্ভরযোগ্য উপায় হলো স্পষ্ট inequality লিখে দেওয়া। যখন আপনার সংক্ষিপ্ততার প্রয়োজন হবে, তখন একটি নির্দিষ্ট ecosystem-এর মধ্যে থাকুন; আর যখন সামঞ্জস্যতা (consistency) প্রয়োজন হবে, তখন clarity-র জন্য shorthand ত্যাগ করুন।
