2 पॉइंट द्वारा GN⁺ 2024-08-07 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Kubernetes का pv_controller.go PV/PVC binding को synchronize करने वाला controller है, और फाइल की शुरुआत से ही यह साफ कहता है कि “इसे सरल न करें और space shuttle style बनाए रखें”
  • इस style में हर if के साथ संबंधित else रखा जाता है और जो conditions obvious लगती हैं उन्हें भी comments में छोड़ा जाता है, ताकि review की गई branches और intent code के अंदर दिखे
  • design का केंद्र pvc.Spec.VolumeName और pv.Spec.ClaimRef से बनने वाले two-way pointers हैं, और यह transaction-less environment में race, deletion, user modification और concurrent binding को recoverable तरीके से handle करता है
  • controller PV/PVC changes की monitoring, internal cache, single worker queue, event recording, dynamic provisioning और CSI migration interface को जोड़कर binding state transitions manage करता है
  • लंबे branches और comments behavior की domain knowledge और failure recovery context को preserve करने के लिए हैं, इसलिए future changes को भी इसी style का पालन करना चाहिए

pv_controller.go की भूमिका और लिखने के सिद्धांत

  • pv_controller.go Kubernetes persistentvolume package की PersistentVolumeController implementation file है
  • यह controller PersistentVolumeClaim और PersistentVolume की state को मिलाता है
    • PersistentVolume changes को monitor करने वाला cache controller
    • PersistentVolumeClaim changes को monitor करने वाला cache controller
    • दोनों objects के change events के आधार पर PV/PVC state synchronization
  • file के शीर्ष comments बार-बार चेतावनी देते हैं कि इस code को सरल न करें
    • style का नाम space shuttle style है
    • हर if statement के साथ corresponding else रखने का तरीका है
    • simple error checks को छोड़कर हर branch को explicit करने का उद्देश्य है
    • जो behavior obvious लगता है उसे भी comments में लिखकर maintainers को binding complexity track करने में मदद दी जाती है

space shuttle style बनाए रखने की वजह

  • यह controller मूल रूप से तीन अलग-अलग controllers में बंटे काम को एक में मिलाने का परिणाम है
  • PV subsystem को simplify करने की प्रक्रिया में, सभी conditions को code में explicitly handle करने का तरीका जरूरी हो गया
  • इसके कारण code verbose लग सकता है और comments व branches ज्यादा दिख सकते हैं
  • यह विस्तार binding behavior की domain knowledge और context को code में बचाए रखने का mechanism है
  • इस file को बदलते समय space shuttle style को preserve करना चाहिए, और जरूरत पड़ने पर उसी तरीके से branches और comments जोड़ने चाहिए

मुख्य design: PV और PVC के two-way pointers

  • design के केंद्र में PV और PVC के बीच two-way pointers हैं
    • PVC side pointer: pvc.Spec.VolumeName
    • PV side pointer: pv.Spec.ClaimRef
  • यह two-way nature transaction-less system में handle करना मुश्किल है, लेकिन failure situations में भी सही behavior सुनिश्चित करने के लिए जरूरी है
  • अगर कोई rogue HA controller instance race condition बनाता है, तो indistinguishable multiple bindings बन सकती हैं और data loss की संभावना पैदा हो सकती है
  • controller को मूल रूप से active-passive high availability mode में काम करने के लिए design किया गया है
    • object transitions active-active HA में भी काम कर सकें, ऐसा design किया गया है
    • हालांकि अगर दो active controllers अक्सर collide करें, तो performance कम हो सकती है

binding तरीका और recovery conditions

  • controller two-way pre-bound objects को support करता है
    • किसी specific PV को चाहने वाला PVC
    • किसी specific PVC के लिए reserved PV
  • binding दो steps में आगे बढ़ती है
    • पहले PV.Spec.ClaimRef modify किया जाता है
    • फिर PVC.Spec.VolumeName modify किया जाता है
  • इस process के किसी भी समय PV या PVC को user या कोई दूसरा controller modify/delete कर सकता है
  • दो या अधिक controllers अलग-अलग volumes और claims को साथ-साथ bind करने की कोशिश भी कर सकते हैं
  • controller को ऐसी conflict situations से recover कर सकना चाहिए

controller struct के मुख्य components

  • PersistentVolumeController में PV/PVC synchronization के लिए जरूरी lister, informer sync functions, Kubernetes client, event recorder, volume plugin manager आदि होते हैं
  • last known PV/PVC versions internal cache में store किए जाते हैं
    • volumes persistentVolumeOrderedIndex
    • claims cache.Store
  • यह cache API server में store किए गए latest version और etcd events से आए versions, दोनों को reflect करता है
  • एक binding लगभग चार events बना सकती है
    • volume.Spec update
    • volume.Status update
    • claim.Spec update
    • claim.Status update
  • internal cache न हो तो informer के stale state रखने पर controller already completed binding को फिर से ठीक करने की कोशिश कर सकता है
  • इस समय API server पर दोबारा write करने की कोशिश करने से पहले से stored object के साथ version conflict हो सकता है

work queue और concurrency constraints

  • controller के पास claim और volume processing के लिए अलग-अलग workqueue हैं
    • claimQueue
    • volumeQueue
  • हर queue में ठीक एक ही worker thread होना चाहिए
  • खासकर syncClaim() reentrant नहीं है
  • अगर दो syncClaim() simultaneously execute हों, तो ये problems हो सकती हैं
    • दो अलग-अलग claims को एक ही volume से bind करना
    • एक claim को दो volumes से bind करना
  • controller API server के version errors और अपनी checks से ऐसी situations recover कर सकता है, लेकिन multi-worker approach overall speed घटा सकती है

syncClaim: PVC synchronization का entry point

  • syncClaim claim create, update या periodic sync होने पर call होने वाला मुख्य method है
  • यह method event type को distinguish नहीं करता
  • पहले PVC पर सही migration annotation set करता है, और जरूरत हो तो API server में update करता है
  • इसके बाद AnnBindCompleted annotation की मौजूदगी के आधार पर branch करता है
    • annotation न हो तो syncUnboundClaim
    • annotation हो तो syncBoundClaim
  • actual processing readability के लिए unbound claim और bound claim methods में split की गई है

checkVolumeSatisfyClaim: PV requirements की checking

  • checkVolumeSatisfyClaim verify करता है कि requested PV, PVC requirements satisfy करता है या नहीं
  • check conditions code में explicitly list की गई हैं
    • PV में DeletionTimestamp हो तो error
    • PV capacity, PVC requested capacity से कम हो तो error
    • storageClassName अलग हो तो error
    • VolumeAttributesClass feature gate on हो तो VolumeAttributesClassName match होने की जांच
    • feature gate off हो और claim या volume में VolumeAttributesClassName हो तो error
    • volumeMode compatible न हो तो error
    • access mode compatible न हो तो error
  • सभी conditions pass होने पर nil return करता है

delayed binding PVC की event handling

  • emitEventForUnboundDelayBindingClaim delayed binding mode के unbound claim को जानकारी देने वाला event बनाता है
  • default reason WaitForFirstConsumer है
  • default message यह है कि पहले consumer के create होने तक binding wait करेगी
  • अगर उस PVC को reference करने वाला कोई अभी तक unscheduled Pod हो, तो reason WaitForPodScheduled में बदल जाता है
    • अगर Pods कई हों, तो message में सभी Pod names शामिल होते हैं
    • volume scheduling में केवल एक Pod consider होता है, लेकिन कौन-सा Pod use होगा यह पता न होने से सभी Pods शामिल किए जाते हैं

syncUnboundClaim: अभी bind न हुए PVC की processing

  • अगर claim.Spec.VolumeName empty है, तो user ने कोई specific PV request नहीं किया है
  • इस case में controller claim के delayed binding mode को check करता है और findBestMatchForClaim से सबसे suitable PV खोजता है
  • suitable PV न हो तो processing इस order में होती है
    • default StorageClass assign कर सकता हो तो PVC update करके synchronization खत्म करता है
    • delayed binding हो और अभी provisioning state में न हो तो wait event create करता है
    • claim में StorageClass हो तो provisionClaim से dynamic provisioning try करता है
    • अन्यथा available PV भी नहीं और StorageClass भी नहीं, ऐसा FailedBinding event record करता है
  • suitable PV हो तो bind call करके PV और PVC को bind करता है
    • success पर provision + binding work का metric record करता है और timestamp cache clear करता है
    • save करते समय error आए तो बाद का syncClaim binding पूरी करेगा

specific PV मांगने वाले PVC की processing

  • अगर claim.Spec.VolumeName empty नहीं है, तो user ने specific PV request किया है
  • requested PV cache में न हो तो PVC state को Pending में update करता है और बाद में retry करता है
  • requested PV मौजूद हो और volume.Spec.ClaimRef न हो, तो PV अभी claim नहीं किया गया है
    • checkVolumeSatisfyClaim से requirements check करता है
    • requirements satisfy न हों तो VolumeMismatch event record करता है और PVC को Pending में रखता है
    • requirements satisfy हों तो bind call करता है
  • requested PV पहले से इसी PVC पर claimed हो तो binding पूरा करने के लिए bind call करता है
  • requested PV किसी दूसरे claim से जुड़ा हो तो यह processing होती है
    • claim के पास controller द्वारा bound annotation न हो तो FailedBinding event record करता है और Pending में रखता है
    • controller द्वारा bound लगता हो लेकिन किसी दूसरे claim से जुड़ा हो तो “should never happen” state के रूप में error return करता है

syncBoundClaim: पहले से bind हुए PVC की processing

  • syncBoundClaim AnnBindCompleted annotation वाले PVC को process करता है
  • already bound claim में अगर claim.Spec.VolumeName empty हो तो claim state को ClaimLost में बदलता है
    • event message यह बताता है कि bound claim ने PV reference खो दिया और volume का data lost हो गया
  • claim जिस PV की ओर point करता है, वह मौजूद न हो तो भी ClaimLost में बदलता है
    • event message यह बताता है कि bound claim ने PersistentVolume खो दिया और data lost हो गया
  • PV मौजूद हो लेकिन volume.Spec.ClaimRef न हो तो volume unbound state में चला गया मानकर फिर से bind call करता है
  • PV के ClaimRef.UID का claim के UID से match हो तो इसे normal binding state मानकर bind call करता है
    • ज्यादातर cases में यह कोई काम न करने वाला call होता है
  • PV किसी दूसरे claimant की ओर point करे तो claim phase को terminal state Lost में set करता है

syncVolume: PV synchronization का entry point

  • syncVolume volume create, update या periodic sync पर call होने वाला मुख्य method है
  • event type distinguish नहीं करता
  • पहले PV पर सही migration annotation और finalizer set करता है, और जरूरत हो तो API server में update करता है
  • volume.Spec.ClaimRef न हो तो इसे unused volume मानकर phase को Available set करता है
  • ClaimRef हो लेकिन UID empty हो तो इसे specific PVC के लिए reserved PV मानकर phase को Available set करता है
    • वह PVC अभी इस PV से bind नहीं हुआ है, और PVC sync इसे handle करेगा

claim न मिले तो PV की processing

  • PV किसी claim से bind हो तो controller ClaimRef के namespace/name से PVC खोजता है
  • cache में PVC न मिलने पर, specific conditions में extra checks करता है
    • informer cache में फिर से check
    • API server में फिर से check
  • external PV provisioner या external PV binder द्वारा बनाए गए PV में heavy load के दौरान PVC अभी local cache में sync नहीं हुआ हो सकता है
  • PVC को गलती से reclaim न करने के लिए double-check किया जाता है
  • claim नहीं है यह तय होने पर volume phase को Released में बदलता है और reclaimVolume execute करता है
    • existing phase Failed हो तो overwrite नहीं करता
    • reclaim policy Retain हो तो non-existent claim को reference करने वाले PV का log छोड़ता है

PV और PVC connection mismatch होने पर

  • claim मौजूद है लेकिन claim.Spec.VolumeName empty है, तो PVC के पास अभी PV name नहीं है
  • volumeMode match न हो तो PV और PVC दोनों पर VolumeMismatch event record करता है और syncClaim skip करता है
  • mismatch न हो तो claim को claimQueue में add करता है ताकि syncClaim जल्द call हो
    • यह approach provisioned volume की binding तेज करती है
  • claim का Spec.VolumeName current volume name जैसा हो तो इसे normal binding मानकर volume phase को Bound में update करता है
  • claim किसी दूसरे volume से bind हो तो situation के हिसाब से process करता है
    • dynamically provisioned volume हो और reclaim policy Delete हो तो Released mark करता है और reclaimVolume execute करता है
    • controller द्वारा bound volume हो तो unbindVolume से clean up करता है
    • user-created pointer हो तो उसे वैसे ही छोड़ता है, लेकिन phase update करने और ClaimRef.UID clear करने के लिए unbindVolume call करता है

status update और event emission

  • updateClaimStatus PVC status को API server में store करता है
    • phase change
    • volume न होने पर AccessModes, Capacity, CurrentVolumeAttributesClassName initialize करना
    • volume होने पर access mode, capacity, current volume attributes class name update करना
  • claim के Bound बनने के क्षण पर ही capacity update करने की condition है
    • PVC filesystem size और PV block device size का अंतर intentional हो सकता है, इसलिए already bound claim की capacity overwrite नहीं की जाती
  • VolumeAttributesClass feature gate on हो तो pending से bound में बदलते समय CurrentVolumeAttributesClassName set करता है
    • इसके बाद resizer या admin override को इसे handle करना चाहिए, और controller लगातार set करता रहे तो race condition की संभावना है
  • updateClaimStatusWithEvent और updateVolumePhaseWithEvent केवल actual status/phase बदलने पर event emit करते हैं

default StorageClass assignment

  • assignDefaultStorageClass claim में storage class न होने पर default StorageClass खोजकर assign करता है
  • जिन claims में already storage class है उन्हें ignore करता है
  • default class न हो तो update नहीं करता और false return करता है
  • default class हो तो claim.Spec.StorageClassName में class name set करता है और API server में update करता है

file scope और explicit limits

  • GitHub page पर दिखे file metadata के अनुसार pv_controller.go 2038 lines, 1864 LOC, 91 KB है
  • दिए गए body में file की शुरुआत से लेकर bindVolumeToClaim function के start तक ही शामिल है, बाकी raw view link से आगे जाता है
  • इसलिए यह summary उपलब्ध code body में दिखे controller structure, design comments, major synchronization branches और status update logic तक सीमित है

1 टिप्पणियां

 
GN⁺ 2024-08-07
Hacker News की टिप्पणियाँ
  • मुझे नहीं पता कि यह अजीब है या नहीं कि इस फ़ाइल का code सच में साधारण Go code जैसा लगता है। Go होने की वजह से यह verbose है, और गहरे abstraction पर निर्भर न होने के कारण लंबा दिखता है, लेकिन code अपने-आप में typical लगता है
    abstraction दोधारी तलवार है, इसलिए यह तरीका भी ठीक है; अगर प्रस्तावना न होती तो शायद लिखने की style पर दो बार सोचता भी नहीं। शायद यह अंतर इसलिए है कि मेरा अनुभव system software से ज़्यादा enterprise software में है। Kubernetes में लगातार योगदान देने वाले किसी व्यक्ति को ये comments अनावश्यक लग सकते हैं, लेकिन enterprise environment में, जहाँ भविष्य का कोई पाठक बिना context के code पढ़ेगा, इस complexity पर मैं तो शायद इससे भी ज़्यादा comments जोड़ता

    • पहले ऐसा code सामान्य लगता था, लेकिन पिछले करीब 10 साल में लगता है बहुत से लोग explicitness की तुलना में brevity को ज़्यादा महत्व देने लगे हैं
      खासकर ऐसे महत्वपूर्ण code में मैं explicitness को कहीं ज़्यादा पसंद करता हूँ। अपने career में कई बार ऐसा हुआ है कि कई conditions को मिला देने और business context व meaning समझाने वाले comments हटा देने वाले code की वजह से यह तय नहीं कर पाया कि मौजूदा behavior intended है या accidental। ऐसा तरीका change-resistant code नहीं, बल्कि change को रोकने वाला code बन जाता है, और कम से कम author के अलावा किसी और के लिए इसे बदलना कठिन बना देता है। अनावश्यक Chesterton's fence बनाना maintainability के खिलाफ है

    • यह comment शायद code को simplify करने की कोशिश के असफल होने के बाद, भविष्य के maintainers को वही कोशिश करने से पहले दोबारा सोचने की चेतावनी के रूप में जोड़ा गया था
      warning जोड़ने वाला commit "Add note about space-shuttle code style"[1] है, और उसके ठीक पहले वाला commit "Revert controller/volume: simplify sync logic in syncUnboundClaim"[2] था

      [1] https://github.com/kubernetes/kubernetes/commit/de4d193d45f6...

      [2] https://github.com/kubernetes/kubernetes/commit/8a1baa4d64ca...

    • मैं भी कुछ ऐसा ही सोच रहा था, फिर बड़े पैमाने पर nested if statements देखकर मेरा विचार बदल गया। उस हिस्से में मैं निश्चित रूप से early return branches बनाता
      यह ऐसा लगता है जैसे "इसे चलने लायक बनाओ, तेज़ बनाओ, सुंदर बनाओ" में केवल पहला चरण पूरा किया गया और "सुंदर बनाना" छोड़ दिया गया। tricky state interactions सुलझाते समय मैंने भी कभी-कभी ऐसा बदसूरत और comment-heavy code लिखा है, लेकिन आमतौर पर review से पहले थोड़ा साफ कर देता हूँ। शायद बेहतर हो कि बस file के सबसे ऊपर "इस code को simplify करने की कोशिश न करें" वाला बड़ा banner लगा दिया जाए। फिर भी, यह निश्चित रूप से बहुत बुरा नहीं है

    • यह अजीब हो सकता है, लेकिन आप अकेले नहीं हैं। मुझे भी यह code पूरी तरह सामान्य लगता है। जिन components को मैं system reliability के लिए महत्वपूर्ण मानता हूँ, उनमें मैंने इसी तरह का code और comments लिखे हैं
      "comment-less code" trend से मैं कभी सहमत नहीं रहा, और महीनों या सालों बाद लौटने पर मेरे लिखे comments मेरे भविष्य के लिए बहुत बार बेहद कीमती साबित हुए। इस complexity के component में जड़ी logic को solid comments के बिना फिर से reconstruct करना कल्पना करना भी मुश्किल है

    • खासकर यह दावा कि हर if के लिए corresponding else comment है, भरोसेमंद रूप से सच नहीं लगता। जिन if का corresponding comment नहीं है, उनमें से काफी simple if (err != nil) { checks या दूसरे early returns हैं, लेकिन उन्हें हटाने पर भी कुछ if बिना corresponding comment के दिखते हैं
      हालांकि enterprise software के अनुभव में extra comments हमेशा बहुत ज़्यादा भी नहीं थे। codebase में // end if comments महामारी की तरह फैले थे, लेकिन वास्तविक explanatory comments दुर्लभ थे

  • Space Shuttle software quality पर लेख: https://archive.is/HX7n4
    अंश के अनुसार, इस software की चौंकाने वाली बात यह नहीं है कि यह कितना काम करता है, बल्कि यह है कि यह कितना अच्छा काम करता है। यह कभी crash नहीं करता, reboot की जरूरत नहीं होती, bugs नहीं हैं, और कहा गया है कि यह मानव-निर्मित स्तर पर perfection के करीब है। अंतिम तीन versions में हर एक 420,000 lines का था और हर एक में सिर्फ एक error था; अंतिम 11 versions में कुल errors 17 थे। कहा गया कि समान complexity वाले किसी commercial program में करीब 5,000 errors होते

    • मुझे उत्सुकता है कि "अंतिम तीन versions में हर एक 420,000 lines का था और हर एक में एक error था" का ठीक मतलब क्या है। अगर तीनों versions में सचमुच ठीक-ठीक एक bug था, तो क्या यह कहने का अजीब तरीका नहीं है कि पहले दो fixes काम नहीं किए या उन्होंने नया bug डाल दिया?
    • NASA approach और SpaceX approach कैसे अलग हैं, इसकी तुलना करना दिलचस्प होगा। SpaceX ने भी crewed missions किए हैं, इसलिए requirements काफी समान लगती हैं
    • 5000 / 17 ≈ 295 है। क्या यह मानना fair है कि समान complexity वाले commercial program में person-hours 295 गुना कम लगे?
    • Space Shuttle development methodology की समस्या यह है कि यह बेहद महंगी और धीमी है, फिर भी 100% bug-free नहीं है
      इतनी महंगी और धीमी कि modern proof assistants से software की correctness prove करना कहीं सस्ता और तेज़ होगा, और वास्तव में ज़्यादा सुरक्षित भी। seL4, CompCert जैसे projects दिखाते हैं कि यह कैसे किया जाना चाहिए
    • यह मेरे पसंदीदा लेखों में से एक है। हैरानी है कि 1996 का internet लेख अभी भी accessible है
  • // KEEP THE SPACE SHUTTLE FLYING. का intent मैं समझता हूँ, लेकिन comment में ऐसे system का reference देना थोड़ा मज़ेदार है जो खराब safety record के कारण अब operate नहीं होता
    करीब 10 साल बाद भी क्या लोग Space Shuttle को अच्छी तरह याद करेंगे?

    • Space Shuttle की safety problems आम तौर पर hardware problems थीं, software problems नहीं
      1986 Challenger accident report में Richard Feynman के appendix "Appendix F - Personal Observations on Reliability of Shuttle" [0] में वे कहते हैं

      संक्षेप में, computer software checking system और attitude सर्वोच्च quality के हैं। Solid Rocket Booster या Space Shuttle Main Engine safety systems में दिखने वाली, standards को कम करते हुए धीरे-धीरे खुद को धोखा देने की प्रक्रिया यहाँ नहीं दिखती।

      उन्होंने avionics software quality को विशेष रूप से इस उदाहरण के तौर पर रेखांकित किया कि Shuttle जैसी बड़ी, complex government projects भी सही ढंग से engineer की जा सकती हैं और अपने-आप में low-quality या risk के लिए नियत नहीं होतीं

0: https://www.nasa.gov/history/rogersrep/v2appf.htm

  • 100 से भी ज़्यादा सफल मिशनों में लोगों और उपकरणों को अंतरिक्ष तक ले गया और वापस घर लाया। आज भी इसे अच्छे नज़रिए से देखता हूँ और आगे भी शायद ऐसा ही रहेगा। मानव प्रगति और कुल असर के लिहाज़ से यह सफल था

  • Shuttle को खत्म करने की वजह खराब safety record नहीं, बल्कि लागत और भविष्य में safety घटने का अनुमान था
    Shuttle की दो दुर्घटनाओं में NASA की दूसरी आपदाओं से ज़्यादा astronauts मारे गए, लेकिन जो असल में किया जा रहा था उसकी कठिनाई को देखते हुए safety record सचमुच हैरान करने वाला था। कोड काफ़ी अच्छा दिखता है

  • Space Shuttle की स्थिति सिर्फ़ खराब safety से ज़्यादा जटिल है। मिशन के आधार पर देखें तो इसका record दूसरे launch vehicles से बेहतर ही था। Shuttle के 135 में 2 घातक मिशन थे, Soviet दौर के Soyuz के 66 में 2, और SpaceShipTwo का record डरावना खराब है—सिर्फ़ 12 flights में 1 घातक मिशन
    हालांकि Space Shuttle की crew capacity ज़्यादातर मिशनों की ज़रूरत से कहीं बड़ी थी। Apollo या Soyuz के 3 लोगों के विपरीत यह 8 लोगों तक ले जा सकता था, और यह देखते हुए कि Soviet/Roscosmos, ESA, CNSA के ज़्यादातर मिशन पूरी तरह uncrewed autonomous mission थे, जोखिम में पड़ने वाला crew था ही नहीं। शायद यह तुलना Kubernetes पर ज़्यादा फिट बैठती है। अत्यधिक engineered, शक्तिशाली और बहुउद्देशीय, लेकिन बहुत सावधानी मांगने वाला system—और शायद ज़रूरत से थोड़ा ज़्यादा इस्तेमाल किया जाने वाला

  • सबसे आम पैमाने, यानी passenger-mile के आधार पर देखें तो Space Shuttle अब तक बनाए और उड़ाए गए सबसे सुरक्षित वाहनों में गिना जाता है
    एक ऐसे व्यक्ति के तौर पर जिसका बचपन ठीक 1980s में था, ईमानदारी से पूछूँ तो समझ नहीं आता कि इसे अच्छी यादों में कैसे न रखा जाए। क्या आप इतने छोटे हैं कि इस program और इसके सभी mission·achievements को सिर्फ़ retrospective रूप में ही देखते हैं, और आपका नज़रिया मौजूदा private space contractors-केंद्रित दौर के माहौल से ही प्रभावित है?

  • Richard Hipp द्वारा SQLite कोड को aviation standards के अनुरूप बनाने की कहानी भी काफ़ी दिलचस्प है: https://corecursive.com/066-sqlite-with-richard-hipp/#testin...

    DO-178B. safety-critical aviation products के लिए quality standard है... tests को यह सुनिश्चित करना होता है कि resulting binary code का हर branch operation कम से कम एक बार execute हो और कम से कम एक बार pass हो... इसमें एक साल तक हफ़्ते में 60 घंटे लगे... इससे बहुत बड़ा फर्क पड़ा। उसके बाद 8~9 साल तक लगभग कोई bug नहीं था

  • यह हिस्सा TypeScript code की exhaustiveness checking की याद दिलाता है। मैं हमेशा इसे इस्तेमाल करने की कोशिश करता हूँ
    https://www.typescriptlang.org/docs/handbook/2/narrowing.htm...

    • नया satisfies never इस काम के लिए बहुत अच्छा है। पसंद के तौर पर if else chain इस्तेमाल करते हों, तब भी सुविधाजनक है

    • आपको ts-pattern भी पसंद आ सकता है

      https://github.com/gvergnaud/ts-pattern

  • अगर सिर्फ़ उन मामलों को देखें जहाँ हर पूरी तरह मामूली न होने वाले if के साथ explicit else लगाया गया है, तो सोचता हूँ कि अगर Kubernetes के authors ने if/else blocks के बजाय structural pattern matching को केंद्र में रखकर design किया होता, तो यह code कितना सरल हो गया होता
    structural pattern matching support करने वाली कई mainstream languages में compile time पर यह जाँचने के tools होते हैं कि matching exhaustive है या नहीं, और सिर्फ़ वही code की information density बढ़ाते हुए idiomatic समाधान बन सकता है

  • 2018 की चर्चा: https://news.ycombinator.com/item?id=18772873

  • मैंने code को बस सरसरी तौर पर देखा है, लेकिन सच कहूँ तो यह इतना खराब नहीं लगता। कुछ चीज़ें मैं अलग तरह से करता, लेकिन इससे कहीं ज़्यादा खराब code बहुत देखा है
    कम-से-कम यह code एक नियम का पालन करता है, हर चीज़ सोच-समझकर लिखी गई लगती है, और इस chaos में भी कोई तरीका है ऐसा impression देता है। बार-बार देखे गए style-mixing, lazy coding, illogical structure जैसे typical गड़बड़झाले की बजाय मैं कभी भी ऐसे code को चुनूँगा

  • समझ नहीं आता कि "safety" practices नए सिरे से बनाते समय documented software engineering best practices को क्यों नज़रअंदाज़ किया जाता है
    2,000-line modules और 200-line methods, 3~4-level if nesting को नुकसानदेह माना जाता है। ऐसे comments भी उपयोगी नहीं होते जो why नहीं, सिर्फ़ what बताते हैं, और वे actual code से आसानी से mismatch हो जाते हैं। अनावश्यक nil usage भी दिखता है। coupling या single responsibility principle जैसी गहरी समस्याओं में गए बिना भी सतह पर ये बातें दिखती हैं

    • अगर आपको लगता है कि ये चीज़ें नुकसानदेह हैं, तो "John Carmack on Inlined Code" पढ़ने की सलाह दूँगा
      http://number-none.com/blow/john_carmack_on_inlined_code.htm...

      "Armadillo rocket का flight control code कुछ हज़ार lines का ही था, इसलिए मैंने main tic function लिया और सभी subroutines को inline करना शुरू किया। मैं यह नहीं कह सकता कि मुझे कोई hidden bug मिला जो actual crash करा सकता था, लेकिन कुछ variables मिले जो कई बार set हो रहे थे और कुछ control flows मिले जो थोड़े संदिग्ध लग रहे थे, और final code छोटा और साफ़ हो गया।"

      अगर Carmack को इस approach में value दिखी, तो लगता है इसे जल्दबाज़ी में dismiss नहीं करना चाहिए। बाद की comment भी देखने लायक है

      "यह लेख लिखने के बाद के कुछ वर्षों में, मैं C/C++ में भी reasonable scope के भीतर pure functional programming को लेकर कहीं ज़्यादा positive हो गया हूँ... जब चीज़ें संभालना मुश्किल होने लगे, तो block को pure function में अलग करने का तरीका खोजो"

    • कभी-कभी मामला "कोई दूसरा तरीका नहीं है(TM)" वाला होता है
      arbitrary line-count limit अक्सर अनावश्यक fragmentation पैदा कर देती है। include, license, glue code, comments जोड़ दें तो यह पहुँच से बाहर spaghetti बन जाता है। high-performance code में methods को 200 lines तक रखने की कोशिश करें, तो performance Icarus की उड़ान की तरह गिर सकती है

कोड की comments पढ़ने पर दिखता है कि इस कोड को single module में सरल बनाया गया है, और इसे accessible तथा उससे भी ज़्यादा sustainable बनाने के लिए इसमें बहुत सारा know-how डाला गया है। जिसे भाषा या logic नहीं पता, उसके लिए comments बहुत उपयोगी होते हैं, क्योंकि वे बताते हैं कि code क्या कर रहा है। 6 महीने बाद अपना ही code भी अनजान लगने लगता है, इसलिए ये खुद लेखक के लिए भी उपयोगी हैं।

Comments code और codebase का हिस्सा होते हैं। अगर आसपास का code ठीक करते समय comments को साथ में update नहीं किया, तो आप code में documentation bug डाल रहे हैं। Compiler इसे process नहीं करता, इसका मतलब यह नहीं कि यह functional हिस्सा नहीं है। मूल रूप से comments ज्ञान हैं, code में embedded research notes हैं, और लिखे हुए code को maintain करते समय चलने वाले code से भी ज़्यादा मूल्यवान हो सकते हैं।

Best practices कोई कानून या सख्त rules नहीं, बल्कि guidelines हैं। उन्हें तब लागू करना चाहिए जब वे codebase के लिए सही बैठें; blindly follow करके problematic codebase नहीं बनाना चाहिए। कभी-कभी rules को bend करना और खुद बनाना पड़ता है, और अगर आपको पता है कि आप क्या कर रहे हैं तो यह पूरी तरह स्वीकार्य है।
  • मैंने काफी समय तक इस तरह के "safe" तरीके से लिखा है, लेकिन early return के जरिए railway-style error handling की तुलना में इससे कहीं ज़्यादा bugs बने, और उन्हें ठीक करने में भी कहीं ज़्यादा समय लगा।
    हर if block में explicit else लगाने से मौजूदा context याद रखने की complexity बहुत बढ़ जाती है। मुझे लगता है कि इस rule को बदलकर "हर if condition block या तो early return करे, या उसका corresponding else block हो" करना उचित है। if (cond) { special handling } pattern निश्चित रूप से early return से कहीं ज़्यादा जोखिम भरा और reason करने में कठिन बना देता है।

  • एक ही आधिकारिक best practices का set जैसी कोई चीज़ नहीं होती।
    Function की length या file में code lines की संख्या अपने आप में न तो मूल रूप से हानिकारक है, न लाभकारी। हर language का code को organize करने के बारे में अपना नजरिया हो सकता है, लेकिन उनमें से कोई भी "best practice" होने का दावा नहीं कर सकता। Go ऐसी language नहीं है जो code को बहुत सारे छोटे files में तोड़ने की शैली को prefer करती हो।

  • 200-line method अपने आप में गलत नहीं है। अगर अंदर का code linear है और abstraction का वही level बनाए रखता है, तो यह सबसे अच्छा विकल्प हो सकता है।
    विकल्प के तौर पर 5-line के 40 methods बनाना और खराब हो सकता है। पूरे को समझने के लिए इधर-उधर jump करना पड़ेगा, और call order भी बिगड़ सकता है। चुनने योग्य permutations तो 40! हैं।

  • ऐसा code declarative, rule-based, table-driven system में ले जाने के लिए ideal candidate जैसा दिखता है।
    वह तरीका ad-hoc imperative if clauses से भरे code की तुलना में समझने और verify करने में आसान होता है। इस तरह का messy code आम तौर पर किसी missing abstraction का संकेत होता है।

    • Go की ideology मूल रूप से कुछ ऐसी है कि जो code आप C में लिखते, उसे काफी सीधे-सीधे port करने जैसा सब कुछ लिखते जाएं, और किसी चीज़ को abstract करने की कोशिश न करें।