ज़्यादातर लोग जो ड्रॉपशिपिंग स्टोर खोलते हैं, वे शॉर्टकट की तलाश में रहते हैं। वे विनिंग प्रोडक्ट्स (winning products) खोजने के लिए फ़ोरम देखते हैं, सस्ते वर्चुअल असिस्टेंट्स को काम पर रखते हैं, और उम्मीद करते हैं कि एल्गोरिदम उन्हें रातों-रात अमीर बना देगा। मुझे यह कभी आकर्षित नहीं किया। मैंने ड्रॉपशिपिंग को एक इंजीनियरिंग समस्या के रूप में देखा। मैं जल्दी पैसा कमाने के पीछे नहीं भाग रहा था। मैं इन्वेंट्री सिंकिंग (inventory syncing) की समस्या सुलझाना चाहता था, ऐसे प्राइसिंग एल्गोरिदम बनाना चाहता था जो बाज़ार के वास्तविक बदलावों पर प्रतिक्रिया दें, और बिना मानसिक तनाव के सप्लायर APIs के साथ काम करना चाहता था। स्टोर उस सिस्टम का एक साइड इफेक्ट बन गया जिसे मैंने Node.js और PostgreSQL का उपयोग करके बनाया था।

स्टोर को एक बैकएंड सर्विस की तरह मानें

जिस क्षण आप ड्रॉपशिपिंग को मार्केटिंग के जुगाड़ के रूप में सोचना बंद कर देते हैं और इसे एक डिस्ट्रिब्यूटेड सिस्टम्स (distributed systems) की चुनौती की तरह मानना शुरू करते हैं, समस्याएँ दिलचस्प हो जाती हैं। जब तीन अलग-अलग सप्लायर आपके स्टॉक को नियंत्रित करते हैं, तो आप स्टोरफ्रंट को सटीक कैसे रखते हैं? जब वे सप्लायर आपको बताए बिना अपनी लागत बदल देते हैं, तो आप प्रतिस्पर्धी मूल्य निर्धारण (competitive pricing) कैसे करते हैं? आप एक ऐसे कैटलॉग को कैसे संभालते हैं जो स्प्रेडशीट्स में डूबने के बजाय पचास SKUs से बढ़कर पाँच हज़ार हो जाता है?

मैंने इन सवालों के जवाब देने के लिए एक पाइपलाइन बनाई। Node.js ने इवेंट-ड्रिवन आर्किटेक्चर (event-driven architecture) को संभाला क्योंकि मुझे एक साथ कई सप्लायर कनेक्शनों को संभालने के लिए नॉन-ब्लॉकिंग I/O की आवश्यकता थी। PostgreSQL 'सोर्स ऑफ ट्रुथ' (source of truth) के रूप में काम करता था। मुझे स्कीमा डिज़ाइन (schema design) की बहुत परवाह थी क्योंकि एक लापरवाही से बनाई गई इन्वेंट्री टेबल पहली बार तब मुसीबत बन जाती है जब आप किसी ऐसी चीज़ को ओवरसेल (oversell) कर देते हैं जो मौजूद ही नहीं है।

पाइपलाइन बनाना

मुख्य काम सरल था: सप्लायर APIs से प्रोडक्ट डेटा निकालना। व्यवहार में, इसका मतलब था SKUs, विवरण, इमेज, स्टॉक लेवल और प्राइसिंग को उन एंडपॉइंट्स (endpoints) से प्राप्त करना जो एक-दूसरे से बात करने के लिए कभी डिज़ाइन ही नहीं किए गए थे। मैंने Node.js में पोलिंग सर्विसेज (polling services) लिखीं जो अलग-अलग अंतराल पर सप्लायर फीड्स को हिट करती थीं। हमारे आंतरिक स्टोरफ्रंट डेटाबेस तक पहुँचने से पहले हर आने वाले पेलोड (payload) वैलिडेशन और मैपिंग लेयर्स से होकर गुजरता था।

मैंने प्रोडक्ट्स, वेरिएंट्स, प्राइसिंग हिस्ट्री और सिंक लॉग्स के लिए अलग-अलग टेबल्स के साथ PostgreSQL को स्ट्रक्चर किया। जब किसी सप्लायर ने चुपचाप किसी फ़ील्ड का नाम बदल दिया या जहाँ नंबर होना चाहिए था वहाँ 'null' भेज दिया, तो पाइपलाइन ने उसे पकड़ लिया और स्टोरफ्रंट को खराब करने के बजाय एक फेलियर रिकॉर्ड लिख दिया। मैं एक लॉग रो (log row) देखकर ठीक से जान सकता था कि कौन सा एंडपॉइंट टूटा, वह किस समय हुआ, और कौन से फ़ील्ड्स गलत थे। उस ऑब्जर्वेबिलिटी (observability) ने मुझे एक से अधिक बार बचाया जब किसी सप्लायर ने वीकेंड पर अपने API को "अपग्रेड" करने का फैसला किया।

क्या चीज़ें अच्छी तरह काम कर गईं

ऑटोमेशन ने बहुत सारा समय बचाया। शुरुआत में, मैंने मैन्युअल तरीका अपनाया: सप्लायर की स्प्रेडशीट्स डाउनलोड करना, उन्हें हाथ से साफ करना, इमेज फॉर्मेट करना और स्टोर पर CSV अपलोड करना। जैसे ही कैटलॉग कुछ दर्जन आइटम्स से आगे बढ़ा, यह असंभव हो गया। ऑटोमेटेड पाइपलाइन ने बिना किसी स्प्रेडशीट को छुए नई लिस्टिंग, प्राइस अपडेट और स्टॉक एडजस्टमेंट को संभाल लिया।

प्रोडक्ट डिस्क्रिप्शन को स्केल करना टेम्प्लेट्स के माध्यम से हुआ। लगभग एक जैसी पाँच सौ चीज़ों के लिए अलग-अलग गद्य (prose) लिखना टिकाऊ नहीं है। इसके बजाय, मैंने एक टेम्प्लेटिंग लेयर बनाई जो मटेरियल, डाइमेंशन या कलर जैसे सप्लायर एट्रिब्यूट्स को लेती थी और उन्हें स्ट्रक्चर्ड डिस्क्रिप्शन ब्लॉक्स में डाल देती थी। आउटपुट इतना साफ था कि उसे कन्वर्ट किया जा सके और इतना सुसंगत था कि एक हज़ार नए SKUs जोड़ने के लिए किसी मैन्युअल कॉपीराइटिंग की आवश्यकता नहीं थी।

प्राइस मॉनिटरिंग ने भी मेरी उम्मीदों से बेहतर काम किया। मैंने एक हल्का मॉनिटरिंग लेयर बनाया जो प्रमुख उत्पादों के एक हिस्से पर प्रतिस्पर्धी कीमतों को ट्रैक करता था। जब इसने बदलावों का पता लगाया, तो सिस्टम ने मेरे द्वारा कॉन्फ़िगर किए गए गार्डरेल्स (guardrails) के भीतर स्वचालित रूप से हमारे मार्जिन को एडजस्ट कर दिया। यदि किसी सप्लायर ने थोक लागत (wholesale cost) कम कर दी, तो लिस्टिंग प्राइस दिनों के बजाय मिनटों में उस बदलाव को दर्शा सकता था। उस रिस्पॉन्सिवनेस ने कम मार्जिन वाले आइटम्स पर काफी अंतर पैदा किया।

क्या टूटा और क्यों

सप्लायर APIs में निरंतरता (consistency) की कमी होती है। यह कोई शिकायत नहीं है; यह एक जमीनी हकीकत है। एक पार्टनर प्रेडिक्टेबल पेजिनेशन (predictable pagination) के साथ साफ JSON देता है। दूसरा सोमवार को camelCase टैग्स के साथ XML देता है और बुधवार को snake_case के साथ। रेट लिमिट्स उदार से लेकर दंडात्मक (punitive) तक भिन्न होती हैं। डाउनटाइम की सूचना उचित स्टेटस कोड के बजाय HTML एरर पेजों के माध्यम से दी जाती है। अंत में, आप उन एंडपॉइंट्स के लिए डिफेंसिव पार्सर्स (defensive parsers) और रिट्राई लॉजिक (retry logic) लिखते हैं जो ऐसे व्यवहार करते हैं जैसे उन्हें 2003 में डिज़ाइन किया गया हो।

इन्वेंट्री सिंक में ऐसी रेस कंडीशंस (race conditions) थीं जिन्होंने मेरी नींद उड़ा दी। कल्पना कीजिए: दो ग्राहक एक-दूसरे के कुछ ही सेकंड के भीतर आखिरी यूनिट का ऑर्डर दे देते हैं, या कोई सप्लायर वेबहुक (supplier webhook) आपको ठीक उसी क्षण बताता है कि स्टॉक शून्य हो गया है जब कोई खरीदार चेकआउट पर क्लिक करता है। मेरा शुरुआती 'read-then-update' लॉजिक पूरी तरह विफल रहा। मुझे हाई-वेलोसिटी SKUs के लिए एटॉमिक PostgreSQL ट्रांजेक्शन (atomic PostgreSQL transactions) और पेसिमिस्टिक लॉकिंग (pessimistic locking) का उपयोग करके सिंक लेयर को फिर से लिखना पड़ा। यह कॉन्करेंसी (concurrency) का एक दर्दनाक और व्यावहारिक सबक था, जिसके लिए कोई भी ट्यूटोरियल आपको तब तक तैयार नहीं कर सकता जब तक दांव पर असली पैसा न लगा हो।

मेरी सबसे बड़ी विफलता कस्टमर सपोर्ट ऑटोमेशन (customer support automation) को नज़रअंदाज़ करना था। मैं डेटा पाइपलाइन्स (data pipelines) को लेकर जुनूनी था और मानवीय परिणामों को बाद की बात मानकर छोड़ दिया। ऑर्डर देर से आए। सप्लायर्स ने गलत रंग भेज दिया। ग्राहकों ने ईमेल भेजे जो मेरे इनबॉक्स में घंटों पड़े रहे जबकि मैं API टाइमआउट (API timeouts) को डीबग कर रहा था। मेरे पास न तो टिकट रूटिंग (ticket routing) थी, न ही ऑटोमेटेड रिस्पॉन्स (automated responses) और न ही चैटबॉट हैंडऑफ (chatbot handoffs)। तकनीकी इंफ्रास्ट्रक्चर मजबूत था। मानवीय इंफ्रास्ट्रक्चर की कमी थी, और उस अंतर ने व्यवसाय को किसी भी अस्थिर वेबहुक (flaky webhook) की तुलना में कहीं अधिक नुकसान पहुँचाया।

एक इंजीनियर की तरह इमेज टेस्टिंग करना

मैंने प्रोडक्ट इमेज पर एक साइड एक्सपेरिमेंट (side experiment) किया। मैंने सेशन-आधारित बकेटिंग (session-based bucketing) से जुड़े सरल URL पैरामीटर रूटिंग का उपयोग करके अलग-अलग उपयोगकर्ताओं को अलग-अलग हीरो इमेज (hero images) दिखाईं। एक वेरिएंट में प्रोडक्ट को सादे सफेद बैकग्राउंड पर दिखाया गया था। दूसरे में इसे एक वास्तविक डेस्क पर लाइफस्टाइल सेटिंग (lifestyle setting) में दिखाया गया था। मैंने सीधे ऑर्डर फ्लो (order flow) से जुड़े बेसिक इवेंट लॉगिंग (event logging) का उपयोग करके प्रत्येक बकेट के लिए कन्वर्जन रेट (conversion rates) को ट्रैक किया।

छोटे बदलावों ने एंगेजमेंट (engagement) में सुधार किया। लाइफस्टाइल शॉट्स हमेशा नहीं जीते, लेकिन जब वे जीते, तो उनका प्रभाव इतना महत्वपूर्ण था कि उसने मेरी प्राथमिकता तय करने के तरीके को बदल दिया।