- Riot Games तकनीकी कर्ज़ को ऐसे code या data के रूप में देखता है जिसकी कीमत भविष्य के developers को चुकानी पड़ेगी, और League of Legends के development उदाहरणों के जरिए कर्ज़ का आकलन करने के लिए एक साझा भाषा व्यवस्थित करता है
- मूल्यांकन के मानदंड तीन हैं: impact, fix cost, और contagion; खासकर contagion यह दर्शाता है कि समय के साथ कर्ज़ दूसरे systems, data, और development practices में कितना फैलता है
- कर्ज़ के प्रकार हैं: Local Debt, जो internal implementation में बंद है; MacGyver Debt, जो दो systems को अस्थायी रूप से जोड़ता है; Foundational Debt, जिसमें गहरी assumptions architecture में धँसी होती हैं; और Data Debt, जिसमें defects के ऊपर content जमा होता जाता है
- Jarvan का Cataclysm,
std::stringऔरAStringका साथ-साथ उपयोग, BlockBuilder में Lua का उपयोग, और block parameter naming bug को हर प्रकार के ठोस उदाहरण के रूप में इस्तेमाल किया गया है - कम contagion वाला कर्ज़ लंबे समय तक छोड़ा जा सकता है, लेकिन उच्च contagion वाला कर्ज़ समय के साथ fix cost और impact दोनों बढ़ा देता है, इसलिए उसकी फैलने की राह को जल्दी रोकना चाहिए
तकनीकी कर्ज़ को परखने के तीन आयाम
- तकनीकी कर्ज़ वह code या data है जिसकी कीमत “भविष्य के developers को चुकानी पड़ेगी”
- किसी खास कर्ज़ को अभी ठीक करना है, बाद में करना है, या व्यावहारिक रूप से छोड़ देना है, यह तय करने के लिए साझा measurement criteria की ज़रूरत होती है
- Riot तकनीकी कर्ज़ का मूल्यांकन तीन axes पर करता है
- impact: players और developers पर पड़ने वाला प्रभाव
- fix cost: सुधार के लिए लगने वाला समय और deployment risk
- contagion: उसे जैसा है वैसा छोड़ देने पर समस्या कितनी फैलती है
-
impact: players और developers पर दिखने वाली लागत
- players के लिए यह bugs, missing features, और unexpected behavior के रूप में सामने आता है
- developers के लिए यह implementation delay, workflow में रुकावट, और याद रखने लायक गैर-ज़रूरी details के रूप में जमा होता है
- यहाँ developers में सिर्फ engineers ही नहीं, बल्कि designers, VFX artists, और game production में शामिल दूसरे roles भी आते हैं
- कुछ कर्ज़ engineers को नया code लिखने से रोकते हैं, और कुछ designers को नई scripts लिखने या VFX artists को नए particles बनाने में बाधा डालते हैं
-
fix cost: implementation time और deployment risk
- fix cost में सिर्फ वास्तविक development time ही नहीं, बल्कि changes deploy करते समय पैदा होने वाला risk भी शामिल है
- किसी single function की simple error कुछ मिनटों में ठीक हो सकती है, लेकिन game के पूरे codebase को प्रभावित करने वाली गहरी assumptions को ठीक करने में हफ्ते या महीने लग सकते हैं
- कोई “गलत” system भी पहले से अच्छे game बनाने के tool के रूप में इस्तेमाल हो रहा हो सकता है, इसलिए उसे ठीक करते ही existing content टूट सकता है
- उदाहरण के लिए, अगर scripting engine की error handling या particle creation time की calculation बदल दी जाए, तो 140 से ज़्यादा champions के 500 से ज़्यादा skills प्रभावित हो सकते हैं
-
contagion: समय के साथ फैलने की मात्रा
- contagion का मतलब है कि तकनीकी कर्ज़ को वैसे ही छोड़ देने पर वह दूसरे systems, data, और development methods तक कितना फैलता है
- यह फैलाव problematic system के साथ interfaces, उसके ऊपर जमा data के copy/paste, और नए features को implement करने के तरीकों में बदलाव के जरिए होता है
- अच्छी तरह isolate किया गया कर्ज़ बाद में ठीक करने पर भी अभी ठीक करने की तुलना में बहुत महँगा नहीं पड़ता
- उच्च contagion वाला कर्ज़ समय बीतने के साथ और कठिन हो जाता है, और जैसे-जैसे ज़्यादा systems उस मूल compromise से संक्रमित होते हैं, उसका impact भी बढ़ता जाता है
Local Debt: अंदर से गंदा black box
- Local Debt पारंपरिक black-box programming model जैसा होता है
- बाहर से system स्थिर दिखता है, लेकिन internal implementation भयानक या उलझी हुई हो सकती है
- उदाहरण: skills, network layer, scripting engine
- अगर आसपास के systems बनाते समय internal debt के बारे में सोचने की ज़रूरत नहीं पड़ती, तो उसका contagion आम तौर पर कम होता है
-
वास्तविक दुनिया की तुलना: इंसानी आँख
- इंसानी आँख संरचना के हिसाब से image को उल्टा ग्रहण करती है, और retinal nerves हर आँख के केंद्र के पास blind spot बनाती हैं
- दिमाग का visual center data को सीधा करता है और blind spot भर देता है, ताकि बाकी दिमाग “सही” image के साथ interact कर सके
- यह विचित्रता आँख और optic nerve system तक सीमित है, और दूसरे systems इसे आसानी से avoid कर सकते हैं, इसलिए इसे “काफ़ी अच्छा” माना जा सकता है
-
League उदाहरण: Jarvan का Cataclysm
- Jarvan का Cataclysm आज भी minion के रूप में बनाया गया है
- जब designers को किसी खास जगह या जगहों के समूह पर gameplay effect लगाना होता है, तो वे “invisible minion” बनाने वाले tool का उपयोग कर सकते हैं
- RiotXypherous ने Reddit comment में यहाँ इस्तेमाल किए गए “minion” की व्याख्या की है
- ऐसे game objects script logic को track और execute करने का एक स्थिर और अच्छी तरह समझा गया तरीका हैं
- Jarvan की दीवार के लिए players को बाहर निकलने से रोकने हेतु ठीक 24 minion चाहिए होते हैं
- पहले 12 थे, लेकिन players कभी-कभी दीवारों के बीच से निकल जाते थे, इसलिए Riot Exgeniar ने संख्या 24 कर दी
- विकल्प के रूप में Cataclysm की pathability नियंत्रित करने वाली single logic unit, यानी ring-terrain structure, इस्तेमाल की जा सकती है; इससे logic साफ़ होगी और calculation cost थोड़ी कम होगी
-
Cataclysm का मूल्यांकन
- impact: 1/5
- यह तथ्य कि दीवार minion से बनी है, नया content बनाने वाले दूसरे developers पर लगभग कोई असर नहीं डालता
- “Jarvan Ult Hitch” इस debt और उस loading bug के संयोजन का परिणाम था जो missing auto-attack definition पढ़ने की कोशिश कर रहा था
- fix cost: 2/5
- अभी नए code के बिना synthetic shapes से custom geometry नहीं बनाई जा सकती
- ring आकार का “area trigger” बनाने के लिए ring collision calculation हेतु dedicated math code चाहिए
- Riot दूसरे उद्देश्यों के लिए Constructive Solid Geometry की खोज कर रहा है, जिससे fix cost काफ़ी कम हो सकती है
- contagion: 1/5
- feature development करते समय Jarvan wall implementation को ध्यान में रखने की ज़रूरत नहीं पड़ती, इसलिए यह अच्छी तरह isolate है
- contagion का जोखिम तब होता है जब कोई दूसरा designer इस implementation को copy/paste करके नए champion पर लगा दे; ऐसा वास्तव में कभी-कभी हुआ भी है
- implementation समस्या के रूप में Cataclysm का संभावित फैलाव कम है और अच्छी तरह समझा गया है
- impact: 1/5
-
Local Debt को संभालने का तरीका
- Local Debt की एक सामान्य विशेषता इसका कम contagion score है
- अगर impact, fix cost से अधिक हो, तो अच्छे engineering discipline वाले developers इसे बहुत देर तक छोड़े बिना ठीक कर देते हैं
- अगर सचमुच contagion न हो, तो इसे जितनी देर ज़रूरत हो उतनी देर छोड़ना सुरक्षित है
- ऐसा Local Debt, जो engineers की perfectionism को तो उकसाए लेकिन जिसका असर पर्याप्त रूप से व्यापक न हो, उस पर तुरंत टूट पड़ना बड़ी गलतियों में से एक है
- क्योंकि बदलाव का दायरा स्थानीय होता है, इसलिए fix verification और regression testing आम तौर पर आसान होती है
- हाल में ठीक किए गए उदाहरणों में inhibitor से जुड़े bugs, Janna’s Monsoon, और Tear of the Goddess शामिल हैं
- वह bug जिसमें कुछ स्थितियों में inhibitor champions को 0,0,0 coordinates की ओर pathing करने पर मजबूर कर देता था
- वह समस्या जिसमें Janna’s Monsoon spell shield को ignore कर देता था
- वह समस्या जिसमें Tear of the Goddess बिना mana वाले cast पर stack हो जाता था
MacGyver Debt: डक्ट टेप से जोड़े गए दो सिस्टम की स्थिति
- MacGyver Debt वह प्रकार है जिसका नाम 1980 के दशक के मध्य के TV शो MacGyver से लिया गया है
- तकनीकी कर्ज़ के संदर्भ में, इसका मतलब ऐसी स्थिति है जहाँ टकराते हुए दो सिस्टम पूरे codebase में interface points पर “डक्ट टेप” से जुड़े होते हैं
-
वास्तविक दुनिया की उपमा: Seattle
- Seattle में पहले दो प्रतिस्पर्धी बस्तियाँ थीं, जिनमें से हर एक का अपना grid था
- जब ये दोनों बस्तियाँ बढ़कर आज के Emerald City में बदलीं, तो थोड़ा अलग-अलग grid आपस में जुड़ गए, और अजीब आकार के block और building तथा space का अक्षम उपयोग पैदा हुआ
-
League का उदाहरण: std::string और AString
- League codebase में C++ std::string और Riot की custom AString class साथ-साथ मौजूद हैं
- दोनों ही string को store, modify और pass करने के तरीके हैं
- Riot का मानना है कि std::string बहुत-सी “hidden” memory allocation और performance cost पैदा करता है, और खराब code लिखना आसान बनाता है
- AString को सावधानीपूर्वक memory management को ध्यान में रखकर डिज़ाइन किया गया है
- replacement strategy यह थी कि दोनों सिस्टम साथ रहें और
.c_str()तथा.Get()के ज़रिए एक-दूसरे में convert कर सकें - AString में usability बढ़ाने वाले सुधार जोड़े गए, और engineers को प्रोत्साहित किया गया कि वे code बदलते समय अपनी पहल पर std::string को replace करें
- इस तरीके से std::string धीरे-धीरे चरणबद्ध रूप से गायब होता है, और दोनों सिस्टम के बीच का “डक्ट टेप” interface भी code cleanup के साथ कम होता जाता है
-
std::string बनाम AString का आकलन
- impact: 2/5
- std::string से होने वाले ज़्यादातर high-impact allocation पहले ही profiling के ज़रिए हटाए जा चुके हैं
- अभी मुख्य cost एक सिस्टम से दूसरे सिस्टम में convert करते समय होने वाली छोटी मानसिक switching cost है
- fix cost: 3/5
- AString migration कोई साधारण find-and-replace नहीं है
- AString के उद्देश्य-विशिष्ट variants हैं, जैसे stack memory में शुरुआती allocation करने वाला AStackString, static string reference के लिए ARefString, और heap allocation आधारित AString
- सही replacement के लिए हर जगह इंसान को खुद देखकर फैसला करना होगा, और पुराने सिस्टम को चरणबद्ध तरीके से हटाने की प्रक्रिया लंबी और धीमी होगी
- contagion: -2/5
- AString को std::string से ज़्यादा आसान बनाकर contagion को अनुकूल दिशा में पलट दिया गया है
- हर बार जब engineers game code में बदलाव check in करते हैं, AString के और फैलने की संभावना बनती है
- impact: 2/5
-
MacGyver Debt को ठीक करने का तरीका
- MacGyver Debt की बड़ी cost अक्सर सीमा पार करते समय mode बदलने की बौद्धिक लागत होती है
- अगर bug या feature “wrong” system में होने की वजह से अटक जाए, तो target point को “right” system में ले जाना आम तौर पर सीधा काम होता है
- नए सिस्टम और पुराने सिस्टम के बीच सापेक्ष contagion मुख्य metric है
- अगर संतुलन इस तरह पलट दिया जाए कि नया सिस्टम ज़्यादा फैलने लगे, तो अंत में बेहतर सिस्टम जीतता है
- जो सिस्टम वैश्विक स्तर पर बेहतर है, उसे स्थानीय स्तर पर भी ज़्यादा आकर्षक बनाना चाहिए
- अगर समय के दबाव में काम कर रहा engineer रोज़मर्रा के काम के दौरान greedy optimization करते हुए भी इच्छित अंतिम अवस्था की दिशा चुनता है, तो चीज़ें सही दिशा में जा रही हैं
- दूसरा तरीका बड़े पैमाने का brute-force refactor है, और सिस्टम कितने नज़दीक से map होते हैं इस पर निर्भर करते हुए, clever regex से इसका कुछ या पूरा हिस्सा ठीक किया जा सकता है
Foundational Debt: ऐसी स्थिति जहाँ गहरी धारणाएँ पूरे ढाँचे में जड़ जमा लेती हैं
- Foundational Debt वह स्थिति है जहाँ सिस्टम के बहुत गहरे हिस्से में मौजूद कोई धारणा पूरे काम करने के तरीके में पकी हुई होती है
- सिस्टम के अनुभवी उपयोगकर्ताओं को यह “बस ऐसा ही है” जैसा लग सकता है, इसलिए इसे पहचानना मुश्किल हो सकता है
-
वास्तविक दुनिया की उपमा: United States Customary Units
- अमेरिका में पले-बढ़े लोग 1 मील = 5,280 फीट, 1 क्वार्ट = 2 पिंट, 1 गैलन = 4 क्वार्ट जैसे रूपांतरण याद कर लेते हैं
- अमेरिकी सरकार ने कई बार metric में बदलाव पर विचार किया है, लेकिन वह अब भी उन 7 देशों में से एक है जिन्होंने Système International को आधिकारिक मापन प्रणाली के रूप में नहीं अपनाया है
- यह debt सड़क संकेतों, recipes, प्राथमिक विद्यालयों और लोगों के दिमाग में पकी हुई है
-
League का उदाहरण: BlockBuilder और Lua
- Riot द्वारा संभाले गए बड़े Foundational Debt उदाहरणों में Determinism in League of Legends और Game Data Server शामिल हैं
- League में Lua scripting language का उपयोग भी Foundational Debt का एक उदाहरण है
- League के designers BlockBuilder नामक टूल से functional blocks को जोड़कर जटिल व्यवहार बनाते हैं
- functional blocks में दो बिंदुओं के बीच दूरी निकालना, minion बनाना, damage प्रोसेस करना, और अलग-अलग script flow control शामिल हैं
- designer जिन operations को चुनते हैं उनका दायरा विविध है, लेकिन सीमित है, और हर operation के parameters पर भी constraints हैं
- League of Legends के शुरुआती दौर में blocks और parameters को data के लिए उपयुक्त सरल और सीमित फ़ॉर्मेट में स्टोर करने के बजाय, उन्हें शक्तिशाली लेकिन इस उद्देश्य के लिए जरूरत से ज़्यादा जटिल Lua language की arrays और tables में स्टोर करने का फैसला किया गया
- उसके बाद लगभग 10 साल का game development उसी आधार पर आगे बढ़ा, और Lua object manipulation engine के सबसे आम कामों में से एक बन गया
-
BlockBuilder Lua का मूल्यांकन
- impact: 4/5
- Lua और संबंधित problem space के बीच असंगति बहुत लागत पैदा करती है
- BlockBuilder logic के हर frame में callstack लगभग 6 marshalling stack frames से प्रदूषित हो जाता है
- marshalling का काम server CPU उपयोग के लिहाज से सस्ता नहीं है
- script बदलावों के diff पढ़ना अनावश्यक रूप से कठिन है
- functionality समझने के लिए script files को parse/search करने में Lua language की काफ़ी गहरी समझ चाहिए
- fix cost: 4/5
- Lua engine में गहराई से जड़ा हुआ है, इसलिए इसे हटाना कठिन है
- मौजूदा प्रस्तावों में से एक यह है कि ऐसी wrapper class बनाई जाए जो Lua object की तरह व्यवहार करे, लेकिन भीतर से कहीं अधिक सरल struct हो, ताकि scripting के अंदरूनी हिस्सों को धीरे-धीरे अधिक उपयुक्त रूप में बदला जा सके
- कोई भी approach अपनाई जाए, उसे सावधानी और गहरे विचार के साथ आगे बढ़ाना होगा
- contagion: 4/5
- जब भी कोई सिस्टम scripting से जुड़ता है, वह सिस्टम Lua backend के operations और requirements से आकार लेने लगता है
- scripting, LoL की core logic unit है
- Riot औसतन हर 3~4 दिन में एक नया Building Block जोड़ता है, और हर Building Block सीधे Lua object को manipulate करता है
- Lua को जितनी देर तक replace नहीं किया जाएगा, उसे replace करना उतना ही कठिन होता जाएगा
- impact: 4/5
-
Foundational Debt को कम करने के तरीके
- Foundational Debt का रुझान impact, fix cost और contagion—तीनों अक्षों पर ऊँचा स्कोर दिखाने का होता है
- ऊँची fix cost अक्सर अपूर्ण सिस्टम का इस्तेमाल जारी रखने पर मजबूर करती है, और कभी-कभी यही सही विकल्प भी होता है
- लेकिन ऊँचे impact और ऊँचे contagion की वजह से गंभीर Foundational Debt को ठीक करने पर बड़ा लाभ मिल सकता है
- Riot में देखी गई सबसे आम सुधार रणनीति यह है कि पुराने सिस्टम के बगल में नया सिस्टम खड़ा किया जाए
- जहाँ संभव हो, मौजूदा foundational debt को MacGyver Debt में बदला जाता है, और conversion operation के ज़रिए नए और पुराने सिस्टम के बीच आना-जाना करते हुए धीरे-धीरे porting की जाती है
- इस तरीके से कुछ खास क्षेत्रों में लाभ मिलना शुरू हो जाता है, जबकि जोखिम का दायरा सीमित रहता है
- जब ऐसा संक्रमण संभव न हो, तब compile time switch, और अगर संभव हो तो loading time switch बनाया जा सकता है, ताकि नए सिस्टम पर भरोसा धीरे-धीरे बनाया जा सके
- compile time switch वाला तरीका GDS transition में इस्तेमाल हो रहा है
- loading time switch वाला तरीका Determinism में प्रभावी रहा था
Data Debt: ऐसी स्थिति जिसमें दोषों के ऊपर भारी मात्रा में content जमा हो गया हो
- Data Debt तब पैदा होता है जब तकनीकी कर्ज़ की दूसरी श्रेणियों के ऊपर बहुत सारा content जमा हो जाता है
- इसकी शुरुआत scripting system के bug, item के लिए अनुपयुक्त file format, या ऐसे दो systems से हो सकती है जो एक-दूसरे के साथ ठीक से फिट नहीं बैठते
- जब उस code defect के ऊपर art, scripts, sounds जैसा भारी content बनाया जाता है, तो शुरुआती तकनीकी कर्ज़ को ठीक करना बहुत जोखिम भरा हो जाता है
- समय बीतने पर यह समझना दर्दनाक रूप से कठिन हो जाता है कि क्या टूटेगा
-
वास्तविक दुनिया की उपमा: DNA
- किसी organism का genome लाखों वर्षों में mutation, transcription error, और evolutionary pressure के माध्यम से धीरे-धीरे जमा होता है
- कुछ copy errors बेकार होते हैं लेकिन हानिकारक नहीं, कुछ हानिकारक होते हैं, और कुछ बहुत शक्तिशाली लाभ देते हैं
- यह पता लगाना कि DNA का कोई टुकड़ा वास्तव में क्या करता है, बेहद कठिन है
- हम पूरी तरह समझते हैं कि base pair का क्या अर्थ है, और base pair के समूह protein construction के लिए amino acid में कैसे translate होते हैं
- DNA की कुछ non-encoding roles के बारे में भी हम अधिक समझना शुरू कर चुके हैं
- लेकिन मानव genome के 3 अरब से अधिक base pairs में अब भी बहुत कुछ ऐसा है जिसे हम लगभग समझते ही नहीं
- Radiolab का CRISPR episode हाल ही में सुलझी ऐसी ही एक पहेली पर बात करता है
-
League का उदाहरण: block parameter naming bug
- League of Legends में Data Debt का सबसे बड़ा असर तब होता है जब वह मूल रूप से छोटे-से बदलाव को भी कठिन मेहनत वाले काम में बदल देता है
- game engineers गेम systems के implementation के बारे में गहरा ज्ञान बना लेते हैं, और यह अनुमान लगाने में कुशल हो जाते हैं कि कौन-सा code change किस data को तोड़ेगा
- LoL engine में बदलाव करते समय Data Debt सबसे महत्वपूर्ण विचारों में से एक है
- कुछ साल पहले ठीक किया गया Data Debt का एक मामला BlockBuilder scripting language में block parameter से जुड़ा bug था
- toy example में अगर Owner के armor को variable और constant से बढ़ाने की कोशिश की जाए, तो अपेक्षित मान variable Delta 20 और constant 5 को जोड़कर 25 bonus armor होना चाहिए
- अगर variable का नाम parameter के नाम जैसा हो, तो पहले परिणाम 40 हो जाता था
- यह 45 क्यों नहीं बनता था, लेखक के अनुसार वह भी नहीं जानते
-
वास्तविक fix प्रक्रिया
- जब Champions team के engineer NoopMoney ने इस behavior को ठीक करने की कोशिश की, तो असली code change सिर्फ़ 4 lines हटाने भर का था
- लेकिन बहुत संक्रामक कर्ज़ के कारण छोटे बदलाव के लिए भी बहुत सावधानी से planning करनी पड़ी
- LoL की 400,000 lines की script में किसी भी numerical parameter पर यह bug लागू होकर उसे दोगुना कर रहा हो सकता था
- बड़ी समस्या यह थी कि गेम उन संभावित रूप से दोगुने हो चुके values के हिसाब से balance और tune किया गया था, इसलिए वे scripts “correctly” काम कर रही थीं
- NoopMoney को अप्रत्याशित bugs के लिए तैयार रहने हेतु fix को Live पर toggle करने योग्य बनाना पड़ा
- किन scripts में यह bug dependency के रूप में मौजूद है, यह पहचानने के लिए उन्होंने व्यापक regex searching और QA sweep किया
- अंत में इस fix से उत्पन्न समस्याएँ अपेक्षाकृत छोटी निकलीं, और केवल कुछ champion scripts में बदलाव की ज़रूरत पड़ी
- Data Debt की वजह से नतीजे का अनुमान लगाना कठिन था
-
Parameter Naming Bug का मूल्यांकन
- impact: 2/5
- होने पर इसका प्रभाव छोटा था
- यह passed value को दोगुना कर सकता था और constant को हटा सकता था
- यह designers और engineers के लिए याद रखने लायक एक और बेकार tribal knowledge बन गया
- developer mindshare इतना कीमती resource है कि उसे इस तरह बर्बाद नहीं किया जाना चाहिए
- fix cost: 2/5
- कुल मिलाकर fix अपने-आप में सीधा था
- live feature toggle बनाकर fix की safety पर भरोसा बढ़ाया जा सकता था
- सबसे महँगा हिस्सा शुरुआती screening था, जिसमें testing targets तय करने के लिए समस्या के scope का आकलन करना पड़ा
- contagion: 4/5
- दुर्भाग्य से यह bug बहुत logical behavior को निशाना बनाता था
- अगर किसी unit को damage देना हो, तो value को “Damage” नाम के variable में store करना पूरी तरह logical है
- ApplyDamage block उसी नाम के parameter में amount लेते समय bug trigger कर देता था
- अगर कोई दूसरा व्यक्ति वैसा ही spell बनाने के लिए उस block को copy/paste करे, तो bug और फैलता
- impact: 2/5
-
Data Debt बहुत संक्रामक क्यों होता है
- Data Debt आम तौर पर fix cost को ऊँचा बनाता है क्योंकि इससे change impact का आकलन करना कठिन हो जाता है
- उससे भी अधिक चिंताजनक बात यह है कि data की प्रकृति के कारण यह लगभग हमेशा बहुत अधिक संक्रामक होता है
- मौजूदा data को copy/paste करके नया data बनाना आम तौर पर स्वीकार्य होता है
- नया skillshot spell बनाते समय Ezreal’s Mystic Shot से शुरुआत करना बहुत समय बचा सकता है, और मौजूदा data की समस्याएँ उसके descendant data में भी फैल जाती हैं
- data को code review जैसी technical review शायद ही कभी मिलती है, इसलिए भले ही खराब practices व्यापक रूप से जानी जाती हों, उनके फैलाव को पहचानना और रोकना कठिन होता है
- data की समस्याओं को ठीक करने के लिए आम तौर पर आँख और दिमाग वाले इंसान द्वारा सीधे verification की ज़रूरत होती है; केवल compiler और formal logic काफ़ी नहीं होते
-
Data Debt को ठीक करने के दो तरीके
- पहला तरीका है do it right checkbox
- यह data creator के लिए मौजूदा “broken” behavior और नए “fixed” behavior के बीच toggle बनाने का तरीका है
- आदर्श रूप से fixed version को default रखा जाए और old content broken version का उपयोग करे
- इसके बाद MacGyver Debt की तरह धीमे लेकिन लगातार replacement के माध्यम से नए version पर जाया जा सकता है
- इसकी कमी यह है कि editing UI में धीरे-धीरे और अधिक अनावश्यक elements जोड़ने की स्थायी लागत बनती जाती है
- दूसरा तरीका है just fix the damn thing
- यही तरीका NoopMoney ने parameter naming bug पर अपनाया था
- bug को ठीक करने के बाद अर्थपूर्ण रूप से प्रभावित होने वाले सभी data को सुधारने की कोशिश की जाती है
- इसे कम डरावना बनाने वाली techniques में theoretical impact समझने के लिए बहुत सारा grep और regex searching, targeted testing, और ship के बाद अगर कोई बदतर छूटा हुआ मामला मिले तो पुराने behavior पर लौटने के लिए toggle तैयार रखना शामिल है
- Determinism यह जाँचने देता है कि change से पहले और बाद में server वही results बना रहा है या नहीं, इसलिए यह ऐसे changes के testing में बहुत मदद करता है
- पहला तरीका है do it right checkbox
निष्कर्ष: लागत के आकलन में contagion को शामिल करना चाहिए
- तकनीकी कर्ज़ को मापने के मानक हैं impact, fix cost, contagion
- impact का मतलब है customers और developers पर प्रभाव
- fix cost का मतलब है समय और जोखिम
- contagion का मतलब है समस्या के फैलने की मात्रा
- ज़्यादातर developers impact और fix cost पर नियमित रूप से विचार करते हैं, लेकिन contagion पर चर्चा अपेक्षाकृत कम होती है
- जब कोई समस्या धीरे-धीरे गहराई तक जड़ जमा लेती है और उसे हटाना लगातार कठिन होता जाता है, तब contagion developer का सबसे बड़ा दुश्मन बन सकता है
- इसके उलट, अगर fix को समस्या से भी अधिक संक्रामक बना दिया जाए, तो contagion को हथियार में बदला जा सकता है
- League में देखे गए अधिकांश तकनीकी कर्ज़ को चार श्रेणियों में समेटा जा सकता है
- Local Debt: ऐसा कर्ज़ जो अंदर से गंदा हो लेकिन black box की तरह बंद हो
- MacGyver Debt: ऐसा कर्ज़ जिसमें conversion function के जरिए 2 या अधिक systems duct tape की तरह जुड़े हों
- Foundational Debt: ऐसा कर्ज़ जिसमें पूरी संरचना दुर्भाग्यपूर्ण assumptions पर बनी हो
- Data Debt: ऐसा कर्ज़ जिसमें दूसरे प्रकार के कर्ज़ के ऊपर भारी data जमा हो गया हो, जिससे fix जोखिम भरा और समय लेने वाला बन जाता है
1 टिप्पणियां
Hacker News की राय
संक्रामकता अपने-आप में यही वजह है कि interfaces डिज़ाइन के सबसे महत्वपूर्ण तत्वों में से एक हैं और उन पर पर्याप्त सोच-विचार होना चाहिए
अगर एक सुंदर interface के साथ गैर-इष्टतम implementation जुड़ा हो, तो समय मिलने पर उसे आसानी से साफ़ किया जा सकता है, लेकिन इसका उल्टा लगभग कभी सही नहीं होता
कभी-कभी तो दोनों ही नहीं होते
ऐसे मामलों में छोटे modules और single responsibility को संयोजनीय तरीके से लागू करना संक्रामकता को बहुत ज़्यादा फैलने से रोक सकता है। इसमें न भविष्य का बहुत ज्ञान चाहिए, न बहुत समय; बस उन रूसी गुड़िया-जैसे wide surface area interfaces से बचना चाहिए जो parameters के ज़रिए व्यवहार के कई रूप नियंत्रित करते हैं। configuration, parsing, और behavior decisions को logic के किनारों पर ले जाना बेहतर है, बजाय इसके कि उन्हें पूरे निचले model में समाने दिया जाए
चाहे आप तकनीकी कर्ज़ से हर हाल में बचने की सचेत शुरुआत करें, इसे सही करना कठिन है, और इसके लिए सिर्फ तकनीकी आत्मविश्वास या architectural vision से अधिक चाहिए। असल में यह भविष्यवाणी के क्षेत्र में चला जाता है
अगर आप कोई बेवकूफ़ implementation interface में implicitly पका देते हैं, तो बाद में सिर्फ implementation बदलकर उसे ठीक करना मुश्किल हो जाता है। इसका एक उदाहरण sorting और pagination behavior है। junior developers, और अब तो कई senior भी जिन्हें इससे बेहतर पता होना चाहिए, अक्सर
limit/offsetजैसे parameters वाले requests से शुरुआत करते हैं, और यह भयानक performance समस्याओं तथा अजीब behavior तक ले जाता है। pagination किस तरह efficiently काम करती है और कौन-से sorting options अच्छी performance के साथ support किए जा सकते हैं, यह data के shape और data store की पसंद से मूल रूप से जुड़ा होता है। जिसने lower layers पर इस प्रक्रिया का अनुभव नहीं किया, उसके ऊपरी interfaces को सही बनाने की संभावना कम है, जब तक वह पहले implementation में काफ़ी गहराई से न उतरेज़्यादातर मामलों में मैं implementation नहीं देखना चाहता, सिर्फ़ ठीक से documented interface देखना चाहता हूँ। अगर interface के behavior को सरल शब्दों में समझाया नहीं जा सकता, तो कुछ गड़बड़ है
QWERTY का प्रसिद्ध उदाहरण है कि वह इष्टतम physical interface नहीं है, और steering wheel भी ऐसा ही मामला हो सकता है। कंप्यूटर दुनिया में x86 सतह पर गैर-इष्टतम interface का प्रतिनिधि उदाहरण है
यह बात काफ़ी चौंकाती है कि यह लेख एक engineering manager ने लिखा है
जिन managers के साथ मैंने काम किया, उनमें से कोई भी हमारे codebase के बारे में इस स्तर की तकनीकी बारीकी से बात नहीं कर सकता था। जो पहले engineer रहे थे, वे भी नहीं
हालाँकि निष्पक्ष रूप से कहूँ तो हमारे यहाँ भीतर से पदोन्नत managers नहीं थे, और अंदर के लोग, मेरी तरह, engineering छोड़ना नहीं चाहते, इसलिए बाहर से managers भर्ती करने की एक बुरी आदत है
मुझे लगता है कि इसमें वह सबसे आम कर्ज़ का प्रकार छूट गया है जो मैंने देखा है: founder debt
यह वह कर्ज़ है जो founders जल्दी और मूल्यवान technology ship करने के लिए बनाते हैं। जो चीज़ आसान अवसर जैसी लगती थी, वही पूरे system की नींव बन जाती है
कई देशों के founding documents भी इस श्रेणी में आते हैं lol (लेकिन USA! USA! USA! नहीं)
MacGyver debt और foundational debt सबसे करीब लगते हैं, लेकिन दोनों में से कोई भी इस घटना को ठीक-ठीक नहीं पकड़ता
तकनीकी दृष्टि से यह शानदार लेख है
लेकिन मुझे यह “taxonomy” से ज़्यादा nomenclature लगता है। क्योंकि यह न तो जान-बूझकर समग्र है, न ही परस्पर अनन्य; हालाँकि मैं ग़लत भी हो सकता हूँ। हर item के भौतिक उदाहरण विशेष रूप से अच्छे थे और सोचने पर मजबूर करते हैं
हमेशा की तरह कुछ दार्शनिक स्तर की छोटी-सी खटकन रहती है। शुरुआती “तीन अक्ष” पारंपरिक RoI के return और investment जैसे लगते हैं, जिनमें एक खास future-oriented और conditional return subcategory जोड़ी गई है। मेरा अंदाज़ा है कि व्यवहार में यह निर्णय अच्छा काम करता होगा, और video game development practices का पूरी तरह वैज्ञानिक होना ज़रूरी नहीं, लेकिन थोड़ी और दार्शनिक स्पष्टता बुरी नहीं होती
उस समय भी इस पर चर्चा हुई थी:
A Taxonomy of Technical Debt - https://news.ycombinator.com/item?id=16810092 - अप्रैल 2018 (113 टिप्पणियाँ)
और यह भी है:
A Taxonomy of Tech Debt (2018) - https://news.ycombinator.com/item?id=39782923 - मार्च 2024 (1 टिप्पणी)
“तकनीकी कर्ज़ को ऐसे code या data के रूप में परिभाषित करना जिसकी कीमत भविष्य के developers चुकाएँगे” — यह इसकी सबसे बेहतरीन व्याख्याओं में से एक है जो मैंने देखी है
हर कर्ज़ की तरह, कर्ज़ लेते समय तत्काल ज़रूरत और भविष्य की लागत के बीच संतुलन बनाने के लिए एक threshold लागू करना पड़ता है। मुझे लगता है कि ज़्यादातर लोग, सिर्फ developers ही नहीं, तत्काल ज़रूरत को बढ़ा-चढ़ाकर आँकते हैं और भविष्य की लागत को कम करके
निजी तौर पर मैं लगभग किसी भी तरह के कर्ज़ से लगभग रोगात्मक स्तर तक नफ़रत करता हूँ। मैं कभी-कभी एक अतिरिक्त दिन इस बात पर लगा देता हूँ कि भविष्य में उपयोगी हो सकने वाली चीज़ को अलग कैसे किया जाए। मैं लगभग 50% मामलों में सही निकलता हूँ, लेकिन हर बार ऐसा काम करने से आदत मज़बूत होती है और मूल workflow भी तेज़ हो जाता है
अब तक मैंने 3 “startups” में काम किया है, और मैं उन सबमें तब शामिल हुआ जब वे लगभग सामान्य स्तर का वेतन देने लायक revenue ला रहे थे
जो बात मैंने सबसे ज़्यादा देखी, वह यह है कि कई founders अपने मन में सोचा गया आइडिया, वास्तव में जो बनाया गया, और implemented हिस्सों में से जो सच में काम करता है, इन सबको धुँधले ढंग से मिला देते हैं
यह लेख पहली बार पढ़ने के बाद से मैं तकनीकी कर्ज़ समझाने के लिए संक्रामकता शब्द का इस्तेमाल करता आया हूँ, और यह काफ़ी सटीक बैठता है
मुझे यक़ीन नहीं कि “local debt” को सामान्य परिस्थितियों में तकनीकी कर्ज़ कहा जा सकता है
व्यवहार में कहीं-न-कहीं हमेशा कुछ गंदे हिस्से होते हैं, और उन्हें encapsulate करके ऐसे छिपा देना कि किसी को नुकसान न पहुँचे, यह सामान्य बात है। जब तक requirements न बदलें, उन्हें लगभग कभी बदलने की ज़रूरत नहीं होती, और अगर बदलें, तो किसी भी implementation को संशोधित करना ही पड़ेगा, तो यह ठीक है
अगर उदाहरण वाले 24 minion instances सिर्फ़ कम सुरुचिपूर्ण होने की बजाय वास्तविक समस्या हैं, तो यह local debt से ज़्यादा foundational debt जैसा लगता है, क्योंकि “minion” सबसे सरल आधारभूत unit बन गया और शायद उससे हल्का कुछ हो सकता था
अगर developers को पुराने, परिपक्व और छेड़ने की ज़रूरत न होने वाले modules तक में बदलाव करने के लिए प्रोत्साहित किया जाए, तो यह उस समस्या को इतना बढ़ने से रोकने का अच्छा तरीका बन जाता है कि वह सचमुच समस्या बन जाए
एक महत्वपूर्ण पहलू यह है कि कभी-कभी लोग अल्पकालिक लाभ पाने के लिए जानबूझकर तकनीकी कर्ज़ लेते हैं
तब उस लाभ को भी तराज़ू पर रखने वाला एक और axis मानना होगा
अगर आप 15 साल बाद पूँजी आने तक इंतज़ार करने के बजाय अभी नई इमारत बनाकर काम पूरा करना चाहते हैं? तो कर्ज़ ले लीजिए
कर्ज़ एक tool है, लेकिन शक्तिशाली और ख़तरनाक tool। अगर आप यह नहीं मानते और सम्मान नहीं करते कि आप इसका उपयोग कर रहे हैं, तो चोट खाएँगे। या फिर वह व्यक्ति चोट खाएगा जिसे आपने अपने हाथ से grenade पकड़ा दिया। बिल्कुल असली कर्ज़ की तरह