7 पॉइंट द्वारा GN⁺ 2025-05-10 | 3 टिप्पणियां | WhatsApp पर शेयर करें
  • Rust का dependency management system development को सुविधाजनक बनाता है, लेकिन dependencies की संख्या और उनकी quality अब चिंता का विषय है
  • जिन Crate का अच्छा-खासा इस्तेमाल होता है, वे भी ज़रूरी नहीं कि up-to-date हों; कई बार उन्हें खुद implement करना बेहतर हो सकता है
  • Axum, Tokio जैसे मशहूर Crate जोड़ने के बाद, dependencies सहित पूरे codebase की lines of code 36 लाख तक पहुँच गईं, जिसे संभालना मुश्किल है
  • मैंने वास्तव में जो code लिखा है वह सिर्फ़ लगभग 1,000 lines का है, लेकिन आसपास के code की review और audit करना practically संभव नहीं है
  • Rust standard library को बढ़ाने या core infrastructure को कैसे implement किया जाए, इसका कोई स्पष्ट solution नहीं है; community को performance, safety और maintainability के बीच संतुलन पर मिलकर सोचना होगा

Rust dependency समस्या का अवलोकन

  • Rust मेरी सबसे पसंदीदा language है, और इसकी community और usability बेहद शानदार हैं
  • development productivity ऊँची है, लेकिन हाल के समय में dependency management को लेकर चिंता बढ़ी है

Rust Crate और Cargo के फ़ायदे

  • Cargo के ज़रिए package management और build automation संभव है, जिससे productivity बहुत बढ़ जाती है
  • अलग-अलग operating systems और architectures के बीच जाना आसान हो जाता है, और files को manually manage करने या build tools configure करने की ज़रूरत नहीं पड़ती
  • अलग से package management की चिंता किए बिना सीधे code लिखना शुरू किया जा सकता है

Rust Crate management के नुकसान

  • package management पर कम ध्यान देने की वजह से stability पर भी कम ध्यान रह जाता है
  • उदाहरण के लिए, मैंने dotenv crate इस्तेमाल किया, लेकिन बाद में Security Advisory से पता चला कि उसका maintenance बंद हो चुका है
  • विकल्प crate (dotenvy) पर विचार करने के बजाय, मैंने जो हिस्सा वास्तव में चाहिए था उसे लगभग 35 lines में खुद implement कर लिया
  • कई languages में packages के unmaintained हो जाने की समस्या बार-बार आती है, इसलिए असली समस्या dependency पर अनिवार्य निर्भरता है

Dependencies से code की मात्रा में तेज़ बढ़ोतरी

  • मैं Tokio, Axum जैसे Rust ecosystem के महत्वपूर्ण और high-quality packages इस्तेमाल कर रहा हूँ
  • dependency के रूप में Axum, Reqwest, ripunzip, serde, serde_json, tokio, tower-http, tracing, tracing-subscriber जोड़े गए
  • मुख्य उद्देश्य web server, file extraction और logging है, इसलिए project ख़ुद काफ़ी simple है
  • Cargo vendor feature का उपयोग करके सभी dependent crates को locally download किया गया
  • tokei से lines of code का analysis करने पर, dependencies सहित यह संख्या लगभग 36 लाख lines निकली (vendored crates को छोड़ने पर लगभग 11,136 lines)
  • तुलना के लिए, पूरा Linux kernel लगभग 2.78 करोड़ lines का माना जाता है; यानी मेरा छोटा-सा project भी उसका करीब सातवाँ हिस्सा बन जाता है
  • मेरे द्वारा लिखा गया वास्तविक code सिर्फ़ लगभग 1,000 lines का है
  • इतनी बड़ी मात्रा में dependency code की निगरानी और audit करना practically असंभव है

समाधान को लेकर विचार

  • फिलहाल कोई स्पष्ट समाधान नज़र नहीं आता
  • कुछ लोग Go की तरह standard library का विस्तार करने की बात करते हैं, लेकिन इससे maintenance burden जैसी नई समस्याएँ भी पैदा होती हैं
  • Rust का लक्ष्य high performance, safety और modularity है, और यह embedded systems या C++ से प्रतिस्पर्धा करना चाहता है, इसलिए standard library को बढ़ाने में सावधानी ज़रूरी है
  • उदाहरण के लिए, Tokio जैसा उन्नत runtime भी Github और Discord पर बहुत सक्रिय रूप से maintain किया जाता है
  • व्यवहारिक रूप से async runtime या web server जैसे core infrastructure को खुद implement करना किसी individual developer के लिए बहुत कठिन है
  • बड़ी service Cloudflare भी tokio और crates.io dependencies का वैसे ही उपयोग करती है, लेकिन वह audit कितनी बार करती है, यह स्पष्ट नहीं है
  • Clickhouse ने भी binary size और crates की संख्या से जुड़ी समस्याओं का उल्लेख किया है
  • Cargo के साथ final binary में शामिल होने वाली lines of code को सटीक रूप से पहचानना मुश्किल है, और platform-specific अनावश्यक code भी शामिल हो सकता है
  • अंत में, पूरी community से ही जवाब माँगना पड़ रहा है

3 टिप्पणियां

 
codemasterkimc 2025-05-11

Trivy चलाकर देखें तो js NPM या Java Maven की तुलना में high या critical काफ़ी कम हैं और ज़्यादा सुरक्षित लगता है, तो यह लेख Rust के बारे में आखिर क्या कहना चाहता है?

 
GN⁺ 2025-05-10
Hacker News की राय
  • मेरी राय में, ऐसी प्रणाली जहाँ dependency जोड़ना "आसान" हो और आकार या लागत की कोई सज़ा न हो, वह अंततः dependency समस्या तक पहुँचती ही है। पिछले 40 वर्षों में software deployment के तरीकों को देखें तो, 80 के दशक में libraries पैसे देकर खरीदी जाती थीं, और capacity-constrained environments के हिसाब से सिर्फ़ ज़रूरी हिस्से चुने जाते थे। आज libraries के ऊपर libraries चढ़ती जाती हैं। import foolib की एक लाइन से काम हो जाता है, और उसके अंदर क्या है इसकी किसी को परवाह नहीं होती। हर स्तर पर शायद सिर्फ़ 5% functionality चाहिए होती है, लेकिन tree जितना गहरा होता है, बेकार code उतना जमा होता जाता है। नतीजा यह कि एक साधारण binary 500MiB की हो जाती है, और सिर्फ़ number formatting के लिए भी dependency खींच ली जाती है। Go या Rust सब कुछ एक ही फ़ाइल में ठूँसने को बढ़ावा देते हैं, इसलिए अगर आप सिर्फ़ कुछ हिस्सा इस्तेमाल करना चाहें तो मुश्किल हो जाती है। लंबी अवधि में असली समाधान ultra-fine-grained symbol/dependency tracking है, जहाँ हर function/type सिर्फ़ वही ज़रूरी तत्व घोषित करे, जिससे बिल्कुल आवश्यक code ही लिया जाए और बाकी फेंक दिया जाए। मुझे व्यक्तिगत रूप से यह विचार पसंद नहीं है, लेकिन dependency tree से पूरा ब्रह्मांड खींच लाने वाली मौजूदा व्यवस्था का समाधान मुझे इसी में दिखता है
    • मैं कॉलेज का छात्र हूँ इसलिए शायद ठीक से न जानता होऊँ, लेकिन Rust compiler पहले से ही unused code, variables, functions वगैरह पहचान लेता है। IDE भी ज़्यादातर भाषाओं में यह कर लेते हैं। तो फिर बस ऐसे हिस्से हटा देना काफ़ी नहीं है क्या? जो code इस्तेमाल नहीं होता, वह compile नहीं होता
    • वास्तव में मैंने Rust में अपेक्षाकृत भारी dependency tree वाली library (Xilem) पर काम करते हुए feature flag से trimming की कोशिश की थी, लेकिन लगभग सभी dependencies ऐसी थीं जिन्हें ज़रूरी functionality के हिसाब से बनाए रखना पड़ता था (vulkan, PNG decoding, unicode shaping आदि)। अनावश्यक dependencies ज़्यादातर बहुत छोटी थीं, और serde_json को ही छोटे बदलाव से हटाया जा सका। बड़ी dependencies (winit/wgpu आदि) हटाने के लिए architecture बदलना पड़ता, इसलिए उन्हें आसानी से नहीं हटाया जा सकता
    • Go या C#(.NET) इसके अच्छे counterexample हैं। इनके पास Rust या JS(Node) जितना ही प्रभावी package management और ecosystem है, लेकिन dependency hell अपेक्षाकृत कम है। वजह है उनकी शानदार standard library। standard library की यह व्यापकता वही चीज़ है जिसमें सिर्फ़ बड़े enterprise (Google, Microsoft) निवेश कर सकते हैं
    • तो फिर मौजूदा compiler unused code को हटाता क्यों नहीं?
    • पहले हर function के लिए .o file बनती थी और उन्हें .a archive में बाँधा जाता था, फिर linker सिर्फ़ ज़रूरी function निकालकर इस्तेमाल करता था। namespacing भी foolib_do_thing() जैसे तरीके से होती थी। अब god object pattern की तरह top-level object में सारे functions रख दिए जाते हैं, इसलिए foolib import करते ही सब कुछ खिंच आता है। ऐसी स्थिति में linker के लिए यह तय करना कठिन हो जाता है कि कौन-सा function वास्तव में ज़रूरी है। इसके उलट Go dead code elimination बहुत अच्छी तरह करता है, इसलिए जो इस्तेमाल नहीं होता वह compiled output से कट जाता है
    • आधुनिक compiler और linker पहले से ही symbol extraction और dead code elimination करते हैं, और Rust भी min-sized-rust जैसे projects के ज़रिए इसे support करता है
    • पहले सभी libraries को project में शामिल करके सीधे build files में integrate करके manage किया जाता था। इसमें बहुत मेहनत और झंझट था, लेकिन deps file में एक लाइन जोड़ने की तुलना में यह कहीं ज़्यादा गहरा संवाद पैदा करता था
    • Go वास्तव में single file पर अड़ा नहीं रहता; वह logical file splitting को भी बहुत आसानी से support करता है। यही बात मुझे उसमें सच में पसंद है
    • Dotnet पहले से Trimming और Ahead Of Time Compilation के ज़रिए इस विचार को लागू कर रहा है। दूसरी भाषाएँ Dotnet से सीख सकती हैं
    • LTO(Link Time Optimization) के साथ binary size के नज़रिए से यह समस्या पूरी तरह हल हो जाती है। unused हिस्से optimization में हट जाते हैं। build time फिर भी लगता है
    • मेरे हिसाब से समस्या library अपने आप में नहीं है, बल्कि dependency जोड़ने के बाद उसके अंदर क्या और कितना इस्तेमाल हो रहा है, इस पर visibility की कमी है। ऐसा environment चाहिए जो हर package के performance/build overhead/extra code ratio जैसी चीज़ों पर आसानी से feedback दे सके
    • Unison नाम की भाषा इस विचार से आंशिक रूप से मिलती-जुलती पद्धति अपनाती है। हर function AST structure के आधार पर define होता है और hash-based global registry से लाकर reuse किया जाता है
    • npm की isEven, isOdd, leftpad जैसी छोटी-छोटी library pieces की distributed maintenance से बेहतर है कि किसी federated team द्वारा managed बड़ी general-purpose library हो, क्योंकि वह भविष्य-स्थिरता और continuity कहीं ज़्यादा देती है
    • ultra-fine-grained symbol/dependency के पीछे जाने के बजाय ultra-fine module composition और मौजूदा tree-shaking systems का उपयोग भी एक विचार है
    • Go का वास्तविक dependency management लेख में बताए गए ideal के अधिक क़रीब है। module असल में packages का संग्रह है, और vendoring के समय सिर्फ़ वास्तव में इस्तेमाल होने वाले packages और symbols ही शामिल किए जाते हैं (हालाँकि यह ठीक symbol level पर काम करता है या नहीं, मुझे पक्का नहीं)
    • JS module system ठीक ऐसा ही ultra-fine-grained symbol management और tree shaking support करता है
    • मूल रूप से प्रस्तावित ultra-fine-grained dependency idea Rust में पहले से --gc-sections जैसी section splitting के ज़रिए हल हो रहा है
    • Rust, crate feature के ज़रिए API splitting में बहुत अच्छा है, इसलिए fine-grained import इसमें बहुत अच्छी तरह संभव है। यह Go से अलग है
    • architecture पर निर्भर करता है; जैसे अगर local-first thick client हो, तो पहली install 800MB की भी हो, तब भी वास्तविक उपयोग में network पर बहुत सीमित communication ही चाहिए, इसलिए यह समस्या नहीं बनती। UI में collaboration के लिए बार-बार आने वाली बड़ी dependencies भी स्वीकार्य हो सकती हैं
    • code reuse के लिए dependency का इस्तेमाल ही सबसे अच्छा तरीका है। optimization सिर्फ़ वहीं करें जहाँ वास्तव में ज़रूरत हो
    • 80 के दशक में ही reusable software components की अवधारणा Objective-C जैसी भाषाओं के माध्यम से वास्तविकता बन चुकी थी। Rust की बड़ी सफलताओं में से एक यह है कि system programming languages में भी ऐसी software componentization व्यापक रूप से अपनाई गई
    • tree shaking से size/code bloat की समस्या कुछ हद तक हल हो सकती है (servers में तो इसकी परवाह ही नहीं की जाती)। ज़्यादा गंभीर समस्या dependency supply-chain risk और security है। बड़ी कंपनियों में open source इस्तेमाल करने के लिए approval process होता है। सिर्फ़ granularity बढ़ा देने से कोई सुरक्षा लाभ नहीं, अगर 1000 features 1000 अलग NPM authors से आ रहे हों
    • अगर package abstraction की हर layer सिर्फ़ 50% उपयोगी हो, तो हर layer पर कुल size वास्तविक ज़रूरत की तुलना में 2 गुना बढ़ता है। 3 layers के बाद 88% code बेकार हो जाता है। उदाहरण: Windows 11 calculator के साथ बेकार libraries (यहाँ तक कि account recovery tools) भी आ जाते हैं। यह उस स्थिति का उदाहरण है जहाँ feature add करना आसान होना complexity बढ़ने में बदल जाता है
    • मैं dependency accumulation को समस्या मानने से सहमत हूँ। इस समय सबसे अच्छी रक्षा यही है कि system dependencies को बेहद सख़्ती से manage किया जाए। 10 lines के function के लिए external library लाने के बजाय कभी-कभी code सीधे copy-paste कर लेते हैं। healthy library ecosystem वास्तव में दुर्लभ है। Junior engineers जब dependencies लापरवाही से जोड़ते हैं, तो उन्हें तुरंत रोका जाना चाहिए
    • Rust की बुनियादी समझ तक न रखते हुए इतने निर्णायक ढंग से बोलते हुए किसी को देखे हुए काफ़ी समय हो गया
    • dead code elimination की वजह से Rust जैसी compiled language में dependency tree बड़ा होने का मतलब binary bloat नहीं होता
  • npm ecosystem में मुझे जो समस्या दिखती है, वह यह है कि बहुत से developers design पर सोचे बिना dependencies उठा लेते हैं। उदाहरण के लिए glob library ideally सिर्फ़ एक साधारण globbing function होनी चाहिए, लेकिन author ने उसके साथ command-line tool भी बाँध दिया और एक बड़ा parser dependency के रूप में जोड़ दिया। इससे बार-बार "dependency out-of-date" warnings आती हैं। साथ ही glob library की responsibility boundary भी विवाद का विषय है। सिर्फ़ string pattern matching करना अधिक flexible design है (testing या filesystem abstraction के लिए आसान)। कई users एक सर्वशक्तिमान "Do everything" library चाहते हैं, लेकिन ऐसा करने पर side effects भी बढ़ते हैं। मुझे लगता है Rust भी इससे बहुत अलग नहीं होगा
    • design sense महत्वपूर्ण है, और अच्छी language ऐसे developer taste को support या hinder नहीं करती। Rust, Zig, C आदि इसके उदाहरण हैं। इनमें समस्या सांख्यिकीय रूप से कम होती है। जब developers की 'भीड़' इकट्ठा होती है, तो "हर किसी को आज़ादी" से crates जोड़ने वाला "bazaar model" बनता है। अंततः मैं चाहता हूँ कि Rust के पास भी एक official standard library हो (जैसे stdlib::data_structures::automata::weighted_finite_state_transducer आदि) और सुव्यवस्थित namespaces के साथ "batteries included" configuration हो। language के अंदर ही version management और backward compatibility built-in है, इसलिए भविष्य में सुधार की उम्मीद है
    • POSIX glob function वास्तव में filesystem को traverse करता है। string matching के लिए fnmatch है। आदर्श रूप से fnmatch अलग module में हो और glob उसकी dependency हो। अगर glob को सीधे implement करना चाहें तो यह काफ़ी कठिन है, क्योंकि directory structure, brace expansion जैसी जटिल requirements होती हैं, इसलिए अच्छी तरह डिज़ाइन किए गए functions के संयोजन की ज़रूरत होती है
    • Rust में borrow checker ने कमज़ोर design sense वाले developers के ख़िलाफ़ एक तरह की ढाल का काम किया है। यह असर कब तक रहेगा, कहना मुश्किल है
    • Rust की एक बड़ी ताक़त यह है कि उसके developers आम तौर पर काफ़ी सक्षम होते हैं, और crates की quality भी ऊँची होती है
    • Bun में भी glob functionality शामिल है
  • Rust को अलग से निशाना बनाने की ज़रूरत नहीं; dependency समस्याएँ और supply-chain attacks पहले से वास्तविक हैं। अगर नई language डिज़ाइन करनी हो, तो उसमें capability system built-in होना चाहिए ताकि पूरे library tree को सुरक्षित रूप से isolate किया जा सके। उदाहरण के लिए image loading library डिज़ाइन करते समय, उसे files की जगह सिर्फ़ stream लेने वाली बनाया जाए, या साफ़-साफ़ बताया जाए कि उसे "file खोलने की permission नहीं है", ताकि ख़तरनाक functions का उपयोग compile time पर रोका जा सके। मौजूदा ecosystems में यह आसान नहीं है, लेकिन सही ढंग से किया जाए तो attack surface कम किया जा सकता है। सिर्फ़ dependency minimization culture से मूल समस्या हल नहीं होती, और Go जैसी languages भी supply-chain attacks से मुक्त नहीं हैं
    • Sans-IO (ऐसा design जहाँ dependency खुद सीधे IO न करे) culture को सक्रिय रूप से फैलाने की ज़रूरत है। नई libraries जारी होने पर अगर वे direct IO implement करती हैं तो उसकी आलोचना करने की संस्कृति भी चाहिए। बेशक सिर्फ़ सार्वजनिक समीक्षा काफ़ी नहीं होगी, लेकिन अगर Sans-IO सिद्धांत फैलें तो अच्छा होगा
    • उदाहरण के तौर पर WUFFS नाम की एक special-purpose language है। वह वास्तव में Hello world तक print नहीं कर सकती, उसमें string type भी नहीं है। लेकिन वह untrusted file format parsing के लिए विशेष रूप से बनी है। ऐसी special-purpose languages और होनी चाहिए। वे तेज़ भी हैं, जोखिम भी कम है, इसलिए अनावश्यक checks भी घटते हैं
    • Java और .NET Framework में decades पहले partial trust/capabilities जैसी सुविधाएँ थीं, लेकिन उनका व्यापक उपयोग नहीं हुआ और वे अंततः हटा दी गईं
    • Rust में भी थोड़ा-बहुत ऐसा रुझान है। #![deny(unsafe_code)] के ज़रिए unsafe code इस्तेमाल होने पर compile error रोकी जा सकती है और यह बात user को बताई जा सकती है। हालाँकि यह पूर्ण enforcement नहीं है; विशेष अनुमति देकर unsafe code फिर भी इस्तेमाल किया जा सकता है। कल्पना की जा सकती है कि standard library की functionality को transitive रूप से नियंत्रित करने वाला कोई capability system, feature flag की तरह, जोड़ा जाए
    • मैं ख़ुद ऐसा कुछ बनाना चाहता हूँ और उम्मीद है कि कभी यह वास्तविकता बने। Rust में linter-based capability tracking आंशिक रूप से संभव है। compiler की unsoundness issues को भी हल करना होगा
    • मौजूदा languages/ecosystems में पूरी तरह static enforcement लाना कठिन है, लेकिन सिर्फ़ runtime validation से भी ज़्यादातर लाभ मिल सकता है। अगर library code source से compile हो रहा हो, तो हर system call के आसपास permission-check wrapper लगाया जा सकता है। violation होने पर panic कराया जाए, और हर library के लिए capability profile लिखने/वितरित करने का प्रयास हो। TypeScript में इस तरह की कुछ चीज़ें पहले ही साबित हुई हैं
    • Haskell अपने IO monad के ज़रिए इस दिशा में कुछ हद तक यह हासिल करता है। जो functions सीधे IO नहीं कर सकते, उनकी type signature से ही यह constraint लागू हो जाता है
    • मेरी समझ में, ऐसी व्यवस्था के लिए OS के साथ interaction का तरीका ही पूरी तरह बदलना पड़ सकता है। stream पढ़ना भी असल में file read system calls का उपयोग कर सकता है, यही असली उलझन है
    • Capslock नाम का project Go में इस विचार के क़रीब काम करता है
    • अगर libraries को system API import करने से entry program स्तर से ही रोका जाए, तो सिर्फ़ dependency injection से capability pass करना संभव हो जाता है। यह मौजूदा languages में भी डिज़ाइन किया जा सकता है, लेकिन व्यवहारिक समस्या यह है कि पुराने libraries के साथ compatibility टूट जाती है
    • जानना दिलचस्प होगा कि क्या इस विचार जैसा कुछ पहले कहीं implement हुआ है। मौजूदा languages में इसे लागू करना बहुत कठिन लगता है
    • एक ही language से काम नहीं चलेगा; multi-language ecosystem चाहिए
    • TypeScript ecosystem में, उदाहरण के लिए, अगर file operations class उपलब्ध न हो तो compile fail हो जाता है, और इस तरह natural restriction लग जाती है
  • यह आधुनिक software development की सामान्य समस्या है। entry barrier कम हो गई है और existing code का reuse बढ़ गया है। dependency अपने आप में untrusted code ही है। अगर कोई तकनीकी समाधान नहीं है, तो किसी न किसी को लगातार code review, maintenance, और social/legal trust systems बनाए रखने होंगे। Rust stdlib में चीज़ें लाने का मतलब होगा कि central team उस पूरे codebase की ज़िम्मेदारी ले, जिससे maintenance burden बढ़ता है
    • हर language में इसकी तीव्रता अलग दिखती है। जिन languages की standard library मज़बूत होती है, वे कम external dependencies के साथ भी बहुत कुछ कर सकती हैं। JS/Node जैसी languages, जिनमें built-in functionality कम है, बाहरी dependencies पर स्वाभाविक रूप से निर्भर होती हैं। "हल्कापन" हमेशा अच्छी चीज़ नहीं होता
    • मेरे हिसाब से Rust में और standard library integration की ज़रूरत है। Go की standard library शानदार है, जबकि Rust में basic functionality (web, tls, x509, base64 आदि) के लिए भी library चुनना और manage करना कष्टदायक है
    • Gilad Bracha ने third-party library sandboxing के लिए एक दिलचस्प तरीका सुझाया था: import हटा दो और सब कुछ dependency injection के ज़रिए दो। अगर IO subsystem जैसी चीज़ inject ही न की जाए, तो 3rd party code कभी वहाँ पहुँच ही नहीं सकता। अगर सिर्फ़ read functionality देनी हो तो सिर्फ़ वही wrap करके inject की जा सकती है। हालाँकि system programming domain में इसकी सीमाएँ हैं (unsafe code आदि के कारण)
    • QubesOS जैसी व्यवस्था भी प्रस्तावित की गई है, जहाँ सभी libraries को isolated environment में चलाया जाए, अपना code dom0 में हो, हर library अलग template VM में हो, और communication network namespaces से हो। sensitive industries में यह व्यावहारिक हो सकता है
    • मेरे देखने में, हम ज़्यादा जटिल काम नहीं कर रहे, बल्कि वही काम ज़्यादा जटिल तरीक़े से कर रहे हैं। लक्ष्य अपने आप में कठिन नहीं हुआ है
    • वास्तव में भाषा के हिसाब से स्थिति बदलती है। C/C++ में dependencies जोड़ना कठिन है, और cross-platform support चाहिए तो और भी झंझट है, इसलिए वैसी समस्या उतनी नहीं दिखती
    • असल complex चीज़ है अनावश्यक code bloat। लगभग हर project अनावश्यक complexity और overengineering से भरा है। यही इस industry की समस्या है
  • blessed.rs standard library में शामिल करना कठिन लेकिन उपयोगी libraries की सूची recommend करता है। मुझे यह व्यवस्था पसंद है क्योंकि इसकी वजह से अधिकांश packages किसी विशेष उद्देश्य तक सीमित रहते हैं और manage किए जा सकते हैं
    • cargo-vet भी recommend करने लायक है। यह trusted packages को track/define करने देता है; जैसे कुछ packages के लिए import से पहले expert audit ज़रूरी हो, जबकि tokio maintainers द्वारा managed packages को सीधा trust कर लिया जाए — ऐसे semi-YOLO policies तक संभव हैं। यह blessed.rs से थोड़ा ज़्यादा formal है, और टीम के भीतर एक official quasi-standard list साझा करने के लिए अच्छा माध्यम है
    • काश Python में भी ऐसा system होता
    • मैंने review किया, और यह वाक़ई काफ़ी अच्छा recommendation project है
  • leftpad घटना के बाद package managers के प्रति नकारात्मक धारणा बची हुई है। tokio जैसी चीज़ें तो लगभग language-level functionality हैं, इसलिए अगर OP यह कह रहा है कि Go के पूरे ecosystem या Node के V8 तक का भी direct audit करना चाहिए, तो यह अव्यावहारिक है
    • वास्तव में tokio का भी कोई न कोई लगातार audit करता है। बहुत लोग नहीं करते, लेकिन कोई तो करता ही है
    • जब दो dependencies अलग-अलग versions इस्तेमाल करती हैं, तो cargo दोनों versions को साथ शामिल कर लेता है — यह एक अनोखी सुविधा है जिसे cargo विशेष रूप से support करता है
  • cargo packages में feature flag वास्तव में बहुत अच्छी चीज़ है। मैं अक्सर ऐसे PR डालता हूँ जो अनावश्यक dependencies को इन flags के पीछे छिपा देते हैं। cargo tree से dependency tree आसानी से देखा जा सकता है। binary में वास्तव में जाने वाली code lines का view बहुत मायने नहीं रखता, क्योंकि function inlining होने पर ज़्यादातर चीज़ें main में मिल जाती हैं
    • अफ़सोस है कि npm में feature flag नहीं है। पता नहीं कोई ऐसा package manager है या नहीं जो यह support करता हो। मैं internal libraries के अंदर किसी specific framework पर निर्भर code को isolate करके extend करना चाहता हूँ
  • मैं भी ऐसा ही महसूस करता हूँ। Cargo से dependency जोड़ना इतना आसान है कि अगर मैं खुद सावधान रहूँ तब भी कुछ ही additions के बाद दर्जनों transitive dependencies साथ आ जाती हैं। इसका मतलब यह नहीं कि उनका इस्तेमाल बंद कर दिया जाए; वह यथार्थवादी नहीं होगा। C++ में यह स्थिति कम दिखती है। Rust में छोटे-छोटे packages में विभाजन बहुत है, इसलिए इंटरनेट से random code उठाने जैसा महसूस होता है। Rust मुझे पसंद है, लेकिन यह संरचना मुझे पसंद नहीं
    • Rust subreddit में linked post में कहा गया था कि C++ में dependencies कम दिखने का एक कारण यह है कि वे अक्सर dynamic libraries के रूप में दी जाती हैं। बल्कि OS package manager की stability/security management क्षमता पर निर्भर होना भी एक लाभ है। Rust के लिए शायद standard library extension जैसी कोई अवधारणा उपयोगी हो सकती है
    • C++ dependencies जटिल हैं और build systems बुरी हालत में हैं, इसलिए मुझे Rust-style less-stable dependencies ज़्यादा बेहतर लगती हैं। असल C++ transitive dependencies precompiled रूप में होती हैं, इसलिए वे और भी कम दिखाई देती हैं
    • Rust में छोटे package splits कोई 'philosophy' कम और build speed की मजबूरी ज़्यादा हैं। project बड़ा होने पर उसे crates में तोड़ना पड़ता है। यह abstraction के लिए नहीं, बल्कि build performance के कारण मजबूरन किया गया restructuring है
    • "तो फिर खुद मत इस्तेमाल करो" वाली बात से हर बार सहमत होना ज़रूरी नहीं। इस पर और सोचना चाहिए
    • C++ और CMake इतने कठिन हैं कि बहुत-सा software वास्तव में इसी वजह से इस्तेमाल ही नहीं किया जाता
  • core libraries के लिए open source libraries इस्तेमाल करता हूँ, और छोटे features के लिए open source code देखकर सीधे अपनी codebase में copy-paste कर लेता हूँ। इससे code कुछ हद तक अनावश्यक रूप से बड़ा हो जाता है, लेकिन external code review का बोझ और supply-chain exposure घटता है। बड़ी libraries फिर भी समस्या रहती हैं, लेकिन सब कुछ खुद से लिखना संभव नहीं। यह सिर्फ़ Rust की समस्या नहीं, व्यापक समस्या है
  • अतीत में (दूसरी languages में) मैंने महत्वपूर्ण systems के लिए खुद module/package minimization policy बनाई थी, और जो भी packages इस्तेमाल होते थे, उन्हें internal repository में mirror करके हर branch और update पर audit किया जाता था। frontend जैसे क्षेत्रों में इतनी सख़्त व्यवस्था व्यावहारिक नहीं होती। हाल के शोरगुल वाले open source AI tools/models के साथ भी dependency management को लेकर ऐसी ही चिंताएँ हैं। Rust में personal projects करते हुए भी UI/async libraries की dependency explosion सबसे असहज लगती है। उनमें से एक भी vulnerable हुई तो breach हो जाएगा; यह बस समय की बात है
    • व्यावहारिक समाधान यही है कि CI/CD systems को सिर्फ़ आधिकारिक internal repositories से जोड़ा जाए। developers local में कुछ भी install कर सकते हैं, लेकिन unauthorized commit build server पर रुक जाएगा
    • security risk हल करने के लिए RFCs भी हैं, लेकिन शायद cultural कारणों से तेज़ बदलाव नहीं हो रहा
    • Rust की एक अच्छी बात यह है कि async भी अपनी पसंद के तरीके से खुद implement किया जा सकता है। किसी एक खास implementation से बँधना ज़रूरी नहीं
 
iolothebard 2025-05-11

यह सिर्फ Rust की ही समस्या नहीं है.
public package repository और transitive dependency को support करने वाले package manager वाली हर language का यह एक साझा फ़ायदा भी है और संभावित समस्या भी.
आखिरकार, इन्हें लाकर इस्तेमाल करने वालों को ही सही तरीके से इस्तेमाल करना चाहिए…
Node&npm के leftpad मामले के बावजूद कुछ भी नहीं बदला.