4 पॉइंट द्वारा GN⁺ 2024-10-30 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • कोड लिखे जाते ही maintenance cost पैदा करता है, इसलिए कई बार reusability से ज़्यादा बाद में उसे हटाना या बदलना आसान रखने वाली संरचना अधिक महत्वपूर्ण होती है
  • जितने अधिक API users होंगे, बदलाव की लागत उतनी बढ़ेगी, और third-party API पर निर्भरता जितनी गहरी होगी, बाहरी बदलाव से codebase उतना अधिक हिलेगा
  • duplication, boilerplate, layering, बड़े code chunks, module separation, और feature flags — ये सब स्थिति के अनुसार dependency management के tools बन सकते हैं
  • अच्छा separation common functionality को एक साथ बाँधना नहीं, बल्कि ऐसे design decisions को एक-दूसरे से छिपाना है जिन्हें बदलना कठिन हो या जिनके बदलने की संभावना अधिक हो
  • अच्छा कोड शुरू से परफेक्ट कोड नहीं होता, बल्कि समय बीतने पर भी कम बाधा डालने वाला legacy code होता है, और अंततः वही हटाने में आसान कोड होता है

कोड एक लागत है, और हटाना लागत कम करना है

  • हर कोड अपनी रचना के क्षण से ही maintenance cost पैदा करता है; reuse कोड की मात्रा घटा सकता है, लेकिन बाद में अपना विचार बदलना कठिन भी बना सकता है
  • API का उपयोग करने वाला कोड जितना अधिक होगा, API बदलने पर rewrite cost उतनी अधिक होगी
  • third-party API पर जितनी अधिक निर्भरता होगी, उसके बदलने पर प्रभाव भी उतना बड़ा होगा
  • बड़े सिस्टम में समय के साथ यह समझना कि कोड आपस में कैसे जुड़ा है और कौन-सा भाग किस पर निर्भर है, और कठिन समस्या बन जाता है
  • अगर code lines को “उत्पादित lines” नहीं बल्कि “लिखी गई लागत” के रूप में देखें, तो कोड हटाना maintenance cost घटाने का काम बन जाता है
  • लक्ष्य सिर्फ reusable software बनाना नहीं, बल्कि discardable software बनाना है

चरण 0: कोड न लिखना

  • lines of code अपने आप में सब कुछ नहीं बतातीं, लेकिन 50 lines, 500 lines, 5,000 lines, 10,000 lines, 25,000 lines जैसी scale महत्वपूर्ण है
  • 10 लाख lines का monolith, 10 हज़ार lines के monolith की तुलना में replace करने में कहीं अधिक समय, पैसा और मेहनत माँगता है
  • कोड जितना अधिक होगा, उसे हटाना उतना कठिन होगा, लेकिन सिर्फ एक line कम कर देने से लगभग कुछ नहीं बचता
  • सबसे आसान हटाया जाने वाला कोड वह है जो शुरू में लिखा ही नहीं गया

चरण 1: copy-paste करना

  • reusable code को भविष्य के उपयोगों का अनुमान लगाकर पहले से बनाने की बजाय, कई वास्तविक use cases आने के बाद बनाना अधिक आसान होता है
  • codebase में कुछ बार copy-paste करके आप वास्तविक usage pattern को बेहतर समझ सकते हैं
  • किसी कोड को shared API बनाते ही उसे बदलना कठिन हो जाता है
  • function को call करने वाला code सिर्फ documented behavior पर नहीं, बल्कि implementation में दिखने वाले इरादतन और अनिरादतन behavior पर भी निर्भर होने लगता है
  • function के अंदर का कोड हटाना, function को ही हटाने से सरल है

चरण 2: copy-paste रोकना

  • जो कोड पर्याप्त बार दोहराया जा चुका है, उसे function में ऊपर उठाने का समय आ जाता है
  • जैसे config file खोलकर hash table लौटाने वाला code, या directory delete करने वाला code — standard library के ऊपर बार-बार काम आने वाला utility code इस श्रेणी में आता है
  • util को एक file की बजाय directory के रूप में रखना और अलग-अलग utilities को अलग files में रखना बेहतर है
    • एक अकेली util file लगातार बढ़ती जाती है, और बहुत बड़ी होने के बाद उसे बाँटना कठिन हो जाता है
  • जो कोड application या project से कम विशेषीकृत होता है, उसे reuse करना आसान होता है और उसके बदलने या हटने की संभावना भी कम होती है
    • logging, third-party API, file handles, process जैसी library code इस श्रेणी में आते हैं
    • list, hash table, collection जैसी चीज़ें सिर्फ सरल interface की वजह से ही नहीं, बल्कि समय के साथ उनका scope न बढ़ने की प्रवृत्ति के कारण भी आसानी से हटाई नहीं जातीं
  • मूल बात यह है कि जिन हिस्सों को हटाना कठिन है, उन्हें जितना संभव हो हटाने में आसान हिस्सों से दूर रखा जाए

चरण 3: और boilerplate लिखना

  • library बनाने से copy-paste से बचा जा सकता है, लेकिन व्यवहार में library का उपयोग करने के लिए बहुत-सा boilerplate लिखना पड़ता है
  • boilerplate इस अर्थ में copy-paste जैसा है कि हर बार थोड़ी-सी अलग जगह बदलनी पड़ती है
  • यह duplication dependencies घटाने और flexibility पाने की कीमत पर verbosity स्वीकार करने का तरीका है
  • जिन libraries में boilerplate की ज़रूरत पड़ती है, वे अक्सर network protocol, wire format, parsing tools जैसी चीज़ें होती हैं जहाँ policy और protocol को मिलाना कठिन होता है
    • protocol इस बात से जुड़ा है कि program क्या कर सकता है
    • policy इस बात से जुड़ी है कि program को क्या करना चाहिए
  • ऐसा code अक्सर दूसरे कंप्यूटरों से बात करने या दूसरे files संभालने की requirement होता है, इसलिए इसे हटाना कठिन होता है
  • महत्वपूर्ण यह है कि business logic को ऐसे code में इधर-उधर न छिड़का जाए
  • चाहे अधिक lines लिखनी पड़ें, बेहतर यह है कि वे lines ऐसे हिस्सों में लिखी जाएँ जिन्हें हटाना आसान हो

चरण 4: boilerplate न लिखना

  • जब boilerplate बहुत अधिक हो जाए, तब flexible library को wrap करके ऐसी library बनाने का समय है जिसकी policy, workflow, और state पर स्पष्ट राय हो
  • आसान-से-उपयोग API बनाना काफ़ी हद तक boilerplate को library में बदलना है
  • Python HTTP client requests, अधिक verbose urllib3 के ऊपर सरल interface देने का उदाहरण है
    • requests HTTP उपयोग के सामान्य workflow को संभालता है और practical details छिपाता है
    • urllib3 pipelining, connection management आदि देता है और user से details नहीं छिपाता
  • एक library को दूसरी library से wrap करना सिर्फ details छिपाना नहीं, बल्कि separation of concerns भी है
  • util directory में business logic न रखें; सरल implementation वाली libraries के ऊपर उपयोग में आसान libraries की परत चढ़ाना बेहतर है
  • कभी-कभी third-party libraries को wrap करना भी अच्छा होता है
    • इससे पूरा project किसी एक खास choice पर स्थिर नहीं हो जाता, और आप अपने code के अनुरूप library बना सकते हैं
  • उपयोग में आसान API और extensible API अक्सर एक-दूसरे से टकराते हैं
  • layering का मतलब बाद में हटाए जाने वाला कोड लिखना कम, और हटाने में कठिन code को business logic को दूषित किए बिना उपयोगी बनाना अधिक है

चरण 5: बड़े code chunks लिखना

  • copy-paste, refactoring, layering, और composition के बावजूद, अंततः code को कुछ काम तो करना ही है, इसलिए कभी-कभी बाकी सबको साथ पकड़े रखने वाला बड़ा code chunk चाहिए होता है
  • business logic को अंतहीन edge cases और तेज़ hacks से पहचाना जा सकता है
  • game code या founder code को भी उसी तरह का code माना जा सकता है जो काफी समय बचाने के लिए shortcuts लेता है
  • आपस में उलझी 18 छोटी गलतियाँ हटाने की बजाय, एक बड़ी गलती हटाना कभी-कभी आसान होता है
  • बहुत-सा programming exploratory होता है, इसलिए शुरुआत से सही करने की कोशिश से बेहतर है कुछ बार गलत होकर iterate करना
  • पहला game बनाते समय engine से शुरू न करें, और application लिखने से पहले web framework न बनाएँ
  • Monorepo भी ऐसा ही trade-off है
    • पहले से यह जानना कठिन है कि code को कैसे बाँटना चाहिए, और 20 tightly coupled चीज़ों की तुलना में एक बड़ी गलती deploy करना आसान हो सकता है
  • अगर आपको पता है कि code जल्द ही फेंका जाएगा, हटाया जाएगा, या आसानी से बदला जाएगा, तो आप और अधिक shortcuts ले सकते हैं
  • लक्ष्य एक ही मिट्टी के ढेले को दस बार दोहराकर गलती को परिष्कृत करना नहीं, बल्कि हर बार नई गलती, नया risk लेकर iteration से आगे बढ़ना है
  • project अंततः या तो fail होते हैं या legacy code बन जाते हैं; failure, success से अधिक सामान्य है
  • कोड को टुकड़ों में हटाने से पूरा का पूरा हटाना आसान होता है

चरण 6: कोड को टुकड़ों में बाँटना

  • बड़ा mud ball बनाना सबसे आसान है, लेकिन उसकी maintenance cost सबसे अधिक होती है
  • दिखने में साधारण बदलाव भी codebase के लगभग हर हिस्से में temporary workaround छूने पर मजबूर कर सकता है
  • जो code पूरे का पूरा हटाना आसान था, वही टुकड़ों में हटाना कठिन हो सकता है
  • modules को common functionality के आधार पर नहीं, बल्कि जो चीज़ें बाकी के साथ share नहीं करनी हैं और जिन design decisions को छिपाना है, उनके आधार पर बाँटना बेहतर है
  • D. Parnas के मानदंड की तरह, कठिन या बदलने की संभावना वाले design decisions की सूची बनाकर modules इस तरह डिज़ाइन किए जा सकते हैं कि हर module उन निर्णयों को दूसरे modules से छिपाए
  • modules reuse के लिए नहीं, बल्कि changeability के लिए बनाए जाते हैं
  • single responsibility principle को “हर module को सिर्फ एक कठिन समस्या संभालनी चाहिए” की तरह देखा जा सकता है, लेकिन अधिक महत्वपूर्ण यह है कि “हर कठिन समस्या सिर्फ एक module में ही संभाली जाए”
  • अगर कोई module दो काम करता है, तो अक्सर वजह यह होती है कि एक हिस्से को बदलने के लिए दूसरे हिस्से को भी बदलना पड़ता है
  • सरल interface वाला एक भयानक component, उन दो components से आसान हो सकता है जिन्हें बारीक तालमेल की ज़रूरत हो

loose coupling और common interface

  • ऐसा system जिसमें बाकी हिस्सों को फिर से लिखे बिना कुछ हिस्से हटाए जा सकें, उसे आम तौर पर loose coupling कहा जाता है
  • loose coupling वह स्थिति है जहाँ विचार बदलने पर बहुत-सा code बदलने की ज़रूरत नहीं पड़ती
  • किसी variable को एक बार hardcode करना, या variable की जगह command-line flag का उपयोग करना भी कुछ स्थितियों में loose coupling हो सकता है
  • Microsoft Windows बाहरी API और आंतरिक API को अलग करके यह उद्देश्य हासिल करता है
    • बाहरी API desktop program के lifecycle से जुड़ी होती है
    • आंतरिक API underlying kernel से जुड़ी होती है
    • API को छिपाने से बहुत-सा software तोड़े बिना flexibility मिल सकती है
  • HTTP भी loose coupling का उदाहरण देता है
    • HTTP server के सामने cache रखा जा सकता है
    • images को CDN पर ले जाकर सिर्फ links बदलने से browser नहीं टूटता
    • HTTP error codes सामान्य समस्याओं को विशिष्ट codes देते हैं ताकि client कई errors को खुद संभाल सके
  • failure handling पर भी कोड को छोटे हिस्सों में बाँटते समय साथ-साथ विचार करना चाहिए

failure handling और coupling

  • Erlang/OTP failure को संभालने के लिए supervision tree का अपेक्षाकृत अनोखा तरीका उपयोग करता है
  • Erlang system में हर process को मोटे तौर पर supervisor शुरू करता है और मॉनिटर करता है
    • process में समस्या आने पर वह terminate हो जाता है
    • process terminate होने पर supervisor उसे फिर शुरू करता है
    • supervisor में failure आने पर bootstrap process उसे फिर शुरू करता है
  • मुख्य विचार यह है कि error को संभालने से तेज़ fail होना और restart करना कई बार अधिक प्रभावी होता है
  • transient failures कभी-कभी off-and-on तरीके से दबाए जा सकते हैं
  • error handling और recovery को codebase की बाहरी layers में करना बेहतर है; इसे end-to-end principle कहा जाता है
  • connection के बीच की बजाय endpoints पर failure handle करना आसान होता है, और अंदर संभालने पर भी अंततः top-level checks की ज़रूरत रहती है
  • error handling उन कई तरीकों में से एक है जो system को मज़बूती से बाँध देते हैं

IMAP, file system, SQL, middleware

  • IMAP में लगभग हर operation अपनी अलग options और handling के साथ एक अपवाद जैसा है, इसलिए error handling बहुत पीड़ादायक हो जाती है
  • IMAP में errors दूसरे operations के परिणामों के बीच में भी आ सकते हैं
  • UUID की जगह हर message की पहचान के लिए unique token बनाया जाता है, और यह token भी operation results के बीच बदल सकता है
  • IMAP के कई operations atomic नहीं होते
  • email को एक folder से दूसरे folder में भरोसेमंद ढंग से move करने का तरीका आने में 25 साल से ज़्यादा लगे
  • इसमें special UTF-7 encoding और एक unique base64 encoding भी है
  • file system और database remote storage के बेहतर comparison examples हैं
    • file system में operations का एक fixed set और कई objects होते हैं
    • SQL file system से अधिक विस्तृत interface जैसा दिखता है, लेकिन यह sets पर कई operations और multiple rows के pattern का पालन करता है
  • databases हमेशा एक-दूसरे के विकल्प नहीं होते, लेकिन अपने बनाए query language की तुलना में SQL के साथ काम करने वाली चीज़ें ढूँढना आसान है
  • Twitter का Finagle services के लिए common API इस्तेमाल करता है ताकि timeout handling, retry mechanism, और authentication checks को client और server code में आसानी से जोड़ा जा सके
  • loose coupling का अच्छा उदाहरण अक्सर uniform interface का भी अच्छा उदाहरण होता है
  • healthy codebase को पूरी तरह modular होना ज़रूरी नहीं, लेकिन moving parts के बीच पर्याप्त दूरी होनी चाहिए
  • loosely coupled code को हटाना हमेशा आसान नहीं होता, लेकिन उसे replace और modify करना बहुत आसान होता है

चरण 7: कोड लिखते रहना

  • अगर आप पुराने code को छेड़े बिना नया code लिख सकते हैं, तो नए ideas के साथ experiment करना बहुत आसान हो जाता है
  • मुख्य बात microservice बनाम monolith नहीं, बल्कि यह है कि आप यह समझते समय कि क्या करना है, system के ऊपर एक-दो experiments चला सकें
  • feature flags बाद में अपना विचार बदल सकने का तरीका हैं
  • feature flags सिर्फ feature experiments के लिए नहीं, बल्कि software को दोबारा deploy किए बिना changes rollout करने के लिए भी उपयोगी हैं
  • Google Chrome ने पाया कि नियमित release cycle में लंबे समय तक चलने वाली feature branches को merge करने में लगने वाला समय सबसे कठिन हिस्सा होता है
  • अगर नए code को बिना recompile किए on/off किया जा सके, तो बड़े changes को छोटे merges में बाँटा जा सकता है और existing code पर असर घटाया जा सकता है
  • जब नई functionality उसी codebase में पहले दिखने लगे, तो long-term feature development का बाकी हिस्सों पर असर अधिक स्पष्ट दिखता है
  • feature flags सिर्फ साधारण command-line switch नहीं, बल्कि feature release को branch merge और code deployment से अलग करने का तरीका हैं
  • जब नए software को deploy करने में घंटे, दिन, या हफ्ते लग सकते हों, तब runtime पर विचार बदल सकने की क्षमता और महत्वपूर्ण हो जाती है

अच्छा कोड वह legacy code है जो रास्ते में बाधा न बने

  • सिर्फ iterate करना महत्वपूर्ण नहीं, feedback loop होना अधिक महत्वपूर्ण है
  • modules को reuse के लिए बनाने से अधिक महत्वपूर्ण है changes के लिए components को isolate करना
  • बदलाव के प्रति उत्तरदायी होना सिर्फ नई features बनाना नहीं, बल्कि पुरानी features हटाना भी है
  • extensible code लिखना इस उम्मीद पर टिकता है कि 3 महीने बाद भी शुरुआती choices सही साबित होंगी
  • deletable code इसका उल्टा मानकर चलता है
  • layering, isolation, common interfaces, और composition — ये “अच्छा software” बनाने के तरीके कम, और समय के साथ बदल सकने वाला software बनाने के तरीके ज़्यादा हैं
  • सब कुछ फेंकने की ज़रूरत नहीं, लेकिन कुछ चीज़ें हटानी ही पड़ेंगी
  • अच्छा कोड वह नहीं जिसे शुरू से सही चुना गया हो, बल्कि वह legacy code है जो बाधा नहीं डालता
  • अच्छा कोड हटाने में आसान कोड है

1 टिप्पणियां

 
GN⁺ 2024-10-30
Hacker News की राय
  • मेरी पसंदीदा बात है सरलता ही मजबूती है
    Lehman के निरंतर परिवर्तन के नियम की तरह, इसका मतलब है कि सिस्टम की complexity जितनी कम होगी, उसे बदलना उतना ही आसान होगा
    भविष्य के लिए extensible code लिखने के बजाय, मुझे लगता है कि भविष्य के लिए intuitive code लिखना बेहतर है
    उदाहरण के लिए, abstraction सिर्फ तभी करें जब सच में जरूरत हो, साधारण duplication को अनुमति दें, शुरुआत monolith से करें, और horizontal scaling से पहले vertical scaling को आजमाएं
    मैंने कई 0→1 सिस्टम बनाए हैं, और उन सबमें common pattern यही था
    https://en.m.wikipedia.org/wiki/Lehman%27s_laws_of_software_...

    • सही है, लेकिन सरलता ही मजबूती है सिद्धांत लागू करते समय intrinsic complexity को भी समझना चाहिए
      edge cases को handle न करने से code मजबूत नहीं हो जाता, भले ही वह कितना भी ज्यादा सरल क्यों न दिखे
    • मेरा नियम यह है: पहली बार बस लिखो, दूसरी बार copy करो, तीसरी बार refactoring पर विचार करो
    • सहमत हूं, लेकिन simple is robust अभिव्यक्ति पर्याप्त रूप से intuitive है या नहीं, यह मुझे नहीं पता
      “सरलता” क्या है और उसे सिस्टम पर कैसे लागू किया जाता है, इस पर बहस खुल जाती है; यह इतना जटिल सवाल है कि Rich Hickey ने भी इसे address किया है
      शायद “मूर्खतापूर्ण चीज मजबूत होती है” या “सीधी-सादी चीज मजबूत होती है” इरादे को बेहतर पकड़ सकती है
    • बहुत मजबूती से सहमत हूं। software का बहुत सारा कचरा काल्पनिक समस्याओं को हल करने की कोशिश से पैदा होता है
      बस ऐसा code लिखो जो जरूरी काम करे। काल्पनिक scaling problems मत बनाओ, smart दिखने के लिए clever abstractions मत बनाओ, monolith लिखो और उसे VM पर डालो, तो वह तुरंत production में चल सकता है
      समस्या आए तो तभी हल करो, और अगर संभव हो तो cash flow positive होने के बाद
      0 users वाले “कुत्तों के लिए AirBnb” startup को C100K की चिंता क्यों है? AWS ने आपको serverless के लिए पैसे देने को मनाया, क्या वह आपके फायदे के लिए था, या पैसे निकालने के लिए?
    • business logic की complexity चाहने भर से गायब नहीं होती। अगर वह विशाल और आपस में उलझी हुई है, तो code भी वैसा ही होगा
  • संबंधित लेख:
    Write code that is easy to delete, not easy to extend (2016) - https://news.ycombinator.com/item?id=24989351 - Nov 2020 (30 comments)
    Write code that is easy to delete, not easy to extend (2016) - https://news.ycombinator.com/item?id=23914486 - July 2020 (109 comments)
    Write code that is easy to delete, not easy to extend - https://news.ycombinator.com/item?id=18761739 - Dec 2018 (2 comments)
    Write code that is easy to delete, not easy to extend - https://news.ycombinator.com/item?id=11093733 - Feb 2016 (133 comments)

  • युवावस्था में की गई अपनी गलती को संक्षेप में कहूं तो अब मैं इसके उलट delete करने के लिए design में विश्वास करने लगा हूं
    पहले मुझे लगता था कि मैं हर स्थिति का अनुमान लगाकर, हर requirement को पूरा करने वाली शानदार कलाकृति बना सकता हूं। लेकिन कोई भी भविष्य की requirements का इतना सही अनुमान नहीं लगा पाता
    किसी दिन मेरी बनाई चीज किसी के लिए “वह बेवकूफी भरी चीज” बन जाएगी, और आज मैं उस पर कितना भी गर्व करूं, उनका उसे पूरी तरह तोड़ देना उचित हो सकता है
    इसलिए बेहतर है कि उसे हटाना आसान बनाने में ऊर्जा लगाई जाए। इससे अक्सर coupling कम हो जाती है, लेकिन अहम बात यह है कि यह हर चीज को meta-configurable framework में अलग करने की उत्साही युवा developer वाली decoupling से अलग है
    कभी-कभी समझने में आसान strong coupling बेहतर भी होती है
    https://news.ycombinator.com/item?id=41219130

    • आप चीजों को हटाना आसान बना सकते हैं, लेकिन दूसरे लोग खुशी-खुशी abstractions और logic बनाकर उन्हें project के core में गाड़ सकते हैं, और बाद में उन्हें हटाना लगभग असंभव स्तर तक जमा सकते हैं
      जैसे CommonExcelFileParser, CommonExcelFileParserUtilities, HasExcelParseStatus, ProductImportExcelParser, ProductImportExcelParserView, ProductImportExcelParserResultHandler जैसी चीजें बन जाती हैं, और आसपास के code की foundation बन जाती हैं
      frontend project को React या Angular से शुरू करने पर किसी और चीज पर जाना Sisyphus के काम जैसा हो जाता है
      असल में लोग पूरा platform ही बना देते हैं, और भले ही भविष्य में समस्या पैदा करने वाले choices हों, coupling के कारण refactoring under-abstracted codebase की तुलना में कहीं ज्यादा कठिन हो जाती है
      लोगों को KISS और YAGNI लागू करके delete करने में आसान code बनाने के बजाय ऐसी चीजें ज्यादा पसंद लगती हैं, इसलिए ऐसे समय क्या करना चाहिए, समझ नहीं आता
    • फिर भी यह context पर निर्भर करता है। अगर business application है, तो सही है, दस बार सही है
      business requirements बदलती और हिलती रहती हैं, इसलिए उनका अनुमान लगाने की कोशिश न करें; ऐसी चीज लिखें जिसे replace या throw away करना आसान हो
      frameworks और libraries थोड़ी अलग हैं। उन्हें दुनिया के बदलावों के साथ चलना तो होता है, लेकिन काफी अधिक संयमित गति से
      सबसे बड़ी समस्या तब होती है जब Rails या Asp.Net जैसे framework का उपयोग कर रही business application में developers फिर से “framework” बनाना चाहते हैं
    • कुछ चीजें बदलती हैं, और कभी-कभी आप गलत abstraction चुन लेते हैं
      अगर आप Linux kernel नहीं लिख रहे हैं, तो Linux kernel की तरह नहीं लिखना चाहिए
  • यह काफी अजीब है कि इस लेख में testing और observability पर बिल्कुल बात नहीं की गई
    testing की भी maintenance cost होती है, लेकिन जब आप कुछ हटाते हैं तो उससे कुछ टूटने का जोखिम कम हो जाता है
    इसके अलावा, अगर आपने कोई service external callers के लिए expose की है, तो आपको कुछ calls को deprecated mark करके बाद में हटाने का मजबूत तरीका भी चाहिए, और यह observe करने का तरीका भी कि वे अभी भी call हो रही हैं या नहीं और कौन call कर रहा है
    हाल ही में हमने पहली बार exposed GraphQL resolver को semi-automatic तरीके से हटाया; हमारे पास पहले से metrics थे कि कोई खास resolver कितनी बार use हो रहा है, इसलिए उन्हें parse करके उन resolvers की list निकाली जिन्हें हटाया नहीं जा सकता था
    GraphQL में पहले से deprecated annotation है, लेकिन हमारी service उस annotation को खास तौर पर handle नहीं करती थी
    इसलिए हमने observability जोड़ी जो deprecated function call होने पर signal देती थी, उसे production में पर्याप्त समय तक चलाया, और फिर external-facing code को सुरक्षित रूप से हटाना संभव हुआ

    • थोड़ा सरल करके कहें तो, जिसे हटाना आसान हो ऐसा कुछ बनाने से हटाते समय अनजाने bugs नहीं बनते
      जब आप चीजों को जरूरत से ज्यादा complex बनाना शुरू करते हैं, तो सब कुछ आपस में जुड़ी हुई गड़बड़ी बन जाता है, और developer यह नहीं जान पाता कि बदलाव का क्या असर होगा
      बेशक चीजें बिगाड़ने के कई तरीके हैं। आप मूर्खतापूर्ण “best practices” principles follow कर सकते हैं, या “microservices” इस तरह कर सकते हैं कि पता ही न हो कौन कौन-सी service consume कर रहा है। लेकिन तब आपने उसे हटाने में आसान नहीं बनाया
      external consumption एक अच्छा example है। service deprecation के बारे में consumers को reasonable warning देना सही है, लेकिन अगर आप जब चाहें उसे सच में बंद नहीं कर सकते, तो वह ऐसा system नहीं है जिसे आसानी से delete होने के लिए design किया गया हो
      अगर वही तरीका सही है तो वैसा कर सकते हैं। बस testing और observability से यह उम्मीद करना कि वे बताएंगी क्या टूटता है, शायद अच्छी तरह काम न करे
      मैं testing के खिलाफ नहीं हूं, लेकिन लंबी और complex chain में आपने कुछ तोड़ा है या नहीं, यह बताने वाले guardrail के रूप में इसे बहुत शानदार मानना मुश्किल है। क्योंकि सच में protect करने वाली test coverage बनाना भी बेहद कठिन है
    • code lines ज्यादा हों तो अनुपात में test lines भी कुछ हद तक होंगी, यह उम्मीद की जा सकती है
      अगर आप कुछ code delete करते हैं तो कुछ tests भी delete कर सकते हैं
      लेख की तरह सिर्फ code की बात करना और tests पर संबंधित प्रभाव को implicit मानना संभव है
      यह मान नहीं सकते कि लेख ने tests का जिक्र नहीं किया, इसलिए उसका मतलब tests न लिखना है
    • tests अच्छी चीज हैं, लेकिन programming सिर्फ tests लिखने पर खत्म नहीं होती। हर लेख में tests का जिक्र जरूरी नहीं है
  • इस हिस्से को देखकर लगता है कि title हमेशा सही नहीं है: delete करने में आसान code अक्सर extend करने में भी आसान code होता है
    क्योंकि वह layered, modular होता है, और interfaces या दूसरे type contracts जैसी abstractions के जरिए अलग-अलग pieces को isolate करता है

  • मैंने computational physics के students से कहा है कि सबसे अच्छी computation वह है जिसे करने की जरूरत ही न पड़े

  • निजी तौर पर मैं code को दो हिस्सों में बांटता हूं: business logic और actual implementation
    business logic अपनी प्रकृति से duplicate हो सकता है, लेकिन technical details बहुत ज्यादा duplicate नहीं होनी चाहिए
    actual implementation चाहे जितना messy हो सकता है, बशर्ते उसमें business logic सीधे न हो और वह application से independent रहे
    ऐसा कर देने पर, जब आपको पता चले कि कुछ गड़बड़ है और ठीक से चल नहीं रहा, तो implementation से actual specification को reverse-engineer करके जबरन fix करने के बजाय पूरा implementation मिटा देने का विकल्प बचता है

  • पहले paragraph में “code reuse की समस्या यह है कि वह बाद में मन बदलने में बाधा बनता है” साफ तौर पर गलती है
    सामान्य तौर पर कहा जाए तो यह गलत है। अगर आपका मन बदल गया और code दस जगह copy-paste है, तो आपको दस जगह fix करना होगा
    इसके उलट अगर वह function में है, तो एक बार बदलना होगा। अगर बाद में पता चले कि दस calls में से एक नहीं बदलनी चाहिए, तो उस समय copy-paste कर सकते हैं या function को ज्यादा general बना सकते हैं
    सड़क पार करते समय बिना देखे पार करने जैसा, copy-paste लगभग हमेशा खराब idea है

    • मेरे अनुभव में खराब copy-paste code एक झुंझलाहट भरी दोपहर के technical debt repayment और fixes पर खत्म हो जाता है
      लेकिन खराब abstraction कई महीनों के technical debt repayment में बदल जाती है
      बेशक जवाब है “खराब abstraction मत बनाओ”, लेकिन teams और बदलती product requirements में यह कैसे होता है, हम सब जानते हैं
    • reused code कई जगह सही code होता है, इसलिए बदलने के लिए आपको धीमा पड़कर उन points को अलग करना पड़ता है
      हमारे पास common UI widgets वाला एक git submodule है, और अब उनमें से किसी एक को बदलना लगभग नामुमकिन है, इसलिए component को project के अंदर copy करके locally बदलना आसान है
      यह समस्या है। shared code जितना हो सके न्यूनतम होना चाहिए, और sharing खुद बदलाव को कठिन बनाती है
    • अगर 10 function calls में से 3 को एक तरीके से, 5 को दूसरे तरीके से बदलना हो, और बाकी 2 अब वही abstraction इस्तेमाल न करते हों इसलिए पूरी तरह rewrite करने पड़ें, तो क्या होगा
      अगर सब कुछ एक function में है, तो ज्यादातर developers उस function को सभी 10 cases satisfy करने के लिए बदलने की कोशिश करेंगे। जबकि शुरुआत से ही वह एक function होना ही नहीं चाहिए था
      एक बार बांधकर system के हिस्सों को पकड़े हुए गलत बंधी गांठ खोलने से, copy-paste की हुई दस जगहों को fix करना कहीं ज्यादा आसान है
    • लेखक शायद जवाब देगा कि उस code को module या function में ले जाना चाहिए था
      ऊपर-ऊपर से देखें तो इस topic में वह खुद से contradict करता लगता है, लेकिन ध्यान से पढ़ें तो वह copy-paste को signal की तरह use कर रहा है कि कौन-सा code abstract होना चाहिए, और असली pattern किसे follow करना चाहिए
  • software के बारे में तरह-तरह के commandments, लगभग धर्म जैसे principles, बार-बार दोहराना अजीब है
    कागज पर वे सभी शानदार दिखते हैं और common sense जैसे लगते हैं, लेकिन 50 साल बाद भी software 90% मामलों में कचरा है
    फिर भी इन्हें genius insight या silver bullet की तरह बार-बार निकाला जाता है

    • मुझे लगता है उस 90% कचरे को वे लोग लिखते हैं जो ऐसे लेख पढ़ते या लिखते नहीं हैं
  • इसका एक बेहतरीन corollary है। खराब code हटाना कहीं ज्यादा मुश्किल होता है, इसलिए वह लंबे समय तक बना रहता है