Skip to content

Latest commit

 

History

History
181 lines (126 loc) · 25.8 KB

File metadata and controls

181 lines (126 loc) · 25.8 KB

संज्ञानात्मक भार नै महत्त्वपूर्ण छ

प्रोम्प्ट | ब्लग संस्करण | चिनियाँ | कोरियाली | टर्किश | जापानी | भियतनामी

यो एक जीवित दस्तावेज हो, अन्तिम अपडेट: अक्टोबर २०२५। तपाईंको योगदान स्वागत छ!

परिचय

त्यहाँ धेरै buzzwords र उत्तम अभ्यासहरू छन्, तर तिनीहरूमध्ये धेरै असफल भएका छन्। तिनीहरू असफल भए किनभने तिनीहरू कल्पना गरिएका थिए, वास्तविक होइनन्। यी विचारहरू सौन्दर्य र व्यक्तिपरक निर्णयहरूमा आधारित थिए। हामीलाई केही अधिक आधारभूत चाहिन्छ, केही जुन गलत हुन सक्दैन।

कहिलेकाहीं हामी कोड मार्फत जाँदा भ्रम महसुस गर्छौं। भ्रमले समय र पैसा खर्च गर्छ। भ्रम उच्च संज्ञानात्मक भार को कारणले गर्दा हुन्छ। यो केही फ्यान्सी अमूर्त अवधारणा होइन, बरु एक आधारभूत मानव बाधा हो। यो कल्पना गरिएको होइन, यो त्यहाँ छ र हामी यसलाई महसुस गर्न सक्छौं।

हामी कोड लेख्नु भन्दा धेरै समय पढ्न र बुझ्न खर्च गर्छौं, त्यसैले हामीले निरन्तर आफैलाई सोध्नुपर्छ कि हामी हाम्रो कोडमा अत्यधिक संज्ञानात्मक भार इम्बेड गर्दैछौं कि छैनौं।

संज्ञानात्मक भार

संज्ञानात्मक भार भनेको कुनै कार्य पूरा गर्न विकासकर्ताले कति सोच्नु पर्छ।

कोड पढ्दा, तपाईंले चरहरूको मानहरू, नियन्त्रण प्रवाह तर्क र कल अनुक्रमहरू जस्ता चीजहरू आफ्नो टाउकोमा राख्नुहुन्छ। औसत व्यक्तिले कार्य सम्झनामा लगभग चार यस्ता खण्डहरू राख्न सक्छ। एक पटक संज्ञानात्मक भार यो सीमामा पुग्छ, चीजहरू बुझ्न धेरै गाह्रो हुन्छ।

मानौं हामीलाई पूर्ण रूपमा अपरिचित परियोजनामा केही समाधानहरू गर्न भनिएको छ। हामीलाई भनिएको थियो कि वास्तवमा स्मार्ट विकासकर्ताले यसमा योगदान गरेको थियो। धेरै राम्रो architectures, फ्यान्सी पुस्तकालयहरू र ट्रेंडी प्रविधिहरू प्रयोग गरियो। अर्को शब्दमा, लेखकले हाम्रो लागि उच्च संज्ञानात्मक भार सिर्जना गरेको थियो।

Cognitive load

हामीले हाम्रो परियोजनाहरूमा संज्ञानात्मक भार सकेसम्म कम गर्नुपर्छ।

संज्ञानात्मक भार र अवरोधहरू

हामी "संज्ञानात्मक भार" लाई अनौपचारिक अर्थमा प्रयोग गर्न जाँदैछौं; कहिलेकाहीं यो संज्ञानात्मक भार को विशिष्ट वैज्ञानिक अवधारणा संग मेल खान्छ, तर हामी यो कहाँ मेल खान्छ र कहाँ खाँदैन भन्ने बारेमा पर्याप्त जान्दैनौं।

संज्ञानात्मक भारका प्रकारहरू

आन्तरिक - कार्यको निहित कठिनाइले गर्दा हुन्छ। यसलाई कम गर्न सकिँदैन, यो सफ्टवेयर विकासको मुटुमा छ।

बाह्य - जानकारी प्रस्तुत गरिएको तरिकाले सिर्जना गरिएको। कार्यसँग सीधा सम्बन्धित नभएका कारकहरूले गर्दा हुन्छ, जस्तै स्मार्ट लेखकको विचित्रता। धेरै कम गर्न सकिन्छ। हामी यस प्रकारको संज्ञानात्मक भारमा ध्यान केन्द्रित गर्नेछौं।

Intrinsic vs Extraneous

आउनुहोस् सीधा बाह्य संज्ञानात्मक भारको ठोस व्यावहारिक उदाहरणहरूमा जाऔं।


हामी संज्ञानात्मक भारको स्तरलाई निम्नानुसार सन्दर्भ गर्नेछौं: 🧠: ताजा कार्य सम्झना, शून्य संज्ञानात्मक भार 🧠++: हाम्रो कार्य सम्झनामा दुई तथ्यहरू, संज्ञानात्मक भार बढ्यो 🤯: संज्ञानात्मक ओभरलोड, ४ भन्दा बढी तथ्यहरू

हाम्रो मस्तिष्क धेरै जटिल र अन्वेषण नगरिएको छ, तर हामी यो सरल मोडेलसँग जान सक्छौं।

जटिल सर्तहरू

if val > someConstant // 🧠+
    && (condition2 || condition3) // 🧠+++, अघिल्लो सर्त सत्य हुनुपर्छ, c2 वा c3 मध्ये एक सत्य हुनुपर्छ
    && (condition4 && !condition5) { // 🤯, यो बिन्दुमा हामी गडबड छौं
    ...
}

अर्थपूर्ण नामहरू भएका मध्यवर्ती चरहरू परिचय गर्नुहोस्:

isValid = val > someConstant
isAllowed = condition2 || condition3
isSecure = condition4 && !condition5 
// 🧠, हामीले सर्तहरू सम्झनु पर्दैन, वर्णनात्मक चरहरू छन्
if isValid && isAllowed && isSecure {
    ...
}

नेस्टेड ifs

if isValid { // 🧠+, ठीक छ नेस्टेड कोड मान्य इनपुटमा मात्र लागू हुन्छ
    if isSecure { // 🧠++, हामी मान्य र सुरक्षित इनपुटको लागि मात्र काम गर्छौं
        stuff // 🧠+++
    }
} 

यसलाई प्रारम्भिक रिटर्न्ससँग तुलना गर्नुहोस्:

if !isValid
    return
 
if !isSecure
    return

// 🧠, हामी पहिलेको रिटर्न्सको बारेमा वास्तवमा वास्ता गर्दैनौं, यदि हामी यहाँ छौं भने सबै राम्रो छ

stuff // 🧠+

हामी केवल खुसी मार्गमा ध्यान केन्द्रित गर्न सक्छौं, यसरी हाम्रो कार्य सम्झनालाई सबै प्रकारका पूर्व-शर्तहरूबाट मुक्त गर्दै।

इन्हेरिटेन्स दुःस्वप्न

हामीलाई हाम्रो प्रशासक प्रयोगकर्ताहरूको लागि केही चीजहरू परिवर्तन गर्न भनिएको छ: 🧠

AdminController extends UserController extends GuestController extends BaseController

ओह, कार्यक्षमताको केही भाग BaseController मा छ, हेरौं: 🧠+ आधारभूत भूमिका मेकानिक्स GuestController मा प्रस्तुत गरियो: 🧠++ चीजहरू आंशिक रूपमा UserController मा परिवर्तन गरियो: 🧠+++ अन्तमा हामी यहाँ छौं, AdminController, कोड गरौं! 🧠++++

ओह, पर्खनुहोस्, त्यहाँ SuperuserController छ जसले AdminController लाई विस्तार गर्छ। AdminController परिमार्जन गरेर हामी इन्हेरिट गरिएको क्लासमा चीजहरू तोड्न सक्छौं, त्यसैले पहिले SuperuserController मा गोता लगाऔं: 🤯

इन्हेरिटेन्स भन्दा कम्पोजिसनलाई प्राथमिकता दिनुहोस्। हामी विस्तृत विवरणमा जाँदैनौं - त्यहाँ धेरै सामग्री छ।

धेरै साना मेथडहरू, क्लासहरू वा मोड्युलहरू

मेथड, क्लास र मोड्युल यो सन्दर्भमा आदान-प्रदान गर्न मिल्छन्।

"मेथडहरू १५ लाइन कोड भन्दा छोटो हुनुपर्छ" वा "क्लासहरू सानो हुनुपर्छ" जस्ता मन्त्रहरू केही हदसम्म गलत साबित भए।

गहिरो मोड्युल - सरल इन्टरफेस, जटिल कार्यक्षमता उथली मोड्युल - इन्टरफेस यसले प्रदान गर्ने सानो कार्यक्षमताको तुलनामा अपेक्षाकृत जटिल छ

Deep module

धेरै उथला मोड्युलहरू हुनुले परियोजना बुझ्न गाह्रो बनाउन सक्छ। हामीले प्रत्येक मोड्युलको जिम्मेवारीहरू मात्र होइन, तर तिनीहरूको सबै अन्तरक्रियाहरू पनि दिमागमा राख्नुपर्छ। उथले मोड्युलको उद्देश्य बुझ्न, हामीले पहिले सबै सम्बन्धित मोड्युलहरूको कार्यक्षमता हेर्नुपर्छ। यस्ता उथले घटकहरू बीच उफ्रनु मानसिक रूपमा थकाइ लाग्दो छ, रैखिक सोच हामी मानिसहरूको लागि अधिक प्राकृतिक छ।

जानकारी लुकाउनु सर्वोपरि छ, र हामी उथले मोड्युलहरूमा धेरै जटिलता लुकाउँदैनौं।

मसँग दुई पेट परियोजनाहरू छन्, दुवै लगभग ५K लाइन कोड छन्। पहिलो एकमा ८० उथला क्लासहरू छन्, जबकि दोस्रोमा केवल ७ गहिरो क्लासहरू छन्। मैले यी मध्ये कुनै पनि परियोजना डेढ वर्षसम्म कायम राखेको छैन।

एक पटक म फर्किएँ, मैले महसूस गरें कि पहिलो परियोजनामा ती ८० क्लासहरू बीचको सबै अन्तरक्रियाहरू खोल्न अत्यन्तै गाह्रो थियो। म कोडिङ सुरु गर्नु अघि मैले ठूलो मात्रामा संज्ञानात्मक भार पुनर्निर्माण गर्नुपर्ने थियो। अर्कोतर्फ, म दोस्रो परियोजना छिटो बुझ्न सकें, किनभने यसमा केवल केही गहिरो क्लासहरू सरल इन्टरफेससहित थिए।

उत्तम घटकहरू ती हुन् जसले शक्तिशाली कार्यक्षमता प्रदान गर्छन् तर सरल इन्टरफेस छ।

John Ousterhout, A Philosophy of Software Design

Unix I/O को इन्टरफेस धेरै सरल छ। यसमा केवल पाँच आधारभूत कलहरू छन्:

open(path, flags, permissions)
read(fd, buffer, count)
write(fd, buffer, count)
lseek(fd, offset, referencePosition)
close(fd)

यस इन्टरफेसको आधुनिक कार्यान्वयनमा लाखौं लाइन कोड छ। धेरै जटिलता हुड मुनि लुकेको छ। तर यसको सरल इन्टरफेसको कारण प्रयोग गर्न सजिलो छ।

यो गहिरो मोड्युल उदाहरण John Ousterhout द्वारा A Philosophy of Software Design पुस्तकबाट लिइएको हो। यो पुस्तकले सफ्टवेयर विकासमा जटिलताको सार मात्र होइन, तर Parnas को प्रभावशाली पेपर On the Criteria To Be Used in Decomposing Systems into Modules को उत्कृष्ट व्याख्या पनि गर्छ। दुवै आवश्यक पठनहरू हुन्। अन्य सम्बन्धित पढाइहरू: A Philosophy of Software Design vs Clean Code, It's probably time to stop recommending Clean Code, Small Functions considered Harmful

महत्त्वपूर्ण चीजहरू ठूला हुनुपर्छ, उदाहरणहरू
Clean vs Dirty
यदि तपाईंले आफ्नो महत्त्वपूर्ण "क्रक्स" फङ्क्सनहरूलाई ठूलो ("फोहोर") हुन दिनुभयो भने, तिनीहरूलाई फङ्क्सनको समुद्रबाट छान्न सजिलो हुन्छ, तिनीहरू स्पष्ट रूपमा महत्त्वपूर्ण छन्: केवल तिनीहरूलाई हेर्नुहोस्, तिनीहरू ठूला छन्!
यो तस्बिर Carson Gross द्वारा Codin' Dirty लेखबाट लिइएको हो। तपाईंले त्यहाँ गहिरो फङ्क्सनहरूको वास्तविक विश्व उदाहरणहरू फेला पार्नुहुनेछ।

P.S. यदि तपाईंलाई लाग्छ कि हामी धेरै जिम्मेवारीहरू भएका फुलाएको God objects को लागि समर्थन गर्दैछौं, तपाईंले यसलाई गलत बुझ्नुभयो।

एउटा कुराको लागि जिम्मेवार

प्रायः, हामी धेरै उथला मोड्युलहरू सिर्जना गर्छौं, केही अस्पष्ट "एक मोड्युल एक, र केवल एक, कुराको लागि जिम्मेवार हुनुपर्छ" सिद्धान्त पछ्याउँदै। यो धमिलो एक कुरा के हो? एक वस्तु तत्काल बनाउनु एक कुरा हो, होइन? त्यसोभए MetricsProviderFactoryFactory ठीक जस्तो देखिन्छ। यस्ता क्लासहरूको नाम र इन्टरफेसहरू तिनीहरूको सम्पूर्ण कार्यान्वयन भन्दा मानसिक रूपमा बढी कर लाग्दछ, त्यो कस्तो प्रकारको अमूर्तता हो? केही गलत भयो।

हामी हाम्रा प्रयोगकर्ताहरू र सरोकारवालाहरूलाई सन्तुष्ट पार्न हाम्रो प्रणालीमा परिवर्तनहरू गर्छौं। हामी तिनीहरूप्रति जिम्मेवार छौं।

एक मोड्युल एक, र केवल एक, प्रयोगकर्ता वा सरोकारवालाप्रति जिम्मेवार हुनुपर्छ।

यो Single Responsibility Principle को बारेमा हो। सरल शब्दमा, यदि हामीले एक ठाउँमा बग परिचय गर्छौं, र त्यसपछि दुई फरक व्यापार व्यक्तिहरू गुनासो गर्न आउँछन्, हामीले सिद्धान्तको उल्लंघन गरेका छौं। यसको हाम्रो मोड्युलमा गर्ने चीजहरूको संख्यासँग कुनै सरोकार छैन।

तर अहिले पनि, यो नियमले राम्रो भन्दा बढी हानि गर्न सक्छ। यो सिद्धान्तलाई व्यक्तिहरू जति छन् त्यति फरक तरिकाले बुझ्न सकिन्छ। राम्रो दृष्टिकोण यो सबैले कति संज्ञानात्मक भार सिर्जना गर्छ हेर्नु हुनेछ। एक ठाउँमा परिवर्तनले विभिन्न व्यापार धाराहरूमा प्रतिक्रियाहरूको श्रृंखला ट्रिगर गर्न सक्छ भनेर सम्झनु मानसिक रूपमा माग गर्ने छ। र त्यो यति हो, सिक्न कुनै फ्यान्सी शब्दहरू छैनन्।

धेरै उथला माइक्रोसर्भिसहरू

यो उथले-गहिरो मोड्युल सिद्धान्त स्केल-अग्नोस्टिक हो, र हामी यसलाई माइक्रोसर्भिस आर्किटेक्चरमा लागू गर्न सक्छौं। धेरै उथला माइक्रोसर्भिसहरूले कुनै राम्रो गर्दैनन् - उद्योग केही हदसम्म "म्याक्रोसर्भिस" तिर अगाडि बढिरहेको छ, अर्थात्, सेवाहरू जुन त्यति उथले छैनन् (=गहिरो)। सबैभन्दा खराब र समाधान गर्न गाह्रो घटनाहरू मध्ये एक तथाकथित वितरित मोनोलिथ हो, जुन प्रायः यो अत्यधिक दानेदार उथले विभाजनको परिणाम हो।

मैले एक पटक एक स्टार्टअपमा परामर्श गरें जहाँ पाँच विकासकर्ताहरूको टोलीले १७(!) माइक्रोसर्भिसहरू प्रस्तुत गरे। तिनीहरू १० महिना पछाडि थिए र सार्वजनिक रिलीजको नजिक कतै पनि देखिँदैनथे। प्रत्येक नयाँ आवश्यकताले ४+ माइक्रोसर्भिसहरूमा परिवर्तनहरू निम्त्यायो। यस्तो वितरित प्रणालीमा समस्या पुन: उत्पादन र डिबग गर्न ठूलो समय लाग्यो। बजार र संज्ञानात्मक भार दुवै समय अस्वीकार्य रूपमा उच्च थिए। 🤯

के यो नयाँ प्रणालीको अनिश्चितता सम्बोधन गर्ने सही तरिका हो? सुरुमा सही तार्किक सीमाहरू प्राप्त गर्न अत्यन्तै गाह्रो छ। कुञ्जी भनेको तपाईंले जिम्मेवारीपूर्वक पर्खन सक्ने गरी ढिलो निर्णयहरू गर्नु हो, किनभने त्यो तपाईंसँग सबैभन्दा बढी जानकारी हुने समय हो। अगाडि नेटवर्क तह प्रस्तुत गरेर, हामी हाम्रो डिजाइन निर्णयहरू सुरुदेखि नै उल्टाउन गाह्रो बनाउँछौं। टोलीको एक मात्र औचित्य थियो: "FAANG कम्पनीहरूले माइक्रोसर्भिस आर्किटेक्चर प्रभावकारी साबित गरे"। नमस्ते, तपाईंले ठूलो सपना देख्न बन्द गर्नुपर्छ।

Tanenbaum-Torvalds debate ले तर्क गर्‍यो कि Linux को मोनोलिथिक डिजाइन त्रुटिपूर्ण र अप्रचलित थियो, र यसको सट्टा माइक्रोकर्नेल आर्किटेक्चर प्रयोग गर्नुपर्छ। वास्तवमा, माइक्रोकर्नेल डिजाइन "सैद्धान्तिक र सौन्दर्य" दृष्टिकोणबाट श्रेष्ठ जस्तो देखिन्थ्यो। व्यावहारिक पक्षम