ब्रेकिंग API बदलाव, समझाए गए
अंतिम अपडेट:
ब्रेकिंग API बदलाव किसी API के प्रकाशित अनुबंध में किया गया कोई भी ऐसा बदलाव है जो पुराने अनुबंध के अनुसार सही ढंग से लिखे गए क्लाइंट को, क्लाइंट के कुछ भी बदले बिना, नए अनुबंध के सामने विफल कर सकता है। यह एक वाक्य ही पूरा सिद्धांत है; इस लेख का बाकी सब कुछ उसके परिणाम हैं: कौन से बदलाव गिने जाते हैं, कौन से प्रसिद्ध जाल हानिरहित दिखते हैं लेकिन हैं नहीं, डिप्लॉय होने से पहले स्पेसिफिकेशन अपग्रेड की समीक्षा कैसे करें, और संस्करण रणनीतियाँ क्लाइंट के लिए समय कैसे खरीदती हैं। इसे दो वास्तविक दस्तावेज़ों पर लागू करने के लिए, पुरानी और नई स्पेसिफिकेशन पर OpenAPI Diff चलाएं और इस पेज के साथ-साथ निर्णय पढ़ें।
अनुबंध भविष्य के बारे में एक वादा है
API स्पेसिफिकेशन आकस्मिक अर्थ में दस्तावेज़ीकरण नहीं है; यह एक वादा है। जब कोई स्पेसिफिकेशन घोषित करती है कि GET /pets एक वैकल्पिक limit पैरामीटर स्वीकार करता है और एक स्ट्रिंग name वाले ऑब्जेक्ट के साथ 200 का उत्तर देता है, तो हर क्लाइंट लेखक ठीक उसी पर निर्माण करता है: ज़रूरत न हो तो पैरामीटर छोड़ दिया जाता है, स्थिति पर शाखा बनती है, फ़ील्ड को स्ट्रिंग के रूप में पढ़ा जाता है। क्लाइंट की शुद्धता वादे के आधार पर आंकी जाती है, इसलिए ब्रेकिंग की परिभाषा असममित है — कोई बदलाव ब्रेकिंग है यदि एक सही पुराना क्लाइंट विफल हो सकता है, भले ही अधिकांश क्लाइंट संयोग से बच जाएं। पुरानी स्पेसिफिकेशन से जनरेट किया गया एक सख्त SDK, बदले हुए प्रकार पर विफल होता हुआ, पर्याप्त है: टूटा वादा था, क्लाइंट नहीं।
यह ढांचा इस बारे में अधिकांश बहसों को सुलझा देता है कि कोई बदलाव «गिना जाता है» या नहीं। एंडपॉइंट हटाना ब्रेकिंग है क्योंकि वादे में उसका अस्तित्व शामिल था। एंडपॉइंट जोड़ना नहीं है, क्योंकि किसी पुराने क्लाइंट ने उन पाथ के बारे में खुद से कोई वादा नहीं किया जिन्हें वह कभी कॉल नहीं करता। एक नया वैकल्पिक रिस्पॉन्स फ़ील्ड सुरक्षित है क्योंकि पुराने पार्सर अज्ञात फ़ील्ड को अनदेखा करते हैं — किसी भी समझदार प्रारूप में, केवल-जोड़ आउटपुट किसी पाठक को अमान्य नहीं कर सकता। इसके विपरीत, एक नया अनिवार्य इनपुट हर मौजूदा अनुरोध तक पहुँचता है और उन्हें अमान्य कर देता है जो उसे छोड़ देते हैं। परीक्षण हमेशा एक ही है: कल के वैध ट्रैफ़िक को कल के नियमों के सामने फिर से चलाएं और पूछें कि अब क्या विफल होता है।
व्यवहार में दो परिणाम मायने रखते हैं। पहला, अनुबंध वह है जो प्रकाशित है, न कि वह जो सर्वर आज संयोग से करता है: ट्रैफ़िक को सूंघकर क्लाइंट द्वारा खोजा गया अदस्तावेज़ीकृत फ़ील्ड वादा नहीं किया गया है, और उसे हटाना असभ्य है लेकिन, सख्ती से, ब्रेकिंग नहीं — जो इस बात का तर्क है कि केवल वही दस्तावेज़ करें जिसे आप बनाए रखने का इरादा रखते हैं, और कुछ नहीं। दूसरा, अनुबंध उन मानकों की पूरी सतह को शामिल करता है जिन पर वह खड़ा है: RFC 9110 द्वारा तय HTTP विधि और स्थिति अर्थशास्त्र वादे का हिस्सा है, इसलिए 200 या 404 का अर्थ फिर से परिभाषित करना एक अनुबंध बदलाव है, भले ही स्पेसिफिकेशन फ़ाइल स्थिति अर्थों का कभी उल्लेख न करती हो।
क्लासिक जाल
सबसे आम प्रोडक्शन ब्रेक है अनिवार्य फ़ील्ड जोड़ना। एक टीम अनुरोध बॉडी में एक फ़ील्ड जोड़ती है, नए व्यावसायिक नियम को उसकी ज़रूरत होने के कारण उसे अनिवार्य चिह्नित करती है, और शिप कर देती है — जिसके बाद हर मौजूदा क्लाइंट, जिनमें से कोई भी वह फ़ील्ड नहीं भेजता, 400 पाने लगता है। सुरक्षित संस्करण उबाऊ हैं: फ़ील्ड को सर्वर-साइड डिफ़ॉल्ट के साथ वैकल्पिक के रूप में जोड़ें, या उसे केवल नए एंडपॉइंट पर अनिवार्य करें। दूसरा जाल है enum मान हटाना: एक स्थिति मान, एक श्रेणी, एक क्रम क्रम नए UI द्वारा अब पेश न किए जाने के कारण सेवानिवृत्त कर दिया जाता है, जबकि संग्रहीत क्लाइंट डेटा, पुराने बिल्ड पर मोबाइल ऐप और साझेदार एकीकरण वर्षों तक उसे भेजते रहते हैं। Enum मानों को स्वीकार करते रहने की लागत लगभग शून्य है; एक को वापस लेना एंडपॉइंट हटाने जैसा माना जाना चाहिए।
प्रकार बदलाव तीसरा जाल है, और वे संकीर्णन और विस्तारण के भेष में आते हैं। संकीर्णन — स्ट्रिंग से enum, पूर्णांक से छोटे दायरे, मुक्त-रूप ऑब्जेक्ट से निश्चित आकार — उन इनपुट को अस्वीकार करता है जो पहले वैध थे, और चेहरे पर ही ब्रेकिंग है। विस्तारण अधिक सूक्ष्म है: किसी रिस्पॉन्स फ़ील्ड को स्ट्रिंग से स्ट्रिंग-या-शून्य में बदलना, या पूर्णांक id को स्ट्रिंग id में बदलना, उदार दिखता है और हर सख्त पार्सर और पुराने प्रकार के लिए बनाए गए जनरेट किए गए SDK को तोड़ देता है। रिस्पॉन्स पक्ष पर एक दर्पण जाल है: एक फ़ील्ड जो हमेशा मौजूद था, वैकल्पिक हो जाता है। क्लाइंट उसे बिना शर्त पढ़ते हैं; उसे छोड़ने वाला पहला रिस्पॉन्स किसी और के कोडबेस में एक null पॉइंटर है।
चौथा जाल अर्थगत है, और कोई differ उसे पकड़ नहीं सकता: फ़ील्ड अपना नाम और प्रकार रखता है और अपना अर्थ बदल देता है। एक कीमत जो चुपचाप सेंट से डॉलर में बदल जाती है, एक टाइमस्टैम्प जो UTC से स्थानीय समय में चला जाता है, एक सूची जो क्रमबद्ध थी और अब नहीं है — स्पेसिफिकेशन diff खाली है और हर क्लाइंट गलत है। अर्थगत ब्रेक ही वह कारण है कि यांत्रिक diff समीक्षा के लिए छत नहीं, फर्श है: यह उन बदलावों को हटा देता है जो किसी को नहीं तोड़ सकते, ताकि मानवीय ध्यान उन बदलावों पर केंद्रित हो जो बस शायद तोड़ सकते हैं।
डिप्लॉय से पहले वेंडर स्पेसिफिकेशन अपग्रेड की समीक्षा
वेंडर लगातार नए स्पेसिफिकेशन संस्करण प्रकाशित करते हैं, और «SDK बढ़ाओ और फिर से डिप्लॉय करो» वह तरीका है जिससे शांत ब्रेक प्रोडक्शन तक पहुँचते हैं। एक अनुशासित समीक्षा में पंद्रह मिनट लगते हैं। पहला, दोनों दस्तावेज़ प्राप्त करें — वह स्पेसिफिकेशन जिसके विरुद्ध आपका डिप्लॉय आज वास्तव में चलता है, और उम्मीदवार — और परिवर्तन लॉग पढ़ने के बजाय उन्हें यांत्रिक रूप से तुलना करें, क्योंकि परिवर्तन लॉग विपणन दस्तावेज़ हैं: वे सुविधाएँ सूचीबद्ध करते हैं, हटाए गए नहीं। OpenAPI Diff वह सूची स्थानीय रूप से बनाता है, जो तब मायने रखता है जब API आंतरिक हो और उसकी स्पेसिफिकेशन किसी अजनबी के होस्ट किए गए टूल में पेस्ट नहीं होनी चाहिए।
दूसरा, ब्रेकिंग सूची को अपने स्वयं के उपयोग के विरुद्ध एक चेकलिस्ट के रूप में काम करें। वेंडर के अधिकांश ब्रेक आपके लिए सैद्धांतिक हैं: एक हटाया गया एंडपॉइंट जिसे आप कभी कॉल नहीं करते, एक संसाधन पर नया अनिवार्य फ़ील्ड जिसे आप कभी नहीं बनाते। खतरनाक प्रविष्टियाँ वे हैं जो आपके ट्रैफ़िक से प्रतिच्छेद करती हैं — यही कारण है कि समीक्षा को केवल diff नहीं, आपकी कॉल सूची चाहिए। तीसरा, अदस्तावेज़ीकृत सतह की जाँच करें: जिन स्थितियों पर आप शाखा बनाते हैं, जिन त्रुटि आकारों को आप पार्स करते हैं, जिन हेडर को आप पढ़ते हैं। वही आदत जो webhook उपभोक्ताओं की रक्षा करती है — पेलोड अनुबंध को पिन करना और परीक्षण करना, जैसा कि webhook हस्ताक्षर, समझाए गए में है — किसी भी अनुबंध पर लागू होती है जिसे कोई और नियंत्रित करता है।
अंत में, इसे चरणों में लाएं। एक स्टेजिंग क्लाइंट को नए संस्करण की ओर इंगित करें, रिकॉर्ड किए गए प्रोडक्शन अनुरोधों को फिर से चलाएं (एक सैनिटाइज़ किया गया कैप्चर, जैसा कि HAR गाइड निर्धारित करता है, वैसा ही संभाला गया), और किसी भी उपयोगकर्ता से पहले रिस्पॉन्स की तुलना करें। यदि वेंडर पदावनति अवधि या संस्करण पिन प्रदान करता है, तो उसे लें: सबसे सस्ता ब्रेकिंग बदलाव वह है जिसे आप अपनी गति से अपनाते हैं, अपने क्लाइंट तैयार होने के बाद, न कि वेंडर के रिलीज़ दिन पर।
संस्करण रणनीतियाँ: URL, हेडर, या कभी न तोड़ना
ब्रेकिंग बदलाव का क्लासिक उत्तर एक संस्करण पहचानकर्ता है, और क्लासिक बहस यह है कि उसे कहाँ रखा जाए। URL संस्करण (/v1/, /v2/) स्पष्ट है, कैश-अनुकूल है, तुच्छ रूप से रूट करने योग्य है और हर लॉग पंक्ति में दिखाई देता है; इसकी लागत यह है कि यह पूरे API को एक साथ संस्करणित करता है, इसलिए एक ब्रेकिंग एंडपॉइंट हर क्लाइंट को माइग्रेशन में घसीटता है। हेडर संस्करण — एक संस्करण हेडर या दिनांकित सामग्री वार्ता योजना — URL को साफ रखता है और अलग-अलग संसाधनों को संस्करणित कर सकता है, अदृश्यता की कीमत पर: संस्करण लॉग, लिंक और ब्राउज़र बार से गायब हो जाता है, और डिबगिंग पुरातत्व बन जाती है। दोनों में से कोई गलत नहीं है; उद्योग बस URL की लागत अधिक स्वेच्छा से चुकाता है क्योंकि सुबह 3 बजे संचालन क्षमता सुंदरता को हरा देती है।
गहरी रणनीति है संस्करणों की शायद ही कभी ज़रूरत पड़ना। केवल-जोड़ विकास — कभी न हटाएं, कभी अनिवार्य न करें, कभी प्रकार न बदलें; केवल वैकल्पिक चीज़ें जोड़ें — एक ही संस्करण को अनिश्चित काल तक जीवित रखता है, जो वास्तव में सबसे लंबे समय तक जीवित रहने वाले सार्वजनिक API का संचालन तरीका है। यह शुरू से ही अनुशासन मांगता है: उदार इनपुट सहिष्णुता, पहले दिन से स्ट्रिंग के रूप में टाइप किए गए id (ताकि दायरा बढ़ सके), विस्तारित होने के लिए डिज़ाइन किए गए enum, और एक पदावनति प्रक्रिया जो कुछ भी वापस लिए जाने से बहुत पहले चेतावनी देती है। अर्थ संस्करण, जैसा कि semver.org पर संहिताबद्ध है, वह संकेत परत प्रदान करता है जिसे OpenAPI Initiative की अपनी स्पेसिफिकेशन जानबूझकर प्रकाशकों पर छोड़ देती है: एक प्रमुख वृद्धि एक ब्रेक की घोषणा करती है, और क्लाइंट उसके विरुद्ध पिन कर सकते हैं।
जो भी रणनीति चुनी जाए, वह केवल यांत्रिकी द्वारा समर्थित एक सामाजिक अनुबंध के रूप में काम करती है: संस्करणों को घोषित अवधि के लिए वास्तव में साथ-साथ चलना चाहिए, पदावनतियों की घोषणा वहाँ की जानी चाहिए जहाँ क्लाइंट लेखक उन्हें देखेंगे (परिवर्तन लॉग, रिस्पॉन्स हेडर, बड़े उपभोक्ताओं को सीधी सूचना), और पुराने संस्करण के उपयोग को मापा जाना चाहिए ताकि «अब कोई इसका उपयोग नहीं करता» एक तथ्य हो, आशा नहीं। एक संस्करण जो प्रकाशित तो है लेकिन समानांतर में संचालित नहीं हो सकता, रणनीति नहीं है; यह ब्रेक का नाम बदलना है।
Differ कहाँ फिट बैठता है
एक यांत्रिक differ दो क्षणों में अपना स्थान अर्जित करता है। समीक्षा में, यह «क्या कुछ टूटा?» को साथ-साथ पढ़ने की एक दोपहर से एक ऐसी सूची में बदल देता है जिसे चेक किया जा सकता है — और, उतना ही उपयोगी रूप से, यह एक नकारात्मक को साबित करता है: जब कोई रिलीज़ केवल-जोड़ होने का दावा करता है, तो एक खाली ब्रेकिंग सूची सबूत है, भावना नहीं। निरंतर एकीकरण में, वही तुलना हर उस पुल अनुरोध पर चल सकती है जो स्पेसिफिकेशन को छूता है, और जब कोई ब्रेकिंग बदलाव परियोजना की नीति द्वारा आवश्यक संस्करण वृद्धि या अनुमोदन के बिना आता है तो बिल्ड को विफल कर सकती है। वर्गीकरण तालिका ही नीति है; टूल बस उसे समय सीमा पर भूलने देने से इनकार करता है।
इसकी सीमाएँ शेष वर्कफ़्लो को परिभाषित करती हैं। Differ केवल घोषित अनुबंध को पढ़ता है: यह व्यवहार, प्रदर्शन, अर्थशास्त्र या उन अदस्तावेज़ीकृत विचित्रताओं को नहीं देख सकता जिन पर क्लाइंट निर्भर करते हैं, और यह रोगात्मक रूप से गहरे स्कीमा को हमेशा के लिए विस्तारित करने के बजाय उनका सारांश बनाता है। इसलिए पूरा गेट एक पाइपलाइन है, एक पेज नहीं: अनुबंध के लिए यांत्रिक diff, व्यवहार के लिए वास्तविक क्लाइंट कॉल फिर से चलाने वाले अनुबंध परीक्षण, अर्थशास्त्र के लिए चरणबद्ध रोलआउट, और उस दिन के लिए निगरानी जब तीनों कुछ चूक जाते हैं। इस तरह उपयोग किए जाने पर, diff चारों परतों में सबसे सस्ती है — पंद्रह सेकंड जो सबसे शर्मनाक प्रकार की विफलता को हटा देते हैं, वह जिसमें ब्रेक हमेशा स्पेसिफिकेशन में लिखा था और किसी ने दस्तावेज़ों की तुलना नहीं की।
यही वह आदत है जिसके लिए यह लेख वास्तव में तर्क दे रहा है। स्पेसिफिकेशन रखना सस्ता है और तुलना करना और भी सस्ता; घटनाएँ नहीं हैं। आपके पास या आपके द्वारा उपभोग किए जाने वाले हर अनुबंध का हर प्रकाशित संस्करण रखें, शिप होने से पहले हर उम्मीदवार की तुलना प्रोडक्शन के संस्करण से करें, और ब्रेकिंग सूची को रिलीज़ का वास्तविक एजेंडा मानें। जो टीमें ऐसा करती हैं वे इस बहस को बंद कर देती हैं कि कोई बदलाव किसी को तोड़ना «चाहिए था» या नहीं — उनके पास सूची है, दिनांकित और संग्रहीत, उसके शिप होने से पहले की।