हर डेवलपर के पास वह फोल्डर होता है। वह जिसे utils या helpers कहा जाता है जिसे आप एक रेपो से दूसरी रेपो में कॉपी करते हैं। आप उसे पेस्ट करते हैं, बीस मिनट पुराने डेटाबेस स्कीमा के रेफरेंस हटाने में, उन ऑथ चेक को निकालने में जो लागू नहीं होते, और वेरिएबल्स का नाम बदलने में बिताते हैं ताकि आपका नया लिंटर चिल्लाना बंद कर दे। मैं भी ऐसा ही अपने बनाए हुए थीमिंग सिस्टम, Dynamic Theme Kit के साथ करता था। यह एक एप्लिकेशन के अंदर एक फीचर के रूप में शुरू हुआ था, और महीनों तक, मैंने इसे एक पोर्टेबल टूल की तरह माना। मैं गलत था। कोड कॉपी करना पुन: उपयोग (reuse) नहीं है। यह अतिरिक्त चरणों के साथ दोहराव (duplication) है।

सिंगल-प्रोजेक्ट माइंडसेट का जाल

जब आप किसी प्रोजेक्ट के अंदर कोई फीचर बनाते हैं, तो आप सैकड़ों अदृश्य धारणाएं (assumptions) बना लेते हैं। कलर पैलेट किसी खास CSS-in-JS सेटअप को मान सकता है। स्पेसिंग स्केल आपकी कंपनी के ब्रांड गाइड से किसी डिज़ाइन टोकन का संदर्भ ले सकता है। लाइट और डार्क मोड के बीच का टॉगल उस ऐप के बैकएंड के लिए विशिष्ट यूजर प्रेफरेंस एंडपॉइंट को कॉल कर सकता है। ये डिपेंडेंसीज़ (dependencies) हानिरहित लगती हैं क्योंकि प्रोजेक्ट के अंदर वे हानिरहित होती हैं। वे वहीं की होती हैं।

समस्या तब शुरू होती है जब आप उस कोड को बाहर निकालने की कोशिश करते हैं। आपको पता चलता है कि वह "reusable" कंपोनेंट वास्तव में छिपे हुए स्ट्रिंग्स का एक जाल है जो उसे उस एक कोडबेस से जोड़ता है। मैंने DTK के साथ यह सीखा। इसने थीम वेरिएबल्स तो जनरेट किए, हाँ। लेकिन इसने एक विशिष्ट फोल्डर स्ट्रक्चर की भी अपेक्षा की। इसने ओरिजिनल ऐप के टाइप्स डायरेक्टरी में कहीं गहराई से एक टाइप डेफिनिशन इम्पोर्ट किया। इसने एक ग्लोबल कॉन्फ़िग ऑब्जेक्ट की उपस्थिति मान ली जो केवल उसी एक रिपॉजिटरी में मौजूद था। मैंने कभी इस पर ध्यान नहीं दिया क्योंकि उस प्रोजेक्ट के भीतर, सब कुछ हमेशा मौजूद था।

DTK को एक स्टैंडअलोन पैकेज में बदलना विस्तार (expansion) नहीं, बल्कि सर्जरी जैसा था। मुझे और अधिक फीचर्स की आवश्यकता नहीं थी। मुझे कम कनेक्शनों की आवश्यकता थी।

Dynamic Theme Kit को एक्सट्रैक्ट करना

सबसे कठिन काम कोडबेस के साथ बैठना और हर फंक्शन और हर एक्सपोर्ट से पूछना था: क्या यह थीमिंग लॉजिक की सेवा करता है, या यह प्रोजेक्ट की सेवा करता है? मैंने स्टाइलिंग प्रीसेट्स को हटा दिया। मैंने इस धारणा को हटा दिया कि उपभोक्ता (consumer) एक React एप्लिकेशन होगा। मैंने डिफ़ॉल्ट कलर पैलेट को पूरी तरह से हटा दिया। ओरिजिनल प्रोजेक्ट में डिफ़ॉल्ट्स में एक नेवी-एंड-स्लेट कॉर्पोरेट एस्थेटिक (aesthetic) शामिल था। उसे हटाना ज़रूरी था। एक पैकेज आपके ब्रांड कलर्स को शिप नहीं कर सकता।

नया किट ठीक एक काम करेगा। यह एक कॉन्फ़िगरेशन ऑब्जेक्ट लेता है—कुछ कलर वैल्यूज़, कुछ स्पेसिंग नंबर्स, कुछ टाइपोग्राफी स्केल्स—और यह CSS कस्टम प्रॉपर्टीज़ जनरेट करता है। बस इतना ही। यह उन्हें लागू नहीं करता है। यह यह तय नहीं करता है कि वे आपके DOM में कहाँ जाएंगे। इसे इससे फर्क नहीं पड़ता कि आप Tailwind, Styled Components, या सादे HTML का उपयोग करते हैं। यह आपके एप्लिकेशन को वेरिएबल्स देता है, और आपका प्रोजेक्ट चुनता है कि उनका उपयोग कैसे करना है।

वह पाबंदी (constraint) शुरू में सीमित करने वाली लगी। लेकिन बाद में यह मुक्तिदायक (liberating) साबित हुई।

जब आप वास्तव में इसे पुन: उपयोग करने की कोशिश करते हैं तो क्या टूटता है

कुछ भी प्रकाशित करने से पहले, मुझे प्रमाण चाहिए था कि क्या वह एब्स्ट्रैक्शन (abstraction) वास्तव में काम करता है। मैंने अपने आर्काइव से तीन छोटे व्यक्तिगत प्रोजेक्ट निकाले: एक मार्कडाउन प्रिव्यू टूल, एक हैबिट ट्रैकर, और एक इवेंट के लिए लैंडिंग पेज। उनमें से किसी का भी फ्रेमवर्क या फोल्डर स्ट्रक्चर एक जैसा नहीं था। मैंने प्रत्येक में स्थानीय रूप से DTK इंस्टॉल किया और उन्हें थीम करने की कोशिश की।

पहला प्रयास तुरंत विफल हो गया। DTK द्वारा जनरेट किए गए वेरिएबल नाम बहुत विशिष्ट थे। यह --primary-action और --background-overlay जैसे टोकन आउटपुट कर रहा था जो एक निश्चित UI लेआउट का संकेत देते थे। मार्कडाउन प्रिव्यूअर में, उन नामों का कोई मतलब नहीं था। वहां कोई एक्शन बटन नहीं था। वहां कोई ओवरले नहीं था। मैंने जनरेशन लॉजिक का नाम बदलकर न्यूट्रल, स्ट्रक्चरल नाम रखे जो विजेट के बजाय वैल्यू का वर्णन करते थे।

मैंने यह भी पाया कि मेरे डिफ़ॉल्ट मान बहुत अधिक आक्रामक (aggressive) थे। जब कोई यूजर एक अधूरा कॉन्फ़िग पास करता था, तो DTK उन कमियों को ऐसे मानों से भर देता था जो एक घने डैशबोर्ड में तो ठीक लगते थे लेकिन एक खाली लैंडिंग पेज पर सब कुछ बिगाड़ देते थे। मैं पारदर्शी डिफ़ॉल्ट्स (transparent defaults) पर आ गया जहाँ गायब टोकन बस रेंडर नहीं होते थे, जिससे कंज्यूमिंग प्रोजेक्ट को अपने स्वयं के फॉलबैक (fallbacks) परिभाषित करने की अनुमति मिलती थी।

फिर डॉक्यूमेंटेशन की बात आई। जो मेरे लिए स्पष्ट था—"बस एक कॉन्फ़िग ऑब्जेक्ट पास करें"—आधी रात को README पढ़ रहे किसी व्यक्ति के लिए वह अस्पष्ट था। मैंने इसे वास्तविक ऑब्जेक्ट्स, वास्तविक फ़ाइल पाथ और इस बात की स्पष्ट व्याख्या के साथ फिर से लिखा कि जब आप फ़ंक्शन को कॉल करते हैं तो क्या होता है बनाम आपके एप्लिकेशन को उसके बाद क्या करने की आवश्यकता है।

ये छोटे व्यक्तिगत प्रोजेक्ट टेस्ट बेड के रूप में काम करते थे। इनमें जोखिम कम था, लेकिन उन्होंने उन वास्तविक खामियों को उजागर किया जिन्हें मैं सोर्स कोड को अलग से देखते हुए नहीं पकड़ पाता।

असली परीक्षा: Web Weavers World में प्रोडक्शन

व्यक्तिगत प्रोजेक्ट्स सैंडबॉक्स की तरह होते हैं। इनमें कोई डेडलाइन, स्टेकहोल्डर्स, या ऐसा पुराना (legacy) CSS नहीं होता जो आपके पैकेज से पहले का हो। असली परीक्षा तब हुई जब मैंने DTK को अपनी बिजनेस साइट, Web Weavers World में इंटीग्रेट किया। यह एक लाइव प्रॉपर्टी थी जिसमें मौजूदा स्टाइल्स, क्लाइंट की उम्मीदें और एनालिटिक्स का ध्यान रखना ज़रूरी था। अगर पैकेज से कुछ टूट जाता, तो मैं बस रेपो (repo) डिलीट करके फिर से शुरुआत नहीं कर सकता था।

मैंने DTK को बिल्ड पाइपलाइन में जोड़ा, इसे एक नए कलर कॉन्फ़िगरेशन की ओर निर्देशित किया, और इसे CSS वेरिएबल्स का एक नया सेट जेनरेट करने दिया। इंटीग्रेशन में एक दोपहर लगी, एक हफ्ता नहीं। यही वह संकेत था। पहले, एक नया थीम जोड़ने का मतलब था नया CSS लिखना, बीस फाइलों में हार्डकोडेड हेक्स (hex) वैल्यूज़ को ढूँढना, और यह उम्मीद करना कि मैंने कोई एज केस (edge case) मिस न कर दिया हो। अब मैं कॉन्फ़िगरेशन फ़ाइल में एक पैलेट जोड़ता हूँ, DTK वेरिएबल्स जेनरेट करता है, और साइट का बाकी हिस्सा उनका उपयोग करता है। थीम लॉजिक एक नाजुक मैन्युअल प्रक्रिया से बदलकर कुछ ऐसा बन गया जिस पर मैं इतना भरोसा करता हूँ कि उसे सहयोगियों (collaborators) को सौंप सकूँ।

तीन सवाल जिन्होंने मेरे बनाने के तरीके को बदल दिया

इस प्रक्रिया से गुजरते हुए मुझे एक मानसिक चेकलिस्ट बनाने के लिए मजबूर होना पड़ा, जिसका उपयोग मैं अब किसी भी चीज़ को एब्स्ट्रैक्ट (abstract) करने से पहले करता हूँ:

  • क्या यह वेरिएबल वास्तव में जेनेरिक है? यदि नाम या लॉजिक मूल प्रोजेक्ट की किसी डोमेन अवधारणा (domain concept) का संदर्भ देता है, तो उसे वहीं छोड़ दें।
  • क्या यह पैकेज में होना चाहिए या एप्लिकेशन में? बिजनेस रूल्स, ब्रांड पहचान और लेआउट की धारणाएं (assumptions) ऐप में रहती हैं। वह प्लंबिंग (plumbing) जो मानकीकृत आउटपुट जेनरेट करती है, पैकेज में रहती है।
  • क्या मैं एक पुन: प्रयोज्य (reusable) समस्या का समाधान कर रहा हूँ या किसी प्रोजेक्ट-विशिष्ट समस्या का? ईमानदारी से इसका उत्तर देना सबसे कठिन है। हम यह सोचना पसंद करते हैं कि हमारे समाधान सार्वभौमिक (universal) हैं। आमतौर पर वे स्थानीय (local) होते हैं।

इन सवालों के जवाब देने से मुझे अपने डिज़ाइन को सरल बनाने के लिए मजबूर होना पड़ा, जो अक्सर कोड जोड़ने के बजाय उसे हटाने से हुआ। DTK ने मुझे सिखाया कि पुन: उपयोग (reuse) कोई ऐसा उपहार नहीं है जो आप खुद को देते हैं। यह एक अनुशासन है जिसे आप सुविधा को 'ना' कहकर अपनाते हैं।

रिफैक्टरिंग (Refactoring) के बारे में सोचने का एक अलग तरीका

मैं रिफैक्टर को इस आधार पर मापता था कि उन्होंने कोड को कितना छोटा कर दिया। कम लाइनें प्रगति जैसा महसूस होती थीं। अब मैं उन्हें इस आधार पर मापता हूँ कि वे कितने नए रास्ते खोलते हैं। Dynamic Theme Kit इसलिए शानदार नहीं है क्योंकि यह संक्षिप्त है। यह इसलिए उपयोगी है क्योंकि इसने अपने इंटरनल को बदले बिना तीन असंबंधित व्यक्तिगत प्रोजेक्ट्स और एक प्रोडक्शन बिजनेस साइट का सामना किया है।

यही वह पैमाना है जो मायने रखता है। जो कोड एक बार काम करता है वह एक खर्च है। जो कोड बार-बार काम करता है वह एक संपत्ति (asset) है। अब मैं कोई भी फीचर शुरू करने से पहले रुकता हूँ। मैं खुद से पूछता हूँ कि क्या मैं कुछ ऐसा बना रहा हूँ जिसकी मुझे फिर से ज़रूरत होगी। यदि उत्तर 'हाँ' है, तो मैं पहली लाइन से ही इसे अलग तरह से बनाता हूँ। मैं इनपुट्स को अलग करता हूँ। मैं आउटपुट्स को परिभाषित करता हूँ। मैं धारणाओं (assumptions) को हटा देता हूँ।

सबसे अच्छा रिफैक्टर आपके कोड को छोटा नहीं बनाता। यह आपके कोड को उन जगहों पर काम करने के लायक बनाता है जिनकी आपने अभी तक कल्पना भी नहीं की है।