- Kubernetes का
pv_controller.goPV/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.goKubernetespersistentvolumepackage की PersistentVolumeController implementation file है- यह controller
PersistentVolumeClaimऔरPersistentVolumeकी state को मिलाता हैPersistentVolumechanges को monitor करने वाला cache controllerPersistentVolumeClaimchanges को monitor करने वाला cache controller- दोनों objects के change events के आधार पर PV/PVC state synchronization
- file के शीर्ष comments बार-बार चेतावनी देते हैं कि इस code को सरल न करें
- style का नाम
space shuttle styleहै - हर
ifstatement के साथ correspondingelseरखने का तरीका है - simple error checks को छोड़कर हर branch को explicit करने का उद्देश्य है
- जो behavior obvious लगता है उसे भी comments में लिखकर maintainers को binding complexity track करने में मदद दी जाती है
- style का नाम
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
- PVC side pointer:
- यह 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.ClaimRefmodify किया जाता है - फिर
PVC.Spec.VolumeNamemodify किया जाता है
- पहले
- इस 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 persistentVolumeOrderedIndexclaims cache.Store
- यह cache API server में store किए गए latest version और etcd events से आए versions, दोनों को reflect करता है
- एक binding लगभग चार events बना सकती है
volume.Specupdatevolume.Statusupdateclaim.Specupdateclaim.Statusupdate
- 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 हैं
claimQueuevolumeQueue
- हर 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
syncClaimclaim create, update या periodic sync होने पर call होने वाला मुख्य method है- यह method event type को distinguish नहीं करता
- पहले PVC पर सही migration annotation set करता है, और जरूरत हो तो API server में update करता है
- इसके बाद
AnnBindCompletedannotation की मौजूदगी के आधार पर branch करता है- annotation न हो तो
syncUnboundClaim - annotation हो तो
syncBoundClaim
- annotation न हो तो
- actual processing readability के लिए unbound claim और bound claim methods में split की गई है
checkVolumeSatisfyClaim: PV requirements की checking
checkVolumeSatisfyClaimverify करता है कि requested PV, PVC requirements satisfy करता है या नहीं- check conditions code में explicitly list की गई हैं
- PV में
DeletionTimestampहो तो error - PV capacity, PVC requested capacity से कम हो तो error
storageClassNameअलग हो तो errorVolumeAttributesClassfeature gate on हो तोVolumeAttributesClassNamematch होने की जांच- feature gate off हो और claim या volume में
VolumeAttributesClassNameहो तो error volumeModecompatible न हो तो error- access mode compatible न हो तो error
- PV में
- सभी conditions pass होने पर
nilreturn करता है
delayed binding PVC की event handling
emitEventForUnboundDelayBindingClaimdelayed 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.VolumeNameempty है, तो 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 भी नहीं, ऐसा
FailedBindingevent record करता है
- suitable PV हो तो
bindcall करके PV और PVC को bind करता है- success पर provision + binding work का metric record करता है और timestamp cache clear करता है
- save करते समय error आए तो बाद का
syncClaimbinding पूरी करेगा
specific PV मांगने वाले PVC की processing
- अगर
claim.Spec.VolumeNameempty नहीं है, तो user ने specific PV request किया है - requested PV cache में न हो तो PVC state को
Pendingमें update करता है और बाद में retry करता है - requested PV मौजूद हो और
volume.Spec.ClaimRefन हो, तो PV अभी claim नहीं किया गया हैcheckVolumeSatisfyClaimसे requirements check करता है- requirements satisfy न हों तो
VolumeMismatchevent record करता है और PVC कोPendingमें रखता है - requirements satisfy हों तो
bindcall करता है
- requested PV पहले से इसी PVC पर claimed हो तो binding पूरा करने के लिए
bindcall करता है - requested PV किसी दूसरे claim से जुड़ा हो तो यह processing होती है
- claim के पास controller द्वारा bound annotation न हो तो
FailedBindingevent record करता है औरPendingमें रखता है - controller द्वारा bound लगता हो लेकिन किसी दूसरे claim से जुड़ा हो तो “should never happen” state के रूप में error return करता है
- claim के पास controller द्वारा bound annotation न हो तो
syncBoundClaim: पहले से bind हुए PVC की processing
syncBoundClaimAnnBindCompletedannotation वाले PVC को process करता है- already bound claim में अगर
claim.Spec.VolumeNameempty हो तो 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 में चला गया मानकर फिर सेbindcall करता है - PV के
ClaimRef.UIDका claim के UID से match हो तो इसे normal binding state मानकरbindcall करता है- ज्यादातर cases में यह कोई काम न करने वाला call होता है
- PV किसी दूसरे claimant की ओर point करे तो claim phase को terminal state
Lostमें set करता है
syncVolume: PV synchronization का entry point
syncVolumevolume create, update या periodic sync पर call होने वाला मुख्य method है- event type distinguish नहीं करता
- पहले PV पर सही migration annotation और finalizer set करता है, और जरूरत हो तो API server में update करता है
volume.Spec.ClaimRefन हो तो इसे unused volume मानकर phase कोAvailableset करता हैClaimRefहो लेकिन UID empty हो तो इसे specific PVC के लिए reserved PV मानकर phase कोAvailableset करता है- वह 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में बदलता है औरreclaimVolumeexecute करता है- existing phase
Failedहो तो overwrite नहीं करता - reclaim policy
Retainहो तो non-existent claim को reference करने वाले PV का log छोड़ता है
- existing phase
PV और PVC connection mismatch होने पर
- claim मौजूद है लेकिन
claim.Spec.VolumeNameempty है, तो PVC के पास अभी PV name नहीं है volumeModematch न हो तो PV और PVC दोनों परVolumeMismatchevent record करता है औरsyncClaimskip करता है- mismatch न हो तो claim को
claimQueueमें add करता है ताकिsyncClaimजल्द call हो- यह approach provisioned volume की binding तेज करती है
- claim का
Spec.VolumeNamecurrent volume name जैसा हो तो इसे normal binding मानकर volume phase कोBoundमें update करता है - claim किसी दूसरे volume से bind हो तो situation के हिसाब से process करता है
- dynamically provisioned volume हो और reclaim policy
Deleteहो तोReleasedmark करता है औरreclaimVolumeexecute करता है - controller द्वारा bound volume हो तो
unbindVolumeसे clean up करता है - user-created pointer हो तो उसे वैसे ही छोड़ता है, लेकिन phase update करने और
ClaimRef.UIDclear करने के लिएunbindVolumecall करता है
- dynamically provisioned volume हो और reclaim policy
status update और event emission
updateClaimStatusPVC status को API server में store करता है- phase change
- volume न होने पर
AccessModes,Capacity,CurrentVolumeAttributesClassNameinitialize करना - 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 नहीं की जाती
VolumeAttributesClassfeature gate on हो तो pending से bound में बदलते समयCurrentVolumeAttributesClassNameset करता है- इसके बाद resizer या admin override को इसे handle करना चाहिए, और controller लगातार set करता रहे तो race condition की संभावना है
updateClaimStatusWithEventऔरupdateVolumePhaseWithEventकेवल actual status/phase बदलने पर event emit करते हैं
default StorageClass assignment
assignDefaultStorageClassclaim में storage class न होने पर default StorageClass खोजकर assign करता है- जिन claims में already storage class है उन्हें ignore करता है
- default class न हो तो update नहीं करता और
falsereturn करता है - default class हो तो
claim.Spec.StorageClassNameमें class name set करता है और API server में update करता है
file scope और explicit limits
- GitHub page पर दिखे file metadata के अनुसार
pv_controller.go2038 lines, 1864 LOC, 91 KB है - दिए गए body में file की शुरुआत से लेकर
bindVolumeToClaimfunction के start तक ही शामिल है, बाकी raw view link से आगे जाता है - इसलिए यह summary उपलब्ध code body में दिखे controller structure, design comments, major synchronization branches और status update logic तक सीमित है
1 टिप्पणियां
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
ifstatements देखकर मेरा विचार बदल गया। उस हिस्से में मैं निश्चित रूप से 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के लिए correspondingelsecomment है, भरोसेमंद रूप से सच नहीं लगता। जिनifका corresponding comment नहीं है, उनमें से काफी simpleif (err != nil) {checks या दूसरे early returns हैं, लेकिन उन्हें हटाने पर भी कुछifबिना corresponding comment के दिखते हैंहालांकि enterprise software के अनुभव में extra comments हमेशा बहुत ज़्यादा भी नहीं थे। codebase में
// end ifcomments महामारी की तरह फैले थे, लेकिन वास्तविक 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 होते
इतनी महंगी और धीमी कि modern proof assistants से software की correctness prove करना कहीं सस्ता और तेज़ होगा, और वास्तव में ज़्यादा सुरक्षित भी। seL4, CompCert जैसे projects दिखाते हैं कि यह कैसे किया जाना चाहिए
// 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] में वे कहते हैं
उन्होंने 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...
यह हिस्सा TypeScript code की exhaustiveness checking की याद दिलाता है। मैं हमेशा इसे इस्तेमाल करने की कोशिश करता हूँ
https://www.typescriptlang.org/docs/handbook/2/narrowing.htm...
नया
satisfies neverइस काम के लिए बहुत अच्छा है। पसंद के तौर परif elsechain इस्तेमाल करते हों, तब भी सुविधाजनक हैआपको
ts-patternभी पसंद आ सकता हैhttps://github.com/gvergnaud/ts-pattern
अगर सिर्फ़ उन मामलों को देखें जहाँ हर पूरी तरह मामूली न होने वाले
ifके साथ explicitelseलगाया गया है, तो सोचता हूँ कि अगर Kubernetes के authors नेif/elseblocks के बजाय 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
ifnesting को नुकसानदेह माना जाता है। ऐसे comments भी उपयोगी नहीं होते जो why नहीं, सिर्फ़ what बताते हैं, और वे actual code से आसानी से mismatch हो जाते हैं। अनावश्यकnilusage भी दिखता है। 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 भी अनजान लगने लगता है, इसलिए ये खुद लेखक के लिए भी उपयोगी हैं।
मैंने काफी समय तक इस तरह के "safe" तरीके से लिखा है, लेकिन early return के जरिए railway-style error handling की तुलना में इससे कहीं ज़्यादा bugs बने, और उन्हें ठीक करने में भी कहीं ज़्यादा समय लगा।
हर
ifblock में explicitelseलगाने से मौजूदा context याद रखने की complexity बहुत बढ़ जाती है। मुझे लगता है कि इस rule को बदलकर "हरifcondition block या तो early return करे, या उसका correspondingelseblock हो" करना उचित है।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
ifclauses से भरे code की तुलना में समझने और verify करने में आसान होता है। इस तरह का messy code आम तौर पर किसी missing abstraction का संकेत होता है।