5 पॉइंट द्वारा GN⁺ 2025-06-18 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • लंबे समय से प्रोग्रामिंग कर रहे “छोटे दिमाग” वाले डेवलपर के अनुभवों के ज़रिए यह जोर दिया गया है कि software development का सबसे बड़ा दुश्मन complexity है, और इसे घटाने वाला रवैया ही वास्तविक काम के केंद्र में होना चाहिए
  • complexity को रोकने का मूल हथियार “no” कहना है, और जब समझौता ज़रूरी हो तो 80/20 solution, prototype, छोटे refactoring और देर से abstraction के ज़रिए व्यावहारिक समाधान खोजे जाते हैं
  • tests कोड के कुछ हद तक स्थिर हो जाने के बाद integration tests को केंद्र में रखकर लिखे जाते हैं; unit tests और कुछ end-to-end tests सहायक के रूप में इस्तेमाल होते हैं, और bugs को ठीक करने से पहले regression test से reproduce किया जाता है
  • tools, type system, logging, debugger और सरल API डेवलपर की memory और reasoning का बोझ घटाते हैं, लेकिन जरूरत से ज्यादा generics, callbacks, microservices और frontend frameworks complexity बढ़ा सकते हैं
  • मौजूदा code और organizational processes के प्रति विनम्रता से पेश आना चाहिए, जिस code को समझा न गया हो उसे लापरवाही से नहीं हटाना चाहिए, और ऐसी culture चाहिए जिसमें “यह बहुत complex है” कहा जा सके

complexity डेवलपर की हमेशा की दुश्मन है

  • software development में सबसे खतरनाक दुश्मन complexity है
    • complexity धीरे-धीरे codebase में घुसती है और एक जगह बदलाव करने पर ऐसी जगहों को भी तोड़ देती है जो संबंधित नहीं लगतीं
    • Grug इसे अदृश्य “दानव” से तुलना करता है—ऐसी चीज़ जिसे डेवलपर सीधे देख नहीं सकता या आसानी से मार नहीं सकता
  • complexity नेकनीयत developers या project managers के ज़रिए भी आ सकती है
    • features, abstractions और processes बढ़ने पर code समझना मुश्किल हो जाता है
    • Grug खुद भी मानता है कि कई बार complexity लाने वाला वही था

“no” और “ok” का इस्तेमाल कैसे करें

  • complexity रोकने का सबसे मजबूत हथियार “no” है
    • गैर-जरूरी features न बनाएं
    • गैर-जरूरी abstractions न बनाएं
  • हालांकि “no” अच्छी engineering सलाह है, लेकिन career के लिहाज से हमेशा फायदेमंद नहीं होता
    • “yes” ज्यादा rewards या management roles तक ले जा सकता है
    • फिर भी developer के रूप में खुद के प्रति ईमानदार रहना हो तो “no” अहम है
  • जब समझौता करना पड़े, तो “ok” कहकर 80/20 solution खोजें
    • 80/20 solution ऐसा तरीका है जिसमें 20% code से 80% जरूरतें पूरी की जाती हैं
    • यह सभी सजावटी features नहीं दे पाता, लेकिन अधिकतर value देता है और complexity को काबू में रखता है
    • कभी-कभी project manager को implementation की सारी details समझाने के बजाय सरल तरीके से हल कर देना बेहतर माना गया है

code structure और refactoring

  • project की शुरुआत में application को बहुत जल्दी हिस्सों में नहीं बांटना चाहिए
    • शुरू में system का आकार अभी साफ नहीं होता और क्या बनाना है यह भी पूरी तरह समझ में नहीं आया होता
    • समय के साथ अच्छे cut points दिखने लगते हैं
  • अच्छा cut point बाकी system के साथ narrow interface रखता है
    • कम functions या abstractions से अंदर की complexity छिपाता है
    • Grug इसे complexity के दानव को crystal के अंदर कैद करने जैसा बताता है
  • जब बड़ा-दिमाग developer project की शुरुआत में बहुत सारी abstractions बनाना चाहता है, तो नुकसान घटाने का तरीका चाहिए
    • उसे UML diagrams जैसे ऐसे outputs की ओर मोड़ा जा सकता है जो code को सीधे नुकसान नहीं पहुंचाते
    • “कल चलने वाला demo” मांगने पर काम करने वाला code और reality जल्दी दिख जाती है
    • इस demo approach को prototype कहा जा सकता है
  • refactoring project के बाद के चरण में, जब code स्थिर हो चुका हो, उपयोगी होती है
    • refactoring जितनी बड़ी होगी, fail होने की संभावना उतनी ज्यादा होगी
    • system को चलते हुए बनाए रखते हुए छोटे steps में आगे बढ़ना बेहतर माना गया है
    • end-to-end tests refactoring के दौरान lifeline बनते हैं, लेकिन टूटने पर कारण समझना मुश्किल हो सकता है
  • जरूरत से ज्यादा abstraction refactoring failure और system failure तक ले जा सकती है
    • J2EE को excessive abstraction के उदाहरण के रूप में पेश किया गया है
    • OSGi अपनाना complexity घटाने की कोशिश थी, लेकिन उसने उलटे और ज्यादा मजबूत complexity पैदा कर दी, और कई person-years का rework चाहिए पड़ा

testing strategy

  • tests बहुत समय बचाते हैं, लेकिन “हर हाल में tests पहले” को लेकर संदेह है
    • जब domain अभी समझ में नहीं आया है, तो क्या test करना है यह जानना मुश्किल होता है
    • Grug prototype के बाद, जब code स्थिर होने लगे, तब अधिकतर tests लिखना पसंद करता है
  • tests बाद में लिखे जाएं तब भी discipline जरूरी है
    • “मेरी machine पर चलता है” के आधार पर tests skip नहीं करने चाहिए
    • इसकी कोई guarantee नहीं कि यह दूसरी machine पर या भविष्य में उसी machine पर चलेगा
  • unit tests, integration tests और end-to-end tests की भूमिकाएं अलग की गई हैं
    • unit tests project की शुरुआत में मदद करते हैं, लेकिन implementation बदलने पर आसानी से टूटते हैं और refactoring मुश्किल बना सकते हैं
    • end to end tests पूरे system का behavior दिखाते हैं, लेकिन टूटने पर कारण पहचानना मुश्किल होता है और बार-बार टूटें तो इन्हें ignore किया जा सकता है
    • integration tests system correctness जांचने के लिए काफी high-level और debugger से कारण देखने के लिए काफी low-level होते हैं, इसलिए “sweet spot” के करीब हैं
  • test setup का ideal रूप इस तरह है
    • शुरुआत में कुछ unit tests
    • cut points बनने और system के stable होने के साथ मजबूत integration tests
    • सबसे common UI features और important edge cases तक सीमित छोटे, अच्छी तरह maintain किए गए end-to-end tests
  • mocking का इस्तेमाल कम ही किया जाए, और जरूरत होने पर भी system boundaries जैसे बड़े units पर ही करना पसंद किया गया है
  • bug fix करते समय अपवाद के तौर पर पहले regression test से bug reproduce करें, फिर fix करें

process, Agile और existing code के प्रति रवैया

  • Agile सबसे बुरा नहीं है, लेकिन इसे सिर्फ अच्छा कहना भी मुश्किल है
    • development organization के तरीके के रूप में यह कुछ हद तक ठीक हो सकता है
    • हर failure पर “Agile सही तरीके से नहीं किया गया” कहने वाले Agile experts से सावधान रहने को कहा गया है
  • success के लिए prototype, tools और अच्छे developers hiring ज्यादा महत्वपूर्ण हैं
    • Agile process मदद कर सकता है, लेकिन उसे बहुत गंभीरता से लेने पर नुकसान हो सकता है
    • no silver club, यानी सभी software problems हल करने वाली कोई silver bullet नहीं है
  • Chesterton’s Fence existing code हटाने के खिलाफ चेतावनी के रूप में इस्तेमाल होता है
    • अगर किसी fence का उद्देश्य नहीं पता, तो पहले उसे समझना चाहिए; उसे तुरंत हटाना नहीं चाहिए
    • बदसूरत code भी अगर आज काम कर रहा है, तो उसका सम्मान करना चाहिए
    • खासकर बड़े systems में पहले समझना और फिर सुधार करना चाहिए
  • tests यह hint दे सकते हैं कि कोई “fence” क्यों मौजूद है

tools और type system

  • tools डेवलपर पर खुद याद रखने और reasoning करने का बोझ घटाते हैं
    • नए environment में जाने पर आसपास के tools सीखने में समय लगाना productivity बढ़ा सकता है
    • documentation न हो तो दूसरे developers से पूछकर समझना पड़ सकता है
  • IDE की code completion सभी APIs याद रखने की जरूरत खत्म कर देती है
    • कहा गया है कि code completion के बिना Java programming लगभग असंभव है
  • अच्छा debugger बहुत महत्वपूर्ण है
    • conditional breakpoints, expression evaluation और stack navigation जैसी features गहराई से सीखनी चाहिए
    • नए developer के लिए debugger सीखना computer के बारे में university classes से ज्यादा सिखा सकता है
  • type system की सबसे बड़ी value यह है कि “dot(.) दबाने पर क्या किया जा सकता है, वह दिख जाता है”
    • type correctness भी अच्छी चीज है, लेकिन Grug के लिए tool support और code completion ज्यादा बड़ी value है
    • जरूरत से ज्यादा type abstraction या generics real-world work code को कठिन बना सकते हैं
    • generics को मुख्यतः container classes तक सीमित रखकर इस्तेमाल करना पसंद किया गया है

expressions, DRY, separation of concerns

  • छोटे code के बजाय debug करना आसान code पसंद किया गया है
    • complex conditional expression को एक line में लिखने के बजाय intermediate variables में बांटने से हर expression का result और meaning देखना आसान होता है
    • lines बढ़ जाएं तब भी conditionals को समझना और debug करना आसान हो जाता है
  • DRY अच्छी सलाह है, लेकिन balance जरूरी है
    • सरल और साफ duplicate code, complex callbacks, closures और object model से बेहतर हो सकता है
    • duplication हटाने की कोशिश में complexity बढ़ जाए तो नुकसान हो सकता है
  • Separation of Concern को लेकर रुख ज्यादा आलोचनात्मक है
    • web development में इसका typical example CSS, HTML और JavaScript को अलग करना है
    • Grug इसके विकल्प के तौर पर locality of behavior पसंद करता है
    • जिस चीज़ पर behavior लागू होता है, उसके पास संबंधित code रखने से उसे देखते ही पता चल जाता है कि वह क्या करता है
  • closures सही उपयोगों, जैसे collection operations abstraction, में उपयोगी हैं
    • लेकिन नमक, type system और generics की तरह थोड़ा इस्तेमाल ही काफी है; ज्यादा होने पर नुकसानदेह है
    • JavaScript का “callback hell” closures के overuse का उदाहरण है

logging, concurrency, optimization

  • logging खासकर cloud deployment environment में बहुत महत्वपूर्ण है
    • हर मुख्य logic branch पर log छोड़ें
    • request कई machines में फैले तो सभी logs में request ID डालें ताकि उन्हें जोड़ा जा सके
    • संभव हो तो log level dynamically adjust करें
    • संभव हो तो per-user log level भी adjust करें
  • Java logging libraries complex हो सकती हैं, लेकिन logging infrastructure में सही investment करने पर बाद में बड़ा return मिलता है
  • concurrency ऐसी चीज है जिससे डरना चाहिए
    • जहां तक संभव हो, stateless web request handlers और independent remote work queues जैसे सरल models इस्तेमाल करें
    • optimistic concurrency web क्षेत्र में अच्छा काम करती है
    • thread local variable मुख्यतः framework code लिखते समय कभी-कभी इस्तेमाल होता है
    • Java की ConcurrentHashMap जैसी concurrent data structures के साथ भी सावधानी रखनी चाहिए
  • optimization तभी शुरू करनी चाहिए जब actual performance profile हो
    • “premature optimization is the root of all evil” से सहमति है
    • actual bottleneck अनुमान से अलग हो सकता है
    • सिर्फ CPU नहीं देखना चाहिए; network calls लाखों CPU cycles के बराबर हो सकती हैं, इसलिए उन्हें जहां संभव हो घटाना चाहिए

API, parsing, visitor pattern

  • अच्छा API डेवलपर को ज्यादा सोचने पर मजबूर नहीं करता
    • खराब API तब बनता है जब वह implementation internals या domain model के हिसाब से design हो, या जरूरत से ज्यादा abstract हो
    • सरल cases के लिए सरल API और complex cases के लिए ज्यादा complex API देने वाली layering पसंद की गई है
  • object-oriented API में functionality को target object से जोड़ना बेहतर माना गया है
    • Java में list filter करने के लिए उसे Stream में बदलना और फिर वापस List में collect करना खराब example बताया गया है
    • आम operation filter() list पर होना चाहिए और list ही return करना चाहिए
  • parser के लिए recursive descent मजेदार और सुंदर तरीका है
    • parser generators को समझना और debug करना मुश्किल बताया गया है
    • दावा है कि real production parsers लगभग हमेशा recursive descent होते हैं
  • Bob Nystrom की Crafting Interpreters recommend की गई है
    • इसे online मुफ्त में देखा जा सकता है, लेकिन book खरीदना भी recommend किया गया है
    • हालांकि visitor pattern को trap बताया गया है
  • Visitor pattern पर फैसला संक्षेप में “bad” है

frontend, microservices, trends

  • microservices खतरनाक हैं क्योंकि वे “system को सही तरह से बांटने की सबसे कठिन problem में network calls भी जोड़ देते हैं”
  • frontend development में complexity खास तौर पर मजबूत है
    • simple form save या brochure site के लिए भी SPA library, GraphQL JSON API और HTTP backend इस्तेमाल होने की स्थिति की आलोचना की गई है
    • frontend और backend को अलग करने पर complexity के रहने की दो जगहें बन जाती हैं, ऐसा कहा गया है
  • Grug बताता है कि complexity घटाने के लिए उसने htmx और hyperscript बनाए
    • वह simple HTML बनाए रखने और बहुत ज्यादा JavaScript न इस्तेमाल करने की दिशा पसंद करता है
    • वह मानता है कि jobs और कुछ खास तरह की applications के लिए React बेहतर हो सकता है
  • development में बहुत trends होते हैं, और frontend में खासकर ऐसा है
    • backend ज्यादा boring हो गया है और कई खराब ideas पहले ही आजमाए जा चुके हैं, ऐसा कहा गया है
    • revolutionary new approach को नमक की चुटकी जितनी सावधानी से अपनाने को कहा गया है
    • कई ideas पहले कभी न कभी आजमाए जा चुके हैं, और recycled bad ideas समय बर्बाद कर सकते हैं

डर और imposter syndrome

  • senior developer का public में “यह बहुत complex है” कहना अच्छा है
    • developers Fear Of Looking Dumb(FOLD) के कारण यह कहना मुश्किल समझते हैं कि उन्हें समझ नहीं आया
    • senior पहले स्वीकार करे तो junior भी complexity और समझ की कमी के बारे में कह सकता है
  • FOLD खासकर युवा developers में complexity को ताकत देने वाला मुख्य source है
    • humor sense और पिछली failures को याद रखने वाला रवैया मददगार माना गया है
  • imposter syndrome भी development में आम है
    • Grug कहता है कि वह सब कुछ control करने की feeling और यह न जानने की feeling के बीच है कि वह क्या कर रहा है
    • htmx और _hyperscript की open source achievements होने के बावजूद वह अब भी mistakes और failures से डरता है
  • अगर सब imposter syndrome महसूस करते हैं, तो यह मानना बेहतर है कि कोई भी सच में fake नहीं है

recommended reading और conclusion

1 टिप्पणियां

 
GN⁺ 2025-06-18
Hacker News की राय
  • अगर प्रोफेसर Carson टिप्पणियाँ देख रहे हों, तो मैं उनके अब तक के सभी योगदानों के लिए दिल से धन्यवाद कहना चाहूँगा
    उस समय समझ नहीं आता था कि विश्वविद्यालय में हम HTMX क्यों सीख रहे हैं, और आप इसे लेकर इतने उत्साहित क्यों थे, लेकिन कुछ साल बाद अब समझ आता है। असल बात पूरी तरह HTML over the wire ही थी
    Staff Ruby on Rails Engineer के रूप में काम करते हुए मैंने Hotwire में आपका काम देखा, और Hacker News पर कभी-कभी आपका दिखना या GitHub पर Hotwire developers से आपकी बातचीत देखना भी शानदार था। programming community में आप एक रोशनी जैसे व्यक्ति हैं, जिनका बहुत सम्मान और आभार है

  • “एक अच्छा debugger चमकते पत्थर जितना कीमती है, बल्कि सच कहें तो उससे भी ज़्यादा” — इस बात से सहमत हूँ
    छोटे startup और ‘elite’ big tech teams, दोनों में काम किया है, लेकिन team में debugger इस्तेमाल करने वाला लगभग हमेशा सिर्फ मैं ही था। असली दुनिया में, कम से कम web technologies के क्षेत्र में, लगता है ज़्यादातर लोग print statements से ही debugging करते हैं
    test चलने के दौरान code की किसी दिलचस्प line पर रुकना और वहाँ तक पहुँचा call stack देखना, दिमाग में code को आगे-आगे चलाकर समझने से कहीं आसान है। अगर आप युवा grug हैं, तो यह skill एक छोटी superpower जैसी है; संभव हो तो इसे अपने codebase में काम कराने में समय लगाना वाजिब है

    • कुछ साल पहले इस विषय पर अच्छी चर्चा हुई थी, और सबसे ऊपर वाले comment ने Brian Kernighan और Rob Pike का quote साझा किया था। दोनों को युवा grug कहना मुश्किल है
      उन्होंने कहा था कि वे stack trace या एक-दो variables की value देखने से आगे debugger का बहुत उपयोग नहीं करते, क्योंकि complex data structures और control flow की बारीकियों में फँसना आसान होता है; उनके हिसाब से अहम जगहों पर output statements और self-checking code डालना ज़्यादा productive है।
      मैं भी काफी हद तक सहमत हूँ। मेरे लगभग हर काम में hypothesis-log-run का loop जवाब तक कहीं तेज़ पहुँचाता है। मैं दिमाग में code चलाने की कोशिश नहीं कर रहा होता; मेरे पास पहले से code कैसे चलता है इसका working model होता है, और अगर वह model सही है तो किस तरह का output आना चाहिए यह पता होता है, जबकि गलत output से वास्तविक स्थिति का जल्दी अंदाज़ा लग जाता है
      [0] The unreasonable effectiveness of print debugging (349 points, 354 comments) April 2021 https://news.ycombinator.com/item?id=26925570
    • सचमुच debugger इस्तेमाल करना चाहता हूँ, लेकिन बड़ी कंपनियों में ही काम करने के अनुभव में यह विकल्प कभी रहा नहीं
      microservices mesh architecture में local पर कुछ ठीक से चलाना भी मुश्किल होता है, और test environment भी अक्सर step-through debugger attach करने लायक configured नहीं होता। आखिर में print debugging ही बचती है, और अगर logging system खुद ही problem हो या program logs flush करने से पहले crash हो जाए, तो वह भी काम नहीं आता
    • किसी codebase में जाते ही सबसे पहले किए जाने वाले कामों में से एक यह होता है कि, भले ही उस समय कोई immediate bug fix न कर रहा हूँ, program को debugger में जल्दी चलाने के लिए IDE/editor setup तैयार करूँ
      थोड़े ही समय में इसकी ज़रूरत पड़ ही जाती है। लेकिन collaboration के दौरान भी ऐसे लोगों को देखकर हैरानी हुई जिन्हें debugger इस्तेमाल करना ही नहीं आता। “वहाँ breakpoint लगाइए”, “अब function के अंदर step into करके variable state देखिए”, “उसे step over कर दीजिए” — कहने पर भी हर बार खाली-सी नज़र मिलती है
    • मैं भी debugger बहुत कम इस्तेमाल करने वालों में हूँ
      सख्ती से कहें तो मैं print statements डालकर हटाता नहीं, बल्कि ऐसा logging code डालता हूँ जो बना रहे। अगर कोई major interface हो, तो आमतौर पर INFO level पर function entry/exit और parameter values log करने से शुरू करता हूँ, और system इस्तेमाल करते हुए जहाँ और जाँच की ज़रूरत दिखती है वहाँ और detailed logs जोड़ता हूँ
      log format पर भी काफी मेहनत करता हूँ। distributed systems पर काम करते समय हर log line का prefix सही रखना बहुत उपयोगी था। node ID, pid और timestamp को fixed width में डालकर पूरे cluster के logs download करके sort करने पर, कई nodes की activity एक ही file में interleave हुई दिखती थी
    • अपने code में debugger इस्तेमाल करना आसान है और मुझे पसंद भी है
      लेकिन जैसे ही debugger मेरी इस्तेमाल की हुई libraries या frameworks के अंदर गहराई में चला जाता है, मैं रास्ता भटक जाता हूँ और यह अप्रिय लगने लगता है। वे frameworks या libraries दसियों हजार person-hours के काम से बने होते हैं, इसलिए लगता है कि वे मेरी समझ के स्तर से बहुत आगे हैं
  • यहाँ कई रत्न जैसी पंक्तियाँ हैं, लेकिन microservices पर यह वाली सबसे अच्छी लगी: “grug सोचता है कि समझदार लोग systems को सही तरह से बाँटने की सबसे कठिन समस्या को उठाकर उसमें network calls भी क्यों डाल देते हैं”

    • मैं इसे 1–2 लोगों वाली छोटी dev teams को लगातार समझाने की कोशिश करता हूँ
      वे पाँच form वाली एक मामूली web app को खुशी-खुशी “microservices” में तोड़ देते हैं, उन्हें वही database share करवाते हैं, API management layer और बड़े (megabyte) batch jobs के लिए queue, email notification system, खुद बनाया observability platform तक जोड़ देते हैं, और फिर “यह ज्यादा आसान है” कहते हुए एक simple web form को SPA में बदल देते हैं
      अब समझ आता है कि “architecture” और “patterns” बेकार developers के लिए jobs creation program हैं। वरना शायद वे सड़क पर “sandwich दोगे तो JavaScript लिखूँगा” वाला sign पकड़े खड़े होते
    • कुछ लोगों को system को छोटे हिस्सों में बाँटने के दूसरे तरीके पता ही नहीं लगते
      उनके लिए अगर कोई चीज API call के रूप में expose नहीं है, तो वह बस code का एक opaque ढेला है जिसे वे समझ या reuse नहीं कर सकते
    • network boundary decomposition का ऐसा tool देती है जो ज्यादातर language module systems में नहीं होता
      कई packages अंदरूनी तौर पर मिलकर काम कर सकते हैं, लेकिन बाकी codebase को केवल एक छोटा API expose कर सकते हैं। यह कि वह network है, modules को callbacks या behavior नहीं बल्कि सिर्फ data भेजने-लेने के लिए मजबूर करता है, और interfaces को backward-compatible तरीके से evolve करने का दबाव डालता है। इसलिए अलग-अलग modules को अलग-अलग समय पर “hot reload” करने पर भी वे फटते नहीं
      लगता है कि असली network hop के बिना भी इसका अधिकांश फायदा मिल सकता है, लेकिन कोई गंभीर कोशिश अभी तक नहीं देखी
    • मेरी conspiracy theory है कि microservices वह pattern है जिसे cloud companies ने लोगों से ऐसी applications बनवाने के लिए push किया
      K8S जैसे orchestrator के बिना इन्हें चलाया नहीं जा सकता, और install/operate करना मुश्किल है, इसलिए managed cloud बेचने में फायदा होता है। ये network bandwidth और CPU ज्यादा इस्तेमाल करवाते हैं, और दोनों पर billing होती है
      application के अंदर complex या बड़ा state share/maintain करना मुश्किल बना देते हैं, जिससे लोग managed databases या event queue services को विकल्प के रूप में इस्तेमाल करते हैं। monolith होता तो queue या channel से काम चल जाता, लेकिन microservices में Kafka जैसे किसी विशाल जानवर की इच्छा होने लगती है
      local execution भी मुश्किल हो जाता है, इसलिए cloud development environment चाहिए होता है, और शायद कई dev/test environments रखने पड़ते हैं। किसी खास cloud के networking model जैसी चीजों पर dependency बनती है, जिससे cloud lock-in भी बढ़ता है
      पता नहीं याद है या नहीं, जब cloud को IT costs घटाने वाली चीज कहकर बेचा जाता था। वह मजाकिया था; 2000s से ही पता था कि यह बकवास है और आखिर में सारी costs बढ़ाएगा
    • मुझे लगता है यह मुख्यतः teams के बीच system बाँटने के लिए है
      क्योंकि उस तरीके को manage करना आसान है, और यह technical decision से ज्यादा development organization model जैसा है। अगर alternative monorepo है, तो निजी तौर पर मुझे वह इससे भी खराब लगता है
  • “complexity और tyrannosaurus के साथ 1:1 में से चुनना हो तो grug tyrannosaurus चुनेगा। कम से कम grug tyrannosaurus को देख तो सकता है”
    मैं इस पंक्ति को कम से कम हफ्ते में एक बार याद करता हूँ

    • grug ने साफ तौर पर invisible tyrannosaurus का सामना कभी नहीं किया
      यह grug एक transparent tyrannosaurus के साथ 1:1 में है, और शापित है
    • उसे पढ़कर लगा जैसे तीसरी आँख खुल गई हो। सच में प्रेरक था
  • इस लेख की एक value यह है कि जो व्यक्ति ज्यादा sophisticated और complex काम कर सकता है, वह अनुभव के आधार पर जानबूझकर उससे बचना चाहता है
    sophistication और higher abstraction की जरूरत के समय और जगह निश्चित रूप से होते हैं। लेकिन grug philosophy कहती है कि ऐसे काम करने में अपने-आप कोई intrinsic value नहीं है, और यह काफी उचित सलाह लगती है
    मैंने देखा है कि AI assistance भी consistent, boring और data-centric code पर ज्यादा effective होती है। संदर्भ के हिसाब से फर्क हो सकता है

    • यह bell curve meme में बिल्कुल फिट बैठता है
      beginner developer simple code लिखता है, intermediate developer complex code लिखता है, और expert developer फिर से simple code लिखता है
    • sophistication और abstraction इस्तेमाल करने का समय और जगह तब है, जब वे किसी पहले से खास lecture सुनने पर ही समझ आने वाली चीज न होकर सच में code समझना आसान बनाते हों
      कौन-सा lecture सुना हुआ मान लेना उचित है, यह context पर निर्भर करता है
    • “हर चीज को जितना संभव हो उतना simple बनाना चाहिए, लेकिन उससे ज्यादा simple नहीं”
  • आधुनिक software development की विडंबनाओं में से एक यह है कि हम यह सोचकर complexity लाते हैं कि “आख़िरकार इससे समय बचेगा”
    कभी-कभी यह सही होता है और सच में समय बचाता है, लेकिन हमेशा नहीं, और शायद अक्सर भी नहीं
    DRY कभी-कभी जल्दबाज़ी में की गई abstraction की ओर ले जाता है। जैसे ही हम सोचते हैं, “यह pattern दूसरी जगह भी इस्तेमाल होगा, तो common हिस्सा निकाल लेते हैं,” complexity वाला शैतान अंदर आ जाता है
    हम चाहते हैं कि जितने ज़्यादा bugs हो सकें compile-time पर पकड़ लिए जाएँ, लेकिन उसके लिए compiler को यह ज़्यादा जानना पड़ता है कि हम असल में क्या करना चाहते हैं, और फिर हम ऐसे जटिल types बना देते हैं जो समझ को कमज़ोर कर देते हैं
    boilerplate से बचने के लिए हम जटिल macros या DSL बनाते हैं, लेकिन leaky abstractions के कानून की वजह से जैसे ही वास्तविक implementation जानने की नौबत आती है, दिमाग फटने लगता है
    इन सभी उदाहरणों में मुश्किल बात यह है कि कभी-कभी ये अच्छे विचार भी होते हैं। कब complexity लाने से सच में सरलता आती है, यह判断 कर पाना ही अच्छे software engineer की निशानी माना जाता है

    • अनुभव के आधार पर, DRY की तुलना में SPoT(single point of truth, सत्य का एकल स्रोत) बेहतर कहावत रही है
      business logic आदर्श रूप से एक ही जगह defined होना चाहिए, लेकिन बाकी चीज़ें ज़रूरत पड़ने पर duplicate की जा सकती हैं, और यह अपने-आप में बुरा नहीं है
      DRY को संतुलित करने के लिए “rule of three” पर भी ज़ोर देता हूँ। copy/paste code तीन बार तक ठीक है, उसके बाद abstraction के बारे में सोचा जा सकता है। बेशक हर मामले पर लागू होने वाला कोई rule of thumb नहीं है, और वह intuition सिखाना मुश्किल है
    • औसतन, मेरा मानना है कि ज़्यादातर code अब भी पर्याप्त DRY नहीं होता, लेकिन personal projects में हाल ही में मैंने यह सिद्धांत अपनाया है कि “application नहीं हैं, सिर्फ screens हैं।” दिलचस्प बात यह है कि मैं HTMX इस्तेमाल कर रहा हूँ, जिसे शायद इसी blog के लेखक ने बनाया है
      आम तौर पर Flask जैसे Sinatra-style framework से web app बनाता हूँ, और URL patterns का जवाब देने वाले functions लिखता हूँ। एक “screen” में साथ काम करने वाले एक या अधिक functions और संबंधित HTML template हो सकते हैं
      अगर application database connection, file locations, HTML header/footer जैसी चीज़ें set करती है, तो इसके अलावा screens के बीच coupling बहुत कम होता है। नई screen चाहिए तो किसी मौजूदा screen को copy करके बदल सकते हैं, या LLM से screen या endpoint बनवा सकते हैं; नतीजा खराब हो तो फिर से बना लेते हैं
      पिछले workplace में मैं Themis नाम का framework बना रहा था, जो ML training sets बनाता था, और इसमें microservices, React, Docker वगैरह इस्तेमाल हो रहे थे। असली requirement यह थी कि नए tasks लगातार जोड़ने हों और हर task के लिए simple लेकिन बहुत optimized screen जल्दी बनाई जाए। 20,000 बार judgement करना हो तो हर बार एक click भी कठिन है; चार-चार clicks करने पड़ें तो 80,000 हो जाते हैं और इंसान हार मान लेता है
      उस समय की architecture में JAXB application के API endpoints और monolithic React app के components लिखने पड़ते थे, और TypeScript·Docker·javac को 20 मिनट तक चलते हुए इंतज़ार करना पड़ता था। किस्मत अच्छी हो तो boot हो जाता, वरना शुरुआत से फिर करना पड़ता
      Themis की आलोचना लिखकर, नए tasks के तेज़ development को लक्ष्य बनाते हुए Nemesis design किया, लेकिन पुराने workplace में वह रास्ता नहीं चुना गया। फिर भी Nemesis और मैं उसके बाद से लाखों task instances process करते आ रहे हैं
    • laptop पर yin-yang sticker लगा है; yin पर DRY और yang पर YAGNI लिखा है
    • DRY इतना खतरनाक नहीं है
      इसका मतलब यह नहीं कि जो helper सिर्फ एक जगह इस्तेमाल हो रहा है उसे निकाल ही लेना चाहिए। अगर logic किसी एक function·class·file आदि में भरपूर है, लेकिन copy नहीं किया गया है, तो वह अब भी DRY है
      premature abstraction सच में मौजूद है। CS classes अक्सर ऐसा करने को सिखाती हैं, यह भी मददगार नहीं है। किसी नए developer को MySQL database दें तो वह सबसे पहले MySQL को abstract करने की कोशिश कर सकता है
    • grug कभी-कभी काम पूरा करने वाली मशीन बनाने में 100 घंटे लगा देता है, जबकि वही काम हाथ से 1 घंटे में खत्म हो सकता था। या फिर मशीन बनाने में 1 घंटा लगाता है और उस बेवकूफ मशीन को ठीक करने में 100 घंटे लगा देता है
      complex हो या simple, अगर result value add नहीं करता तो वह मायने नहीं रखता। पहले हटाने से ज़्यादा value add करने पर ध्यान दें, और complexity की चिंता उसके बाद करें
  • LLM का मेरा एक पसंदीदा इस्तेमाल यह है कि इस essay को डालकर, grug-brained developer persona में, अभी मैं जिस issue पर काम कर रहा हूँ उस पर comment करने को कहता हूँ। stress कम करने में अच्छा है

    • मैं अभी LLM से बहुत familiar नहीं हूँ, लेकिन यह बढ़िया लगता है। इस essay को “feed” करना कैसे होता है, यह जानना चाहूँगा
      क्या prompt को कुछ इस तरह शुरू करना होता है: “इस essay के Grug Brained Developer की तरह act करो”?
  • “जटिलता बहुत बुरी” सचमुच सही बात है
    सॉफ्टवेयर इंजीनियरिंग करते हुए मेरे सारे वर्षों में, यह विचार उन गिने-चुने सिद्धांतों में से एक रहा है जो हर स्थिति में लगातार सही साबित हुआ. कुछ समस्याएं स्वभाव से जटिल होती हैं, लेकिन तब भी सबसे सरल समाधान तक पहुंचने के लिए समय लगाना कहीं बेहतर होता है
    मेरा सबसे प्रभावी काम तब निकला जब मैंने पिछले approach पर सवाल उठाया और उसे साहस के साथ सरल बनाया. संभावित flexibility थोड़ी खो सकती है, लेकिन ज़्यादातर मामलों में जितनी flexibility की ज़रूरत मुझे लगती थी, उतनी असल में चाहिए ही नहीं थी
    उदाहरण के लिए, काफ़ी अच्छे और agentic तरीके से काम करने वाले LLM आने के बाद से, मैं टूटने में आसान और debug करने में कठिन, जरूरत से ज्यादा जटिल TypeScript types से बचता हूं; इसके बजाय spec जैसे code लिखता हूं और LLM से उसके आधार पर code statically generate करवाता हूं
    project की ESLint dependency version update के बाद लगातार टूटती रही, कई rules false positives से बचने लायक refined नहीं थे, और TypeScript व VSCode में उसे ठीक से maintain करना भी जटिल था. Biome.js पर जाने से यह ज्यादा सरल और पर्याप्त रूप से प्रभावी हुआ, लेकिन हाल में उसमें भी bugs आ रहे हैं. फिर भी मुझे समझ आया कि linting होना अच्छी बात है, लेकिन ऐसा कुछ नहीं जिसके रखरखाव में जरूरत से ज्यादा समय लगाया जाए; इसलिए मैंने उसे build toolchain से हटा दिया, और VSCode में भी उसे हमेशा on रखने की जरूरत नहीं रही. कभी-कभार Biome चलाकर code style और format ही check कर लेना काफी है
    project के लिए custom data migration tool बनाते समय, मुझे लगा कि forward migrations तो चाहिए, लेकिन reverse migrations लागू करने में लगने वाला समय और जटिलता worth it नहीं है. अगर data वाली database को rollback करना पड़े तो backup restore कर सकते हैं, और अगर data नहीं है या वह production DB नहीं है तो versioned initialization script से clean state में शुरू किया जा सकता है

    • पहले दो उदाहरण तो ऐसे लगते हैं जैसे complexity को किसी दूसरे tool का इस्तेमाल करके छिपा दिया गया हो
      तीसरा कैसे ज्यादा सरल है, यह भी मुझे ठीक से समझ नहीं आता. गणितीय दिमाग से bijective space आसानी से बनाया जा सकता है. दूसरे तरीकों से reverse migration की नकल करना शायद और कठिन हो सकता है. बेशक, यह details पर निर्भर करता है, इसलिए कोई general rule नहीं है
    • बहुत लोग complexity की बात करते हैं, लेकिन complexity का मतलब क्या है, यह लगभग कोई नहीं बताता
      समझदार Rich ने कहा था कि complect का मतलब चीजों को आपस में बांधना है, और मैं इससे सहमत हूं. Rich ने कहा कि complexity बुरी है, लेकिन उससे मैं सहमत नहीं हूं. चीजों को बांधना जरूरी है. अगर वे आपस में जुड़ी न हों तो समस्या हल नहीं की जा सकती
  • यकीन नहीं होता कि यह लेख 2022 का है
    मुझे लगता है मैं पूरे विश्वास से कहता कि मैंने इसे 10 साल पहले पढ़ा था और तब भी यह classic था

    • मेरा भी यही हाल है. शायद यह किसी और पुराने काम से प्रेरित रहा होगा
  • दुखद है, लेकिन सही है: “yes” सीखने के बाद, असफल होने पर दोष किसी और grug पर डालना सीखना ideal career advice है
    जब मैं पहली बार corporate दुनिया में आया था, तो मुझे लगा यह सच नहीं है, बस tech teams में communication की कमी है. मैं गलत था, और grug सही था