शून्य-डाउनटाइम स्कीमा परिवर्तन: ऐसे ऐडिटिव माइग्रेशन जो सुरक्षित रहें
ऐडिटिव माइग्रेशन, सुरक्षित बैकफिल और चरणबद्ध रोलआउट के साथ शून्य-डाउनटाइम स्कीमा बदलाव सीखें ताकि पुराने क्लाइंट रिलीज़ के दौरान काम करते रहें।

स्कीमा परिवर्तनों के मामले में शून्य-डाउनटाइम का असली मतलब\n\nशून्य-डाउनटाइम स्कीमा परिवर्तन का मतलब यह नहीं कि कुछ भी बदल नहीं रहा। इसका मतलब है कि उपयोगकर्ता तब भी काम कर सकते हैं जब आप डेटाबेस और ऐप अपडेट कर रहे हों, बिना असफलताओं या वर्कफ़्लो ब्लॉकेज के।\n\nडाउनटाइम वह कोई भी क्षण है जब आपकी सिस्टम सामान्य रूप से काम करना बंद कर देती है। ये 500 एरर, API टाइमआउट, स्क्रीन जो लोड होती हैं पर ख़ाली या गलत मान दिखाती हैं, बैकग्राउंड जॉब्स का क्रैश होना, या ऐसा डेटाबेस जो पढ़ तो ले रहा है लेकिन बड़े माइग्रेशन के कारण राइट्स को ब्लॉक कर रहा है — इन सब रूपों में दिख सकता है।\n\nएक स्कीमा परिवर्तन सिर्फ़ मेन ऐप UI को ही तोड़ सकता है, ऐसा नहीं है। सामान्य विफलता-बिंदुओं में API क्लाइंट शामिल हैं जो पुराने रेस्पॉन्स शेप की उम्मीद करते हैं, बैकग्राउंड जॉब्स जो खास कॉलम पढ़ते या लिखते हैं, रिपोर्ट्स जो सीधे टेबल क्वेरी करते हैं, थर्ड-पार्टी इंटीग्रेशन, और आंतरिक एडमिन स्क्रिप्ट्स जो "कल तक ठीक चल रही थीं।"\n\nपुराने मोबाइल ऐप्स और कैश्ड क्लाइंट अक्सर समस्या बन जाते हैं क्योंकि आप उन्हें तुरंत अपडेट नहीं कर सकते। कुछ यूज़र्स किसी ऐप वर्ज़न को हफ्तों तक रखते हैं। कुछ के पास अनियमित कनेक्टिविटी होती है और वे बाद में पुराने अनुरोध फिर से भेजते हैं। यहाँ तक कि वेब क्लाइंट भी पुराने वर्ज़न जैसा व्यवहार कर सकते हैं जब सर्विस वर्कर, CDN, या प्रॉक्सी कैश स्टेल कोड या मान्यताओं को पकड़ कर रखे हों।\n\nअसल लक्ष्य "एक बड़ा माइग्रेशन जो जल्दी खत्म हो जाए" नहीं है। लक्ष्य छोटे कदमों का एक अनुक्रम है जहाँ हर कदम अपने आप में काम करता है, भले ही क्लाइंट्स अलग-अलग वर्ज़न पर हों।\n\nएक व्यावहारिक परिभाषा: आप नए कोड और नया स्कीमा किसी भी क्रम में डिप्लॉय कर सकें, और सिस्टम फिर भी काम करे।\n\nयह मानसिकता आपको क्लासिक जाल में फँसने से बचाती है: नया ऐप डिप्लॉय करना जो नए कॉलम की उम्मीद करे जबकि कॉलम अभी मौजूद नहीं है, या नया कॉलम डालना जो पुराने कोड हैंडल नहीं कर सकता। परिवर्तनों को पहले ऐडिटिव प्लान करें, चरणों में रोल आउट करें, और केवल तय होने के बाद पुराने रास्ते हटाएँ कि कोई उनका उपयोग नहीं कर रहा।\n\n## पहले ऐसे ऐडिटिव परिवर्तनों से शुरू करें जो पुराने कोड को न तोड़ें\n\nशून्य-डाउनटाइम स्कीमा परिवर्तनों के लिए सबसे सुरक्षित रास्ता जोड़ना है, बदलना नहीं। नया कॉलम या नई टेबल जोड़ने से आमतौर पर कुछ टूटता नहीं क्योंकि मौजूदा कोड पुराने शेप को पढ़ना और लिखना जारी रख सकता है।\n\nरिनेम और डिलीशन जोखिमभरे कदम हैं। एक रिनेम मूलतः "नया जोड़ें + पुराना हटाएँ" है, और "पुराना हटाएँ" वही हिस्सा है जहाँ पुराने क्लाइंट क्रैश होते हैं। अगर आपको रिनेम करना ही है, तो इसे दो-स्टेप बदलाव के रूप में ट्रीट करें: पहले नया फील्ड जोड़ें, पुराना फील्ड कुछ समय के लिए रखें, और केवल तब हटाएँ जब आप सुनिश्चित हों कि कोई निर्भर नहीं कर रहा।\n\nकॉलम जोड़ते समय nullable फ़ील्ड्स से शुरू करें। एक nullable कॉलम पुराने कोड को बिना नई जानकारी के रो इनसर्ट करने देता है। अगर अंततः आप NOT NULL चाहते हैं, तो पहले इसे nullable के रूप में जोड़ें, बैकफिल करें, और बाद में NOT NULL लागू करें। डिफ़ॉल्टस मदद कर सकते हैं, पर सावधान रहें: कुछ डेटाबेस में डिफ़ॉल्ट जोड़ने से भी कई रोज़ टच हो सकती हैं, जिससे परिवर्तन धीमा पड़ सकता है।\n\nइंडेक्स भी एक "सुरक्षित पर निःशुल्क नहीं" जोड़ हैं। वे पढ़ को तेज कर सकते हैं, पर इंडेक्स बनाना और बनाए रखना राइट्स को धीमा कर सकता है। इंडेक्स तब जोड़ें जब आप जानते हों कि कौन सा क्वेरी उसका उपयोग करेगा, और यदि आपका डेटाबेस व्यस्त है तो शांत समय में रोलआउट पर विचार करें।\n\nएडिटिव डेटाबेस माइग्रेशनों के लिए एक सरल नियम सेट:\n\n- पहले नई टेबल्स या कॉलम जोड़ें, पुराने को छेड़ें नहीं।\n- नए फील्ड्स को तब तक ऑप्शनल (nullable) रखें जब तक डाटा भर न जाए।\n- क्लाइंट अपडेट होने तक पुराने क्वेरीज और पेलोड काम करते रहें।\n- बैकफिल के बाद ही constraints (NOT NULL, unique, foreign keys) लागू करें।\n\n## चरणबद्ध रोलआउट प्लान जो पुराने क्लाइंट्स को काम करने दे\n\nशून्य-डाउनटाइम स्कीमा बदलावों को एक रोलआउट समझें, न कि एक सिंगल डिप्लॉय। लक्ष्य यह है कि पुराने और नए ऐप वर्ज़न साइड-बाय-साइड चलें जबकि डेटाबेस धीरे-धीरे नए शेप की ओर बढ़े।\n\nएक व्यावहारिक अनुक्रम:\n\n1. नया स्कीमा एक कम्पैटिबल तरीके से जोड़ें। नए कॉलम या टेबल बनाएं, nulls की अनुमति दें, और कड़े constraints से बचें जो पुराने कोड पूरा नहीं कर सके। अगर आपको इंडेक्स चाहिए, तो इसे ऐसे जोड़ें कि राइट्स ब्लॉक न हों।\n2. बैकएंड बदलाव डिप्लॉय करें जो दोनों “भाषाएँ” समझ सके। API को अपडेट करें ताकि वह पुराने अनुरोध और नए अनुरोध दोनों स्वीकार कर सके। नया फील्ड लिखना शुरू करें जबकि पुराने फील्ड को सही रखा जाए। यह "डुअल राइट" फेज़ मिक्स्ड क्लाइंट वर्ज़न को सुरक्षित बनाता है।\n3. मौजूदा डाटा को छोटे बैचों में बैकफिल करें। पुराने रोज़ के लिए नया कॉलम धीरे-धीरे भरें। बैच साइज सीमित रखें, ज़रूरत पड़ने पर डिले जोड़ें, और प्रगति ट्रैक करें ताकि लोड बढ़ने पर आप रोक सकें।\n4. कवरेज उच्च होने पर ही रीड स्विच करें। जब ज्यादातर रोज़ बैकफिल हो जाएँ और आप आश्वस्त हों, तो बैकएंड को नया फील्ड प्राथमिकता देने के लिए बदलें। कुछ समय के लिए पुराने फील्ड का फॉलबैक रखें।\n5. पुराना फील्ड आख़िर में हटाएँ, और केवल तब जब यह असल में अनयूज़्ड हो। पुराने मोबाइल बिल्ड्स के अक्स जात होने तक प्रतीक्षा करें, लॉग्स में पुराने फील्ड की कोई रीड न दिखे, और आपके पास क्लीन रोलबैक प्लान हो। फिर पुराना कॉलम और संबंधित कोड हटाएँ।\n\nउदाहरण: आप full_name जोड़ते हैं पर पुराने क्लाइंट अभी भी first_name और last_name भेजते हैं। एक अवधि के लिए बैकएंड लिखते समय full_name बना सकता है, मौजूदा यूज़र्स को बैकफिल कर सकता है, फिर बाद में डिफ़ॉल्ट रूप से full_name पढ़ना शुरू कर सकता है जबकि पुराने पेलोड भी सपोर्ट करता है। केवल जब अपनापन स्पष्ट हो तब पुराने फील्ड्स हटाएँ।\n\n## अनपेक्षित प्रभाव से बचते हुए बैकफिल कैसे करें\n\nबैकफिल किसी नए कॉलम या टेबल के लिए मौजूदा रोज़ को भरता है। यह शून्य-डाउनटाइम स्कीमा बदलावों का सबसे रिस्की हिस्सा होता है क्योंकि इससे भारी डेटाबेस लोड, लंबे लॉक और "आधा-माइग्रेटेड" व्यवहार हो सकते हैं।\n\nपहले तय करें कि आप बैकफिल कैसे चलाएंगे। छोटे डाटासेट के लिए, एक बार मैन्युअल रनबुक ठीक हो सकती है। बड़े डाटासेट के लिए, बैकग्राउंड वर्कर या शेड्यूल्ड टास्क पसंद करें जो बार-बार चल सके और सुरक्षित रूप से रोक सके।\n\nवर्क को बैच करें ताकि आप डेटाबेस पर दबाव नियंत्रित कर सकें। लाखों रोज़ एक ही ट्रांज़ैक्शन में अपडेट न करें। एक अनुमानित चंक साइज और बैचों के बीच छोटा विराम रखें ताकि सामान्य यूज़र ट्रैफिक स्मूद रहे।\n\nएक व्यावहारिक पैटर्न:\n\n- इंडेक्स्ड की का उपयोग करके अगला छोटा बैच चुनें (उदा., अगले 1,000 रोज़)।\n- केवल वही अपडेट करें जो मिसिंग हो (पहले से बैकफिल की गई रोज़ को फिर से न लिखें)।\n- जल्दी कमिट करें, फिर थोड़ी देर सोएँ।\n- प्रगति रिकॉर्ड करें (अंतिम प्रोसेस्ड ID या टाइमस्टैम्प)।\n- फ़ेलियर पर री-ट्राई करें बिना सब कुछ फिर से शुरू किए।\n\nजॉब को रीस्टार्टेबल बनाएं। डेडिकेटेड टेबल में एक सिंपल प्रोग्रेस मार्कर रखें, और जॉब को इस तरह डिज़ाइन करें कि दोबारा चलाने से डाटा भ्रष्ट न हो। आइडेम्पोटेंट अपडेट्स (उदा., UPDATE ... WHERE new_field IS NULL) उपयोगी होते हैं।\n\nजैसे-जैसे आप चलते हैं वैलिडेट करें। कितने रोज़ अभी भी नई वैल्यू मिसिंग है यह ट्रैक करें, और कुछ सैनीटी चेक्स जोड़ें: नेगेटिव बैलेंस न हों, टाइमस्टैम्प अपेक्षित रेंज में हों, स्टेटस अनुमत सेट में हो। असली रिकॉर्ड्स को सैंपल करके स्पॉट-चेक करें।\n\nनिर्णय लें कि बैकफिल अधूरा होने के दौरान ऐप क्या करे। एक सुरक्षित विकल्प फॉलबैक रीड्स है: अगर नया फील्ड null है तो पुरानी वैल्यू को गणना या पढ़ लें। उदाहरण: आप preferred_language जोड़ते हैं। बैकफिल पूरा होने तक API मौजूदा भाषा को प्रोफ़ाइल सेटिंग्स से लौटाकर preferred_language खाली होने पर फॉलबैक कर सकती है, और पूरा होने के बाद ही नए फील्ड को आवश्यक बनाती है।\n\n## मिक्स्ड क्लाइंट वर्ज़न्स के लिए API कम्पैटिबिलिटी नियम\n\nजब आप स्कीमा बदलते हैं, तो आप शायद हर क्लाइंट को नियंत्रित नहीं करते। वेब यूज़र्स जल्दी अपडेट होते हैं, जबकि पुराने मोबाइल बिल्ड्स हफ्तों तक सक्रिय रह सकते हैं। इसलिए बैकवर्ड-कम्पैटिबल APIs मायने रखते हैं, भले ही आपका डेटाबेस माइग्रेशन "सुरक्षित" हो।\n\nनए डाटा को शुरुआत में ऑप्शनल समझें। रिक्वेस्ट और रेस्पॉन्स में नए फील्ड जोड़ें, पर पहले दिन उन्हें ज़रूरी न बनाएं। अगर कोई पुराना क्लाइंट नया फील्ड नहीं भेजता, तो सर्वर को फिर भी अनुरोध स्वीकार करना चाहिए और वही व्यवहार करना चाहिए जैसा कल करता था।\n\nमौजूदा फील्ड्स के अर्थ बदलने से बचें। किसी फील्ड का नाम बदलना ठीक है अगर आप पुराना नाम भी काम करता रखें। किसी फील्ड को नए मतलब के लिए दोबारा उपयोग करने में सूक्ष्म ब्रेकेज़ होते हैं।\n\nसर्वर-साइड डिफ़ॉल्ट आपका सुरक्षा जाल हैं। जब आप preferred_language जैसा नया कॉलम जोड़ते हैं, तो सर्वर पर डिफ़ॉल्ट सेट कर दें जब यह गायब हो। API रेस्पॉन्स में नया फील्ड शामिल हो सकता है, और पुराने क्लाइंट इसे नज़रअंदाज़ कर सकते हैं।\n\nकम्पैटिबिलिटी नियम जो ज़्यादातर आउटेज रोकते हैं:\n\n- पहले नए फील्ड्स को ऑप्शनल के रूप में जोड़ें, फिर अपनाने के बाद लागू करें।\n- पुराना व्यवहार स्थिर रखें, भले ही आप बेहतर व्यवहार किसी फ्लैग के पीछे जोड़ें।\n- सर्वर पर डिफ़ॉल्ट लगाएं ताकि पुराने क्लाइंट नए फील्ड छोड़ सकें।\n- मिश्रित ट्रैफ़िक मानकर दोनों पथों का परीक्षण करें: "नया क्लाइंट भेजता है" और "पुराना क्लाइंट नहीं भेजता"।\n- एरर मेसेज और कोड स्थिर रखें ताकि मॉनिटरिंग अचानक शोर न करे।\n\nउदाहरण: आप साइनअप फ्लो में company_size जोड़ते हैं। बैकएंड मिसिंग होने पर "unknown" जैसा डिफ़ॉल्ट सेट कर सकता है। नए क्लाइंट असली वैल्यू भेज सकते हैं, पुराने क्लाइंट बिना रुकावट के काम करते रहते हैं, और डैशबोर्ड पठनीय रहते हैं।\n\n## जब आपका ऐप पुनः जनरेट होता है: स्कीमा और लॉजिक को सिंक में रखना\n\nअगर आपका प्लेटफ़ॉर्म ऐप्लिकेशन को पुनः जनरेट करता है, तो आपको कोड और कॉन्फ़िग का साफ़ रीबिल्ड मिलता है। यह शून्य-डाउनटाइम स्कीमा बदलावों में मदद करता है क्योंकि आप छोटे, ऐडिटिव कदम ले सकते हैं और अक्सर रीडिप्लॉय कर सकते हैं बजाय महीनों तक पैच रखने के।\n\nकुंजी है एक स्रोत-निस्संदेह सच — one source of truth। अगर डेटाबेस स्कीमा एक जगह बदलता है और बिजनेस लॉजिक कहीं और, तो ड्रिफ्ट तेज़ी से होता है। तय करें कि बदलाव कहाँ पर परिभाषित होंगे, और बाकी सब कुछ जनरेटेड आउटपुट मानें।\n\nस्पष्ट नामकरण चरणबद्ध रोलआउट के दौरान दुर्घटनाएँ कम करता है। अगर आप नया फील्ड जोड़ते हैं, तो यह स्पष्ट रखें कि कौन सा फील्ड पुराने क्लाइंट्स के लिए सुरक्षित है और कौन सा नया रास्ता है। उदाहरण के लिए, status_v2 नाम देना status_new से सुरक्षित है क्योंकि छह महीने बाद भी इसका मतलब समझ आता है।\n\n### हर रीजेनरेशन के बाद किसे री-टेस्ट करें\n\nभले ही परिवर्तन ऐडिटिव हों, एक रीबिल्ड छिपी हुई कप्लिंग को उजागर कर सकता है। हर रीजेनरेशन और डिप्लॉय के बाद कुछ महत्वपूर्ण फ्लोज़ फिर से जांचें:\n\n- साइन अप, लॉगिन, पासवर्ड रिसेट, टोकन रिफ्रेश।\n- कोर क्रिएट और अपडेट एक्शन्स (सबसे ज़्यादा उपयोग होने वाले)।\n- एडमिन और परमिशन चेक्स।\n- पेमेंट्स और वेबहुक्स (उदा., Stripe ईवेंट्स)।\n- नोटिफिकेशन्स और मेसेजिंग (ईमेल/SMS, Telegram)।\n\nमाइग्रेशन कदम पहले से प्लान करें: नया फील्ड जोड़ें, दोनों फील्ड सपोर्ट के साथ डिप्लॉय करें, बैकफिल करें, रीड्स स्विच करें, और बाद में पुराना पाथ रिटायर करें। यह सिक्वेंस स्कीमा, लॉजिक, और जनरेटेड कोड को एक साथ रखता है ताकि बदलाव छोटे, समीक्षा योग्य और उल्टे किए जा सकने वाले रहें।\n\n## आउटेज पैदा करने वाले सामान्य गलतियाँ (और उनसे कैसे बचें)\n\nज़्यादातर आउटेज "कड़ी" डेटाबेस वर्क से नहीं आते; वे तब होते हैं जब डेटाबेस, API और क्लाइंट्स के बीच कॉन्ट्रैक्ट गलत क्रम में बदला जाता है।\n\nसामान्य जाल और सुरक्षित कदम:\n\n- किसी कॉलम का नाम बदलना जबकि पुराना कोड उसे पढ़ता रहे। पुराना कॉलम रखें, नया जोड़ें, और दोनों को कुछ समय के लिए मैप करें (दोनों पर लिखें, या एक व्यू का उपयोग करें)। केवल तब रिनेम करें जब आप साबित कर सकें कि कोई पुराना निर्भर नहीं है।\n- nullable फील्ड को बहुत जल्दी required बनाना। कॉलम को पहले nullable जोड़ें, कोड शिप करें जो हर जगह उसे लिखे, पुराने रोज़ बैकफिल करें, फिर आख़िरी माइग्रेशन में NOT NULL लागू करें।\n- एक विशाल ट्रांज़ैक्शन में बैकफिल करना जो टेबल्स को लॉक कर दे। छोटे बैचों में बैकफिल करें, लिमिट्स और पॉज़ के साथ। प्रगति ट्रैक करें ताकि आप सुरक्षित रूप से resume कर सकें।\n- राइट्स नए डाटा नहीं बना रहे हों और आप पहले रीड्स स्विच कर दें। पहले राइट्स स्विच करें, फिर बैकफिल, और उसके बाद रीड्स बदलें। रीड्स पहले बदलें तो आप खाली स्क्रीन, गलत टोटल्स, या "मिसिंग फील्ड" एरर देख पाएँगे।\n- पुराने फील्ड्स को बिना सबूत हटाना कि पुराने क्लाइंट चले गए हैं। पुराने फील्ड्स को जितना ज़रूरी हो उतना लंबा रखें। केवल तब हटाएँ जब मेट्रिक्स दिखाएँ कि पुराने वर्ज़न प्रभावी रूप से निष्क्रिय हैं, और आपने डिप्रीकेशन विंडो कम्युनिकेट की हो।\n\nअगर आप अपना ऐप रीजेनरेट करते हैं, तो एक साथ नामों और constraints को "साफ़ करने" का प्रलोभन होता है। उस प्रलोभन का विरोध करें। क्लीनअप अंतिम कदम होना चाहिए, पहला नहीं।\n\nएक अच्छा नियम: अगर एक बदलाव सुरक्षित तरीके से आगे नहीं बढ़ता और वापस नहीं आ सकता, तो वह प्रोडक्शन के लिए तैयार नहीं है।\n\n## चरणबद्ध माइग्रेशनों के लिए मॉनिटरिंग और रोलबैक प्लानिंग\n\nशून्य-डाउनटाइम स्कीमा बदलाव दो चीजों पर सफल या विफल होते हैं: आप क्या देखते हैं, और कितनी तेज़ी से आप रोक सकते हैं।\n\nउन संकेतों को ट्रैक करें जो असली यूज़र इम्पैक्ट दिखाते हैं, सिर्फ़ "डिप्लॉय फिनिश हुआ" नहीं:\n\n- API एरर रेट (विशेषकर अपडेट किए गए एंडपॉइंट्स पर 4xx/5xx स्पाइक्स)।\n- स्लो क्वेरीज (आपने जिन टेबल्स को छुआ है उनके लिए p95 या p99 क्वेरी टाइम)।\n- राइट लेटेंसी (पीक ट्रैफ़िक के दौरान इनसर्ट्स और अपडेट्स में लगने वाला समय)।\n- क्यू डेप्थ (बैकफिल या इवेंट प्रोसेसिंग के लिए जॉब्स का जमा होना)।\n- डेटाबेस CPU/IO प्रेशर (किसी भी अचानक उछाल पर ध्यान दें)।\n\nयदि आप डुअल राइट्स कर रहे हैं (पुराने और नए कॉलम या टेबल दोनों में लिखना), तो अस्थायी लॉगिंग जोड़ें जो दोनों की तुलना करे। इसे तंग रखें: केवल तब लॉग करें जब वैल्यूज़ भिन्न हों, रिकॉर्ड ID और छोटा कारण कोड शामिल करें, और यदि वॉल्यूम ज्यादा हो तो सैंपलिंग करें। माइग्रेशन के बाद इस लॉगिंग को हटाने की रिमाइंडर बनाना न भूलें ताकि यह स्थायी शोर न बन जाए।\n\nरोलबैक वास्तविक होना चाहिए। ज़्यादातर मामलों में आप स्कीमा नहीं रोलबैक करते; आप कोड रोलबैक करते हैं और ऐडिटिव स्कीमा जगह पर रख लेते हैं।\n\nएक व्यावहारिक रोलबैक रनबुक:\n\n- एप्लिकेशन लॉजिक को आख़िरी ज्ञात अच्छे वर्ज़न पर revert करें।\n- पहले नए रीड्स डिसेबल करें, फिर नए राइट्स।\n- नई टेबल्स या कॉलम रखें, पर उनका उपयोग बंद कर दें।\n- बैकफिल्स को तब तक पॉज़ करें जब तक मेट्रिक्स स्थिर न हों।\n\nबैकफिल्स के लिए एक स्टाप स्विच बनायें जिसे आप सेकंडों में फ्लिप कर सकें (फीचर फ्लैग, कॉन्फ़िग वैल्यू, जॉब पॉज़)। साथ ही चरणों को पहले से कम्युनिकेट करें: कब डुअल राइट्स शुरू होंगे, कब बैकफिल चलेगा, कब रीड्स स्विच होंगे, और "स्टाप" कैसा दिखेगा ताकि दबाव में कोई improvisation न करे।\n\n## त्वरित प्री-डिप्लॉय चेकलिस्ट\n\nस्कीमा चेंज भेजने से ठीक पहले रुकें और यह त्वरित चेक चलाएँ। यह उन छोटी धारणाओं को पकड़ता है जो मिक्स्ड क्लाइंट वर्ज़न्स के साथ आउटेज में बदल सकती हैं।\n\n- चेंज ऐडिटिव है, विनाशकारी नहीं। माइग्रेशन केवल टेबल्स, कॉलम्स, या इंडेक्स जोड़ता है। कुछ भी हटाया, रिनेम या सख़्त न किया गया है जिससे पुराने राइट्स अस्वीकार हो सकें।\n- रीड्स दोनों शेप के साथ काम करते हैं। नया सर्वर कोड "नया फील्ड मौजूद" और "नया फील्ड गायब" दोनों को बिना एरर के संभालता है। ऑप्शनल वैल्यूज़ के लिए सुरक्षित डिफ़ॉल्ट हों।\n- राइट्स कम्पैटिबल रहते हैं। नए क्लाइंट नया डाटा भेज सकते हैं, पुराने क्लाइंट पुराने पेलोड भेजकर सफल हो सकते हैं। अगर दोनों वर्ज़न सह-अस्तित्व में होने चाहिए, तो सर्वर दोनों फॉर्मैट्स स्वीकार करता है और पुराने क्लाइंट पार्स कर सकें ऐसे रेस्पॉन्स देता है।\n- बैकफिल रोकना और फिर से शुरू करना सुरक्षित है। जॉब बैचों में चलता है, बिना डुप्लिकेट या डाटा करप्ट किए री-स्टार्ट होता है, और "बाकी रोज़" की संख्या नापी जा सकती है।\n- आप जानते हैं कि हटाने की तारीख क्या है। एक ठोस नियम है कि कब लेगसी फील्ड्स या लॉजिक हटाया जा सकता है (उदा., X दिनों के बाद और पुष्टि कि Y% रिक्वेस्ट अपडेटेड क्लाइंट्स से हैं)।\n\nअगर आप किसी रीजेनरेटिंग प्लेटफ़ॉर्म का उपयोग कर रहे हैं, तो एक और सैनीटी चेक जोड़ें: उसी मॉडल से एक बिल्ड जनरेट और डिप्लॉय करें जिसे आप माइग्रेट कर रहे हैं, फिर पुष्टि करें कि जनरेटेड API और बिजनेस लॉजिक अभी भी पुराने रिकॉर्ड्स को सहन करते हैं। सामान्य विफलता यह मान लेना है कि नया स्कीमा नए आवश्यक लॉजिक को भी निहित करता है।\n\nसाथ ही दो त्वरित कार्रवाईयाँ लिखें जो आप डिप्लॉय के बाद कुछ गलत दिखने पर करेंगे: आप क्या मॉनिटर करेंगे (एरर, टाइमआउट, बैकफिल प्रगति) और आप सबसे पहले क्या रोलबैक करेंगे (फीचर फ्लैग ऑफ, बैकफिल पॉज़, सर्वर रिलीज revert)। यह "हम तेज़ी से प्रतिक्रिया देंगे" को एक वास्तविक योजना में बदल देता है।\n\n## उदाहरण: नया फील्ड जोड़ना जबकि पुराने मोबाइल ऐप्स अभी भी सक्रिय हों\n\nआप एक ऑर्डर ऐप चलाते हैं। आपको नया फील्ड delivery_window चाहिए, और यह नए बिजनेस नियमों के लिए आवश्यक होगा। समस्या यह है कि पुराने iOS और Android बिल्ड्स अभी भी उपयोग में हैं, और वे यह फील्ड कुछ दिनों या हफ्तों तक नहीं भेजेंगे। अगर आप डेटाबेस को तुरंत इसे आवश्यक बनाते हैं, तो वे क्लाइंट फेल होने लगेंगे।\n\nएक सुरक्षित रास्ता:\n\n- चरण 1: कॉलम को nullable के रूप में जोड़ें, बिना constraints के। मौजूदा रीड्स और राइट्स अपरिवर्तित रखें।\n- चरण 2: डुअल राइट। नए क्लाइंट (या बैकएंड) नया फील्ड लिखें। पुराने क्लाइंट काम करते रहें क्योंकि कॉलम null की अनुमति देता है।\n- चरण 3: बैकफिल। पुराने रोज़ के लिए delivery_window भरें—शिपिंग मेथड से अनुमान लगाकर या डिफ़ॉल्ट रूप से "anytime" तक ग्राहक उसे एडिट करे।\n- चरण 4: रीड्स स्विच करें। API और UI को पहले delivery_window पढ़ने के लिए अपडेट करें, पर जब वह गायब हो तो अनुमानित वैल्यू पर फॉलबैक रखें।\n- चरण 5: बाद में लागू करें। अपनापन और बैकफिल पूर्ण होने के बाद NOT NULL जोड़ें और फॉलबैक हटाएँ।\n\nप्रत्येक चरण के दौरान उपयोगकर्ताओं को जो महसूस होता है वह सुस्त रहेगा (यही लक्ष्य है):\n\n- पुराने मोबाइल यूज़र्स अभी भी ऑर्डर कर सकते हैं क्योंकि API मिसिंग डाटा को रिजेक्ट नहीं करती।\n- नए मोबाइल यूज़र्स नया फील्ड देखते हैं, और उनके विकल्प विश्वसनीय तरीके से सेव होते हैं।\n- सपोर्ट और ऑप्स धीरे-धीरे फील्ड भरते हुए देखते हैं, बिना अचानक गैप के।\n\nप्रत्येक स्टेप के लिए एक सरल मॉनिटरिंग गेट: उन नए ऑर्डर्स का प्रतिशत ट्रैक करें जहाँ delivery_window non-null है। जब यह लगातार उच्च रहे (और "मिसिंग फील्ड" वैलिडेशन एरर न्यूनतम हों), तो आम तौर पर बैकफिल से कंस्ट्रेंट लागू करने की ओर बढ़ना सुरक्षित होता है।\n\n## अगला कदम: दोहराने योग्य माइग्रेशन प्लेबुक बनाएं\n\nएक बार सावधानीपूर्वक रोलआउट करना ही रणनीति नहीं है। स्कीमा परिवर्तनों को एक सामान्य प्रक्रिया समझें: वही कदम, वही नामकरण, वही साइनऑफ़। तब अगला ऐडिटिव बदलाव भी सुस्त रहेगा, भले ही ऐप व्यस्त हो और क्लाइंट्स अलग वर्ज़न पर हों।\n\nप्लेबुक को संक्षिप्त रखें। इसे यह बताना चाहिए: हम क्या जोड़ते हैं, हम इसे सुरक्षित रूप से कैसे शिप करते हैं, और हम कब पुराने हिस्सों को हटाते हैं।\n\nएक सरल टेम्पलेट:\n\n- केवल जोड़ें (नया कॉलम/टेबल/इंडेक्स, नया API फील्ड जो ऑप्शनल हो)।\n- ऐसा कोड शिप करें जो पुराने और नए शेप दोनों पढ़ सके।\n- छोटे बैचों में बैकफिल करें, "हो गया" सिग्नल के साथ।\n- बिहेवियर को फीचर फ्लैग या कॉन्फ़िग से फ़्लिप करें, न कि री-डिप्लॉय से।\n- कटऑफ डेट और वेरिफिकेशन के बाद ही पुराने फील्ड/एंडपॉइंट हटाएँ।\n\nकम जोखिम वाली तालिका (एक नया ऑप्शनल स्टेटस, एक नोट्स फील्ड) से शुरू करें और पूरा प्लेबुक एंड-टू-एंड चलाएँ: ऐडिटिव चेंज, बैकफिल, मिक्स्ड-वर्ज़न क्लाइंट्स, फिर क्लीनअप। यह अभ्यास मॉनिटरिंग, बैचिंग और कम्युनिकेशन में गेप्स को बड़े रिस्क से पहले उजागर कर देता है।\n\nएक आदत जो दीर्घकालिक गड़बड़ी रोकती है: “बाद में हटाने” आइटम्स को असली काम के रूप में ट्रैक करें। जब आप अस्थायी कॉलम, कम्पैटिबिलिटी कोड, या डुअल-राइट लॉजिक जोड़ते हैं, तो तुरंत एक क्लीनअप टिकट बनाएं जिसमें मालिक और एक तारीख हो। रिलीज़ डॉक्स में एक छोटा "compatibility debt" नोट रखें ताकि यह दिखाई दे।\n\nअगर आप AppMaster के साथ बनाते हैं, तो आप रीजेनरेशन को सुरक्षा प्रक्रिया का हिस्सा बना सकते हैं: ऐडिटिव स्कीमा मॉडल करें, ट्रांज़िशन के दौरान पुराने और नए फील्ड दोनों को हैंडल करने के लिए बिजनेस लॉजिक अपडेट करें, और रीजेनरेट करें ताकि सोर्स कोड परिवर्तनों के रूप में साफ़ रहे। अगर आप देखना चाहते हैं कि यह वर्कफ़्लो नो-कोड सेटअप में कैसे फिट बैठता है जो वास्तविक सोर्स कोड भी उत्पन्न करता है, तो AppMaster (appmaster.io) उस तरह के पुनरावर्तीय, चरणबद्ध डिलीवरी स्टाइल के इर्द-गिर्द डिज़ाइन किया गया है।\n\nलक्ष्य पूर्णता नहीं है। लक्ष्य दोहरावनीयता है: हर माइग्रेशन का एक प्लान, एक माप और एक निकास मार्ग हो।\n
सामान्य प्रश्न
Zero-downtime का मतलब है कि उपयोगकर्ता तब भी सामान्य रूप से काम कर सकते हैं जब आप स्कीमा बदल रहे हों और कोड डिप्लॉय कर रहे हों। इसका मतलब सिर्फ़ स्पष्ट आउटेज से बचना नहीं, बल्कि चुपचाप होने वाली ब्रेकेज़ जैसे खाली स्क्रीन, गलत मान, जॉब क्रैश या लंबे लॉक के कारण राइट्स ब्लॉक होना भी रोकना है।
क्योंकि आपकी सिस्टम की कई हिस्से डेटाबेस की आकृति पर निर्भर होते हैं, सिर्फ़ माइग्रेशन का सफल होना ही पर्याप्त नहीं। बैकग्राउंड जॉब, रिपोर्ट, एडमिन स्क्रिप्ट, इंटीग्रेशन और पुराने मोबाइल ऐप अक्सर नए स्कीमा आने के बाद भी पुराने फील्ड भेजते या पढ़ते रहते हैं।
पुराने मोबाइल बिल्ड्स कई हफ्तों तक सक्रिय रह सकते हैं, और कुछ क्लाइंट बाद में पुराने अनुरोधों को फिर से भेजते हैं। इसलिए आपका API कुछ समय के लिए दोनों—पुराने और नए—पेलोड स्वीकार करने में सक्षम होना चाहिए ताकि मिक्स्ड वर्शन बिना त्रुटियों के साथ रह सकें।
ऐडिटिव परिवर्तन आमतौर पर सबसे सुरक्षित होते हैं क्योंकि पुरानी स्कीमा बनी रहती है और पुराने कोड टूटते नहीं। रीनेम्स और डिलीशंस जोखिमभरे होते हैं क्योंकि वे किसी चीज़ को हटा देते हैं जिसे पुराने क्लाइंट अभी भी पढ़ या लिख रहे हों।
पहले कॉलम को nullable बनाकर जोड़ें ताकि पुराने कोड बिना नई जानकारी के भी रो जोड़ सके। फिर बैकफिल करें—छोटे बैचों में—और जब कवरेज उच्च हो और नए राइट्स लगातार हों तभी NOT NULL लागू करें।
इसे एक रोलआउट की तरह ट्रीट करें: कम्पैटिबल स्कीमा जोड़ें, दोनों वर्ज़न संभालने वाला कोड डिप्लॉय करें, छोटे बैचों में बैकफिल करें, रीड्स स्विच करें पर फॉलबैक रखें, और तभी पुराना फील्ड हटाएँ जब यह साबित हो कि उसका उपयोग नहीं हो रहा।
छोटे ट्रांज़ैक्शनों में चलाएँ ताकि टेबल्स लॉक न हों या लोड न बढ़े। जॉब को री-स्टार्टेबल और आइडेम्पोटेंट बनाएं—सिर्फ़ उन्हीं रोज़ को अपडेट करें जिनमें नई वैल्यू मिसिंग है—और प्रगति रिकॉर्ड करें ताकि आप सुरक्षित रूप से.pause और resume कर सकें।
पहले नए फील्ड्स को वैकल्पिक बनाकर जोड़ें और सर्वर-साइड पर जब फ़ील्ड गायब हो तो डिफ़ॉल्ट लागू करें। पुराने व्यवहार को स्थिर रखें, मौजूदा फील्ड्स का अर्थ न बदलें, और दोनों पथों—"नया क्लाइंट भेजता है" और "पुराना क्लाइंट भेजता नहीं"—का परीक्षण करें।
आम तौर पर आप स्कीमा वापस नहीं करते; आप कोड रिवर्ट करते हैं और ऐडिटिव स्कीमा को जगह पर छोड़ते हैं। नए रीड्स पहले बंद करें, फिर नए राइट्स बंद करें, और बैकफिल्स को तब तक पॉज़ करें जब तक मेट्रिक्स स्थिर न हों।
यूज़र-इम्पैक्ट सिग्नल पर ध्यान दें: एरर रेट, स्लो क्वेरीज (p95/p99), राइट लेटेंसी, क्यू डेप्थ और डेटाबेस CPU/IO प्रेशर। हर फेज़ के बाद तभी आगे बढ़ें जब मेट्रिक्स स्थिर हों और नए फील्ड के लिए कवरेज अच्छी हो।


