npm, Cargo, Composer, pip എന്നിവയെല്ലാം ഒരേ semver shorthand വ്യത്യസ്ത രീതിയിലാണ് വ്യാഖ്യാനിക്കുന്നത്. ഞാൻ പരിശോധിച്ച പതിമൂന്ന് റേഞ്ച് എക്സ്പ്രഷനുകളിൽ (range expressions) രണ്ട് എണ്ണം മാത്രമാണ് ഈ നാല് ടൂളുകളിലും ഒരേപോലെ പ്രവർത്തിക്കുന്നത്.

ഓരോ മാനേജർക്കും വേണ്ടി ഞാൻ പ്രത്യേക പാഴ്സറുകൾ (parsers) എഴുതുകയും അവയുടെ ഔദ്യോഗിക സ്പെസിഫിക്കേഷനുകൾ പിന്തുടരുകയും ചെയ്തു. തുടർന്ന് പത്തൊൻപത് വേർഷൻ നമ്പറുകളുടെ ഒരു മാട്രിക്സിനെതിരെ ഓരോ റേഞ്ചും പരീക്ഷിച്ചു. 13 × 19 ഗ്രിഡ് (247 സെല്ലുകൾ) പരിശോധിച്ചപ്പോൾ ലഭിച്ച ഫലം വളരെ വിഭിന്നമായിരുന്നു: എല്ലാ ടൂളുകളിൽ നിന്നും ഒരേപോലെ “yes” അല്ലെങ്കിൽ “no” എന്ന ഉത്തരം ലഭിച്ചത് വെറും 17 സെല്ലുകളിൽ മാത്രമാണ്, അതിൽ ഭൂരിഭാഗവും ഒരേപോലെ നിരസിക്കപ്പെട്ടവയായിരുന്നു (rejections). ചുരുക്കത്തിൽ, “yes” എന്നതിനേക്കാൾ കൂടുതൽ തവണയാണ് ഈ ടൂളുകൾ “no” എന്ന കാര്യത്തിൽ യോജിക്കുന്നത്.

ടൂളുകൾ തമ്മിൽ വ്യത്യാസപ്പെടുന്നത് എന്തുകൊണ്ട്?

ഈ നാല് മാനേജർമാരും Semantic Versioning പിന്തുടരുന്നുണ്ടെന്ന് അവകാശപ്പെടുന്നുണ്ടെങ്കിലും, ഓരോന്നും അവരുടേതായ shorthand നിയമങ്ങൾ ചേർക്കുന്നുണ്ട്.

  • Caret (^) and dot-x – npm കെയററ്റ് (caret) പിന്തുണയ്ക്കുന്നു; എന്നാൽ pip കെയററ്റും dot-x ഉം അവഗണിക്കുന്നു. ഇത് മാത്രം npm-ഉം pip-ഉം തമ്മിൽ 49 സെല്ലുകളിൽ അഭിപ്രായവ്യത്യാസമുണ്ടാക്കുന്നു.
  • Bare versions – Cargo ഒരു സാധാരണ 1.2.3 എന്നതിനെ ഒരു കെയററ്റ് റേഞ്ച് ആയിട്ടാണ് കണക്കാക്കുന്നത്, അതായത് “1.x-മായി പൊരുത്തപ്പെടുന്നവ” എന്നാണ് ഇതിനർത്ഥം. എന്നാൽ npm-ഉം Composer-ഉം ഇതേ സ്ട്രിംഗിനെ ഒരു കൃത്യമായ മാച്ച് (exact match) ആയിട്ടാണ് കാണുന്നത്, അതായത് 1.2.3 മാത്രം സ്വീകരിക്കുന്നു.
  • Tilde (~) – Cargo-യിലെ tilde minor വേർഷനെയാണ് നിജപ്പെടുത്തുന്നത് (~1.2 എന്നത് 1.2.* യോട് പൊരുത്തപ്പെടും, എന്നാൽ 1.3.0 യോട് പൊരുത്തപ്പെടില്ല). Composer-ലെ tilde major വേർഷനെയാണ് നിജപ്പെടുത്തുന്നത് (~1.2 എന്നത് 1.* യോട് പൊരുത്തപ്പെടും). മൈനർ ഘടകത്തിൽ മാറ്റം വരുന്ന ഏത് വേർഷനിലും ഈ രണ്ട് വ്യാഖ്യാനങ്ങളും വ്യത്യസ്തമായിരിക്കും.

Pre-release കൈകാര്യം ചെയ്യുന്ന രീതി മറ്റൊരു അത്ഭുതമാണ്. ^1.2.3 പോലുള്ള ഒരു റേഞ്ചിൽ 2.0.0-rc.1 ഉൾപ്പെടില്ല, അതിന്റെ സംഖ്യാഭാഗം അപ്പർ ബൗണ്ടിനേക്കാൾ (upper bound) കുറവാണെങ്കിൽ പോലും. ഇതിന്റെ നിയമം ഇതാണ്: റേഞ്ചിൽ ഒരു pre-release ഐഡന്റിഫയർ വ്യക്തമായി പരാമർശിച്ചിട്ടില്ലെങ്കിൽ, pre-releases ഒഴിവാക്കപ്പെടും.

ടൂളുകൾക്കിടയിൽ സുരക്ഷിതമായി ഉപയോഗിക്കാവുന്ന ഒരേയൊരു സിന്റാക്സ്

വ്യക്തമായ inequality ranges—ഉദാഹരണത്തിന് >=1.2.0 <2.0.0—npm, Cargo, Composer, pip എന്നിവയിൽ ഒരേപോലെ പ്രവർത്തിക്കുന്നു എന്ന് ഈ പരീക്ഷണം കാണിച്ചുതരുന്നു. ഓരോ മാനേജറും ഈ രണ്ട് അതിരുകളെയും (boundaries) നേരിട്ടുള്ള സംഖ്യാ പരിധികളായിട്ടാണ് കാണുന്നത്; ഇതിൽ കെയററ്റോ (caret) ടിൽഡോ (tilde) പോലുള്ള ഒളിഞ്ഞിരിക്കുന്ന അർത്ഥങ്ങളില്ല.

നിങ്ങൾക്ക് “കുറഞ്ഞത് 1.2 ആയ ഏതൊരു 1.x വേർഷനും” എന്ന് പ്രകടിപ്പിക്കണമെന്നുണ്ടെങ്കിൽ, അത് പൂർണ്ണമായി എഴുതുക. ഇതിന് കുറച്ച് അധികം അക്ഷരങ്ങൾ വേണ്ടി വന്നേക്കാം; എന്നാൽ കോഡ് എവിടെ പ്രവർത്തിച്ചാലും കൃത്യമായ റിസല്യൂഷൻ (resolution) ഇത് ഉറപ്പാക്കുന്നു.

എപ്പോഴാണ് shorthand ഉപയോഗിക്കുന്നത് പ്രായോഗികമാകുന്നത്?

ഒറ്റ ഭാഷയിലുള്ള പ്രോജക്റ്റുകൾക്ക് shorthand ഇപ്പോഴും ആകർഷകമാണ്. ഒരു ശുദ്ധമായ npm ഇക്കോസിസ്റ്റത്തിനുള്ളിൽ, ^1.2.3 എന്നത് “ഭാവിയിലെ minor, patch റിലീസുകളുമായി പൊരുത്തപ്പെടുന്നവ” എന്നതിനെ ലളിതമായി സൂചിപ്പിക്കുന്നു. ഉപയോഗിക്കുന്ന ടൂൾ മാറില്ലെന്ന് ഉറപ്പുള്ള സാഹചര്യത്തിൽ Cargo-യുടെ കെയററ്റോ Composer-ന്റെ ടിൽഡോ ഉപയോഗിക്കുന്നതും ശരിയാണ്. എന്നാൽ ഒരേ മാണിഫെസ്റ്റ് (manifest) വിവിധ ഭാഷകൾക്കിടയിൽ ഉപയോഗിക്കുമ്പോഴോ, അല്ലെങ്കിൽ ഒരു CI ജോബ് ഒന്നിലധികം ഇക്കോസിസ്റ്റങ്ങളിൽ നിന്ന് ഡിപെൻഡൻസികൾ (dependencies) എടുക്കുമ്പോഴോ ആണ് അപകടം സംഭവിക്കുന്നത്.

ചുരുക്കത്തിൽ

വിവിധ ഭാഷകൾക്കിടയിലുള്ള പൊരുത്തപ്പെടലിനായി (cross-language compatibility) semver shorthand-നെ ആശ്രയിക്കുന്നത് ഒരു ചൂതാട്ടമാണ്. ഒരു വേർഷൻ കൺസ്ട്രയിന്റ് (version constraint) എല്ലായിടത്തും ഒരേ അർത്ഥം നൽകുന്നു എന്ന് ഉറപ്പാക്കാനുള്ള ഏക വിശ്വസനീയമായ മാർഗ്ഗം വ്യക്തമായ inequality-കൾ എഴുതുക എന്നതാണ്. നിങ്ങൾക്ക് ചുരുങ്ങിയ രീതിയിൽ എഴുതണമെന്നുണ്ടെങ്കിൽ ഒരു ഇക്കോസിസ്റ്റത്തിനുള്ളിൽ മാത്രം ഒതുങ്ങിനിൽക്കുക; എന്നാൽ കൃത്യതയാണ് (consistency) വേണ്ടതെങ്കിൽ shorthand ഒഴിവാക്കി വ്യക്തതയ്ക്ക് മുൻഗണന നൽകുക.