- Rust ecosystem में जैसे ही कोई abandoned dependency RUSTSEC में चढ़ती है, वैसे ही सीधे तौर पर प्रभावित न होने वाली लाइब्रेरी भी users और CI के ज़रिए तकनीकी कर्ज़ बन जाती है
instaजिसyaml-rustपर निर्भर था, उसमें मूल लेखक की रुचि कम होने के बाद feature requests और bugs जमा होते गए थे- RUSTSEC में दर्ज होने के बाद direct और indirect users के CI fail होने लगे, और वित्तीय उपमा में इसे downgrade और margin call जैसा कहा जा सकता है
- alternative libraries या forks भी स्पष्ट समाधान नहीं थे, क्योंकि dependency बदलने पर भी maintenance burden और नई dependency risk बनी रहती है
- अंतिम प्रतिक्रिया
yaml-rustकोड कोinstaके भीतर vendoring करने की थी, और इस पर आलोचना हुई कि यह खराब तकनीकी कर्ज़ कोAAAपैकेजिंग में बदल देने वाले CDO जैसा है
yaml-rust dependency के तकनीकी कर्ज़ के रूप में सामने आने की प्रक्रिया
instayaml-rustपर निर्भर था, और मूल लेखक की रुचि खत्म होने के बादyaml-rustमें issues लगातार जमा होते गए थे- कुछ feature requests थे, और कुछ वास्तविक bugs थे
instamaintainer ने इन समस्याओं का सीधे सामना नहीं किया था, लेकिन maintenance बंद हो चुकी dependency होने के कारण यह तकनीकी कर्ज़ था
- जब
yaml-rustको RUSTSEC database में जोड़ने पर चर्चा शुरू हुई, तो स्थिति बदल गई- वित्तीय उपमा में RUSTSEC credit rating agency की भूमिका निभाता है
- दर्ज होने के बाद
yaml-rustको सीधे या परोक्ष रूप से इस्तेमाल करने वाले कई projects का CI कुछ ही मिनटों में fail होने लगा - users ने
instamaintainer कोyaml-rustके इस्तेमाल पर सवाल उठाना शुरू किया, जो वित्तीय उपमा में margin call जैसा था
विकल्प और वास्तविक प्रतिक्रिया
- किसी alternative पर migrate करना आकर्षक नहीं था
- एक alternative
yaml-rustका fork था, उसका केवल 1 maintainer था, और वह 3 dependencies और जोड़ता था - उनमें से एक को पहले से ही “B-” rating मिली हुई थी
- ecosystem के दूसरे विकल्प ने सवाल उठने से पहले ही default बदलने का फैसला कर लिया था
- एक alternative
- खुद fork करना भी मूल समाधान नहीं था
- fork की गई लाइब्रेरी के साथ वही maintenance requirements जुड़ी रहतीं
- अगर bug reports का जवाब न दिया जाए, तो अंततः वही स्थिति बनेगी जैसी
yaml-rustके साथ हुई - इसलिए fork कुछ समय तो खरीद सकता है, लेकिन समस्या खत्म नहीं करता
- वास्तविक प्रतिक्रिया
yaml-rustकोड कोinstaके भीतर merge कर देने वाली vendoring थी- अब
insta,instacode औरyaml-rustके मिले-जुले रूप में है - यह खराब तकनीकी कर्ज़ को
AAAमें upgrade करने जैसी संरचना है - शीर्षक का CDO 2007 की वित्तीय संकट के दौरान बदनाम हुए Collateralized debt obligation को संदर्भित करता है
- अब
- अंतिम आकलन लगभग यही है कि “कोई नहीं जीता”
- समस्या वाला code गायब नहीं हुआ, बस
instaके भीतर शिफ्ट हो गया - बाहरी dependency दिखने पर जो दबाव होता था, वह कम हुआ, लेकिन maintenance burden खुद बना हुआ है
- समस्या वाला code गायब नहीं हुआ, बस
1 टिप्पणियां
Hacker News टिप्पणियाँ
सीधी बात करें तो, सबसे लोकप्रिय YAML parser serde_yaml(https://lib.rs/crates/serde_yaml) के लेखक ने बिना किसी अग्रिम सूचना या उत्तराधिकारी maintainer तय किए अचानक हाथ खींच लिया, और इसे deprecated तथा unmaintained के रूप में चिह्नित कर दिया
यह left-pad जैसा बिल्कुल नहीं है. पैकेज अब भी काम करता है और crates.io इसे हटाने की अनुमति भी नहीं देता, लेकिन यह पैकेज 4,000 दूसरे crates में इस्तेमाल हो रहा है
audit और auto-update tools अब unmaintained crates के इस्तेमाल को समस्या के रूप में उठाएँगे
साथ ही, लोगों को बड़ी संख्या में उधर migrate करने से रोकने के लिए serde_yaml को भी unmaintained के रूप में चिह्नित किया गया
अच्छा होता अगर audit tools और company policies सिर्फ हाल की commits न होने के आधार पर “unmaintained” की चेतावनी देने के बजाय ऐसे मामलों में फर्क कर पातीं
जानना चाहूँगा कि क्या इस पर और जानकारी है
बिना किसी explanation के आए CDO संक्षेप को मैं तुरंत नहीं समझ पाया, लेकिन लेख में collateralized शब्द कई बार आता है, तो शायद इसका मतलब collateralized debt obligation है
https://en.wikipedia.org/wiki/Collateralized_debt_obligation
पहले मेरे दिमाग में chief data officer आया था
खास तौर पर 2008 में यह कई mortgages में fractional ownership जैसी चीज़ थी, और जब खराब mortgages default में गए, तो CDO भी साथ में टूट गए [1]
वैसे, लेखक जो सोच रहा है वह debt-based metaphor से ज़्यादा leftpad या लगातार होने वाले DNS outages जैसी systemic risk के करीब है. 2008 में इतना बड़ा संकट बनने की बड़ी वजह भी debt को product में बदलना भर नहीं था, बल्कि systemic problems कहीं ज़्यादा बड़ी थीं [2]
इसका यह मतलब भी है कि regulator जैसी भूमिका निभाने वाली rating agencies पहले ही capture हो चुकी हैं
इसे “जीत” कहना है या नहीं, यह “जीत” की परिभाषा पर बहुत निर्भर करता है, इसलिए उस पर बहस नहीं करना चाहता, लेकिन इसके फ़ायदे स्पष्ट हैं. जो vulnerable code paths न execute होते हैं और न ही external libraries से reachable हैं, वे अब safe code paths बन जाते हैं
बेशक यह स्थिति चिंताजनक लगती है, लेकिन सुरक्षित तो है
इस तरह की vendoring का एक और फ़ायदा है. अगर आपकी अपनी library में मज़बूत test coverage है, तो आप नई लाई गई library पर code coverage tools चला सकते हैं
library को modify करना कठिन हो सकता है, लेकिन आपकी codebase जिन हिस्सों को छूती ही नहीं, उन्हें अपेक्षाकृत आसानी से हटाया जा सकता है. यह structure पर निर्भर करता है, लेकिन अगर vulnerable code पूरी तरह हटा दिया जाए तो यह साफ़ लाभ है, और अगर पता चले कि आप वास्तव में उसका कुछ हिस्सा इस्तेमाल कर रहे थे, तो वह और भी स्पष्ट लाभ हो सकता है
यानी public maintenance के लिए library को fork करना बड़ी ज़िम्मेदारी है, लेकिन ज़रूरी हिस्सों को vendor करके उनमें बदलाव करना कहीं छोटा बोझ है. भले आप वास्तव में pruning न करें, सिर्फ यह तथ्य कि वह आसान हो जाता है, अपने आप में प्रगति है
नुकसान भी साफ़ हैं, लेकिन सब कुछ बुरा नहीं है
लेकिन अगर external dependency पर अब कोई नज़र रखने वाला ही न हो, तो वह तर्क खत्म हो जाता है, और वह dependency बोझ बन जाती है
dependency को abandoned के रूप में चिह्नित किया जाना और security reports में दिखना वही व्यवहार है जो हम चाहते हैं. इससे आप जानकारी के आधार पर तय कर सकते हैं कि vendoring करनी है, यानी “कोई malicious actor चुपके से कुछ घुसा दे” वाले जोखिम को हटाना है, या कोई और विकल्प चुनना है
यह भी अच्छी बात है कि Rust build system इसे आसानी से संभव बनाता है
आम तौर पर पूरे विशाल monorepo में उस dependency का सिर्फ एक version अनुमति पाता है. हो सकता है मेरे वहाँ होने के बाद यह बदल गया हो
और हर third_party dependency के लिए एक designated owner या OWNERS होते हैं
इससे चीज़ें काफ़ी हद तक व्यवस्थित रहती हैं
हालाँकि, ऐसी enforced discipline Google जैसे संगठनों में ठीक बैठ सकती है, जहाँ समय और पैसा पर्याप्त हो और जो “move fast and break things” वाली philosophy से बँधे न हों. startups में यह कितना कारगर होगा, पता नहीं
एक अर्थ में, दूसरी libraries लगभग यादृच्छिक calls उपलब्ध कराने वाले directed fuzz tests जैसी होती हैं, इसलिए यह एक दिलचस्प testing strategy हो सकती है
JS npm ecosystem में भी यही pattern देखा गया है
npm audit सुरक्षा समस्याओं में अक्सर झूठा अलार्म साबित होता है, और अगर license अनुमति देता हो तो code को अपने अंदर ले आना, users के नकली issues से दबे बिना निपटने के सबसे स्थिर तरीकों में से एक है
अक्सर users context नहीं समझते, या उनके employer की policy ऐसी जगह बनाई गई होती है जो वास्तविकता से कटी हुई होती है, इसलिए वे इसे नज़रअंदाज़ नहीं कर सकते
build pipeline की transitive dependency codebase के किसी हिस्से में इस्तेमाल होने वाला regex वास्तव में denial-of-service attack के लिए exploit किया जा सके, ऐसा ज़रूरी नहीं है
गहरी transitive dependency के “issues” से बचना खास तौर पर झंझटभरा हो सकता है। संरचना के कारण अक्सर यह तकनीकी रूप से साबित करना मुश्किल होता है कि “हम कभी उस code path में जाते ही नहीं, इसलिए defect का असर हम पर नहीं पड़ता” या “उस path में जाने का एकमात्र मामला offline environment में trusted input है”
ऐसे scenarios मौजूद हैं जहाँ यह समस्या महत्वपूर्ण होती है। उदाहरण के लिए, compromised build tool build हो रही library में malicious code inject कर सकता है
लेकिन ऐसे मामले बहुत दुर्लभ हैं, और build के दौरान सिर्फ call किए जाने वाले, पर वास्तव में महत्वहीन denial-of-service वाले regex की बाढ़ में दब जाते हैं
ऊपर से सामान्य build tools की dependency tree में लगभग 50 अरब transitive dependencies जैसी स्थिति जुड़ जाए तो यह वाकई बहुत कठिन काम बन जाता है
मेरा मानना है कि ऐसे issues report करने वाले tools को “redistribute करने पर exploit हो सकता है” और “build pipeline में इस्तेमाल करने पर exploit हो सकता है” के बीच फर्क करना चाहिए
“अब घटिया technical debt अचानक AAA grade बन गया” में “अचानक” का मतलब शायद यह है कि सिर्फ वही code vendoring होने की वजह से पहले से बेहतर debt grade पा जाए, यह तर्कसंगत नहीं लगता
लेकिन वह code के अपने मूल्य तक ही सीमित नज़र है, और पूरी value proposition के सबसे महत्वपूर्ण हिस्से को छोड़ देती है
अगर maintainer code को अपने अंदर ले आता है, तो अब वह code उसी maintainer का हो जाता है। अगर किसी dead project का code किसी active maintainer द्वारा vendor किया जाता है, तो उस code की value बढ़ जाती है, क्योंकि अब कोई active person मौजूद है जो issue पर प्रतिक्रिया दे सकता है, pull request review कर सकता है, और bug fix कर सकता है
एक और उपमा लें तो यह ऐसा है जैसे उपेक्षित पालतू जानवर को नए मालिक के पास भेजना; बेहतर देखभाल मिलने से वह ज़्यादा स्वस्थ रह सकता है, लंबा जी सकता है, और उसकी value बढ़ जाती है
maintainer को भी बड़े और अपरिचित codebase के साथ अभ्यस्त होना पड़ेगा, इसलिए fix implement करने या review करने में entry barrier होगा
थोड़ा side note और विवादास्पद विचार है, लेकिन मेरा मानना है कि अगर source-based package manager, प्रकाशित package के maintenance को registry द्वारा जबरन अपने हाथ में लेने का कानूनी अधिकार सुनिश्चित नहीं करते, तो भयानक समस्याओं से बचना मुश्किल है
जैसे abandonment, malicious changes, malicious removal, और impersonation
अगर किसी package को बड़े community के लिए पर्याप्त रूप से महत्वपूर्ण माना जाए, तो package registry entry को उसके मूल मालिक से छीनकर fork की ओर point कराने का कोई तरीका होना चाहिए
स्वाभाविक है कि ऐसे कदम के साथ बहुत drama होगा, लेकिन इससे downstream users की सक्रिय रूप से रक्षा की जा सकती है
असली समस्या यह है कि open source project को maintain करने में समय और मेहनत लगती है, और खाली समय रखने वाला कोई दूसरा व्यक्ति आसानी से मिल नहीं जाता
यह उस copyright model से भी कम आकर्षक है जहाँ हर contribution अपने-आप किसी खास समूह की मिल्कियत बन जाती है
यह तर्क दिया जा सकता है कि GitHub free software host करते हुए उस software के copyright infringement के लिए विशेष रूप से अनुकूल जगह है, लेकिन कम से कम फिलहाल वह package की नाममात्र ownership छीनने की कोशिश तो नहीं करता
क्योंकि यह community की बात है, ग्राहक-आपूर्तिकर्ता संबंध नहीं, और अधिकतर packages उतने ही महत्वपूर्ण होते हैं जितना कि उनका मुफ़्त में उपलब्ध होना
यह “हम तुम्हें 10 डॉलर प्रति माह देंगे, बस हमारे लिए 100000 packages manage कर लो” जैसी अपमानजनक रकम भी बन सकती है
इंटरनेट पर unmanaged source code बहुत है, और अक्सर वह बस एक बार जैसा है वैसा प्रकाशित कर दिया गया है। मुफ़्त code के लिए किसी को updates की गारंटी नहीं मिलती
यह काफ़ी सौभाग्यशाली बात थी कि किसी ने पहले ही yaml-rust को fork करके yaml-rust2(https://github.com/Ethiraric/yaml-rust2/blob/master/document...) बना लिया था
यह भी बढ़िया है कि वह fork YAML test suite पूरी तरह pass करता है, benchmark में भी तेज़ है, और migration भी आसान लगती है
फिर भी मूल समस्या बनी रहती है। हम अभी ऐसे लोगों के काम पर निर्भर हैं जो खुशी-खुशी मुफ़्त श्रम दे रहे हैं, लेकिन यह ज़रूरी नहीं कि वह हमेशा चलता रहे
मुझे नहीं पता कि उनके समय और मेहनत का प्रतिफल देने और उनसे अच्छे काम को जारी रखने की उम्मीद करने के अलावा कोई दूसरा रास्ता है भी या नहीं
“A pure rust YAML implementation.”
बल्कि serde_yaml को pure Rust कहना और भी मुश्किल था, क्योंकि वह libyaml के c2rust-converted रूप unsafe-libyaml पर निर्भर था
पर्याप्त लंबे समय-पैमाने पर open source maintainer के project छोड़ देने की संभावना 1 होती है
इससे बचने का एकमात्र तरीका यह है कि एकल-maintainer projects को वर्जित माना जाए
Rust standard library बनाने वाले लोग standard library को सीमित रखना चाहते हैं, इसलिए Rust projects में शायद यह विकल्प न हो
लेकिन दूसरी भाषाओं में, जहाँ standard library पर्याप्त रूप से सक्षम हो, यह स्पष्ट रूप से संभव विकल्प है
मैं जानबूझकर ऐसी भाषाएँ इस्तेमाल करता हूँ जिनमें ज़रूरी चीज़ें built-in हों, इसलिए database driver के अलावा मैं लगभग कोई बाहरी library इस्तेमाल नहीं करता
यह पूरी स्थिति थोड़ी हास्यास्पद लगती है। अगर कोड काम करता है और सालों से करता आ रहा है, तो मुझे समझ नहीं आता कि उसका maintain न होना समस्या क्यों है
अगर उसे बदलने की ज़रूरत नहीं है और उसकी सीमाएँ व फीचर्स पता हैं, तो वह ठीक है
कोड अपने-आप खराब नहीं हो जाता। मैंने कई बार दशकों पुराने कोड को उधार लेकर या integrate करके अच्छे से इस्तेमाल किया है
व्यक्तिगत रूप से, मैं शायद उस लाइब्रेरी के बारे में सारी शिकायतों को बस नज़रअंदाज़ करके आगे बढ़ जाता
पहले से बहुत सारे bugs जमा हो चुके हैं, और fixes मौजूद होने के बावजूद कभी patch नहीं किए जाएंगे। https://github.com/chyh1990/yaml-rust/issues और /pulls देख लें
यानी आप उस कोड के हिस्से के maintenance की ज़िम्मेदारी अपने ऊपर ले रहे हैं जिसका आप इस्तेमाल करते हैं
कभी-कभी यह बुरा हो सकता है। यह copy-paste coding या duplicated effort बन सकता है
लेकिन अगर third-party component पूरी तरह unmaintained हो, तो यह काफ़ी समझ में आने वाली बात है
सही है, dependencies को vendor कर लेना चाहिए। जो dependencies “लगभग पूरी” हों और जिनका development व maintenance धीमा पड़ गया हो, उनके साथ पिछले 20 सालों में मैंने ज़्यादातर यही किया है
हालांकि, मैंने कभी ऐसी language में काम नहीं किया जहाँ “batteries are not included” हो
क्या
cargo vendor --aggressiveजैसा कुछ हो सकता है, ताकि मेरे crate के हिसाब से dependency के अंदर का सारा dead code prune किया जा सके?सोच रहा हूँ कि क्या इससे “dependencies को review करो” वाली समस्या को ज़्यादा संभालने लायक बनाया जा सकता है
यह लेख के मुख्य बिंदु से थोड़ा हटकर है, लेकिन इस मायने में जुड़ा हुआ भी है कि dependency चुनने और उससे जुड़ी सारी ज़िम्मेदारियाँ आख़िरकार हमारी ही होती हैं
ऐसा लगता है कि ऐसे tools के लिए जगह है जो हमें इस बात के प्रति ज़्यादा ज़िम्मेदार बनने में मदद करें कि वास्तव में crate में compile होकर क्या शामिल हो रहा है
अगर नहीं, तो मुझे समझ नहीं आता कि इससे बेहतर क्या होता है