कॉन्फ़िगरेशन, fixtures और स्थानीय डेटा के लिए JSON प्रबंधित करना
मुख्य बात: कॉन्फ़िगरेशन को परतों में और सत्यापित होना चाहिए; fixtures को निर्धारक, पठनीय, और diff-योग्य होना चाहिए। यदि इन दो प्रकार की फ़ाइलों को ख़राब तरीके से प्रबंधित किया जाए, तो समस्या-समाधान की लागत लंबे समय तक ऊँची रहती है।

मुख्य निष्कर्ष: फ़ाइल फ़ॉर्मेट की चिंता से पहले कॉन्फ़िगरेशन को डिफ़ॉल्ट, पर्यावरण, और स्थानीय ओवरराइड के रूप में बनाएँ और स्टार्टअप पर सत्यापन करें; fixtures को समय, ID, कुंजी क्रम, और इंडेंटेशन में निर्धारक बनाएँ ताकि वे diff-योग्य रहें, और केवल तभी मास्किंग स्वचालित करें।
ये दो प्रकार की JSON व्यावसायिक तर्क में भाग नहीं लेतीं, फिर भी वे तय करती हैं कि "नए सहकर्मी को चालू होने में कितना समय लगता है" और "उत्पादन समस्या ढूँढने में कितना समय लगता है"।
1. कॉन्फ़िगरेशन: तीन-परत संरचना
| परत | स्रोत | बनाए रखने वाला | कमिट |
|---|---|---|---|
| डिफ़ॉल्ट | कोड में स्थिरांक / default.json |
डेवलपर | हाँ |
| पर्यावरण कॉन्फ़िगरेशन | config/production.json, पर्यावरण चर |
ऑपरेशन्स | हाँ (रहस्य बिना) |
| स्थानीय ओवरराइड | .env.local, config/local.json |
व्यक्ति | नहीं (gitignored) |
मर्ज क्रम: डिफ़ॉल्ट → पर्यावरण → स्थानीय, हर बाद की परत पिछली को ओवरराइड करती है। केवल व्यवसाय को जितनी गहराई चाहिए उतना ही मर्ज करें; आँख मूँदकर डीप-मर्ज न करें — डीप मर्ज "ऐरे अप्रत्याशित रूप से जुड़ गए" जैसी समस्याओं को बेहद कठिन बना देता है।
/**
* कॉन्फ़िगरेशन परतों को प्राथमिकता से मर्ज करता है; बाद की परतें पहले वाली को ओवरराइड करती हैं, और ऐरे जोड़ने के बजाय पूरी तरह बदले जाते हैं।
* @param layers सबसे कम से सबसे अधिक प्राथमिकता वाली कॉन्फ़िगरेशन परतें
* @returns मर्ज किया गया कॉन्फ़िगरेशन ऑब्जेक्ट
*/
function mergeConfigLayers(layers: Array<Record<string, unknown>>): Record<string, unknown> {
return layers.reduce<Record<string, unknown>>((mergedConfig, currentLayer) => {
for (const [configKey, configValue] of Object.entries(currentLayer)) {
const isPlainObject =
typeof configValue === 'object' && configValue !== null && !Array.isArray(configValue);
const existingValue = mergedConfig[configKey];
if (isPlainObject && typeof existingValue === 'object' && existingValue !== null) {
mergedConfig[configKey] = mergeConfigLayers([
existingValue as Record<string, unknown>,
configValue as Record<string, unknown>,
]);
continue;
}
// ऐरे और स्केलर हमेशा पूरी तरह बदले जाते हैं ताकि संयोगवश एलिमेंट जुड़ने से बचा जाए
mergedConfig[configKey] = configValue;
}
return mergedConfig;
}, {});
}
2. स्टार्टअप पर सत्यापित करें, runtime तक न रुकें
कॉन्फ़िगरेशन त्रुटि की लागत उसके देर से पकड़े जाने के साथ तेज़ी से बढ़ती है:
| कब पकड़ी जाती है | लागत |
|---|---|
| स्टार्टअप पर | एक लॉग पंक्ति, रीस्टार्ट करके आगे बढ़ें |
| पहले अनुरोध पर | कुछ सुविधाएँ विफल, एक घंटे की डिबगिंग |
| जब निर्धारित जॉब चलती है | आधी रात में अलर्ट |
| सेटलमेंट के दिन | मिलान की घटना |
import Ajv2020 from 'ajv/dist/2020';
import configSchema from './config.schema.json' with { type: 'json' };
const ajv = new Ajv2020({ allErrors: true, strict: true });
/**
* कॉन्फ़िगरेशन सत्यापित करता है और विफलता पर तुरंत throws करता है, fail fast लागू करता है।
* @param rawConfig मर्ज किया गया कच्चा कॉन्फ़िगरेशन
* @returns सत्यापित कॉन्फ़िगरेशन
*/
export function assertConfig(rawConfig: unknown): Record<string, unknown> {
const validate = ajv.compile(configSchema);
if (!validate(rawConfig)) {
const messages = (validate.errors ?? []).map(
(errorItem) => `${errorItem.instancePath || '/'} ${errorItem.message ?? ''}`,
);
throw new Error(`配置校验失败:\n${messages.join('\n')}`);
}
return rawConfig as Record<string, unknown>;
}
सहायक सिद्धांत:
- अनिवार्य मद गायब होने पर तुरंत बाहर निकलें; "प्रशंसनीय दिखने वाला" डिफ़ॉल्ट न दें (जैसे डेटाबेस पता
localhostपर डिफ़ॉल्ट करना); - प्रकार त्रुटियों पर तुरंत बाहर निकलें; स्ट्रिंग
"false"बूलियनfalseनहीं है, इसलिए पर्यावरण चर पढ़े जाने के बाद स्पष्ट रूप से रूपांतरित होने चाहिए; - रहस्य कभी रिपॉज़िटरी में न जाएँ; उन्हें केवल पर्यावरण चर या रहस्य प्रबंधन सेवा से पढ़ें।
3. कॉन्फ़िगरेशन फ़ाइलों के लिए कौन-सा फ़ॉर्मेट
| फ़ाइल | फ़ॉर्मेट | कारण |
|---|---|---|
package.json |
मानक JSON | पैकेज मैनेजर केवल इसे स्वीकार करता है |
tsconfig.json |
JSONC | टिप्पणियाँ अनुमत, आधिकारिक रूप से समर्थित |
.vscode/settings.json |
JSONC | ऊपर जैसा ही |
| व्यावसायिक कॉन्फ़िगरेशन | YAML या JSONC | हर मद का अर्थ समझाने वाली टिप्पणियाँ चाहिए |
| पर्यावरण चर सूची | .env |
समतल कुंजी-मान जोड़े, इंजेक्ट करना आसान |
एक व्यावहारिक परीक्षण: क्या यह फ़ाइल ग़ैर-मानवीय उपकरणों द्वारा पढ़ी जाएगी? यदि हाँ → मानक JSON; यदि केवल आपका अपना कोड इसे पढ़ता है → कोई भी बोली ठीक है।
4. परीक्षण fixtures: निर्देशिकाएँ और नामकरण
tests/fixtures/
├─ valid/
│ ├─ order-minimal.json
│ ├─ order-full.json
│ └─ order-with-null-fields.json
├─ invalid/
│ ├─ order-missing-id.json
│ ├─ order-price-as-number.json
│ └─ order-nested-broken.json
└─ edge/
├─ big-integer.json
├─ emoji-and-surrogate.json
└─ deep-nesting-64.json
एक अच्छा नामकरण सम्मेलन object-scenario है: order-missing-field test1.json से कहीं अधिक उपयोगी है। ग़ैर-ASCII फ़ाइल नाम क्रॉस-प्लेटफ़ॉर्म टूलचेन में कभी-कभी एन्कोडिंग समस्याएँ पैदा करते हैं, इसलिए फ़ाइल नाम अंग्रेज़ी में रखें जबकि सामग्री चीनी में रह सकती है।
4.1 तीन प्रकार के केस, तीन प्रकार की अपेक्षाएँ
| निर्देशिका | अपेक्षा | उद्देश्य |
|---|---|---|
valid/ |
सत्यापन पास | मुख्य प्रवाह, प्रकार उत्पन्न करना |
invalid/ |
सत्यापन विफल, और त्रुटि अपेक्षित फ़ील्ड की ओर इशारा करे | त्रुटि प्रबंधन, संदेश प्रतिलिपि |
edge/ |
व्यवहार स्पष्ट (पास या विफल ठीक है, लेकिन असर्ट होना चाहिए) | रिग्रेशन सुरक्षा |
JSONTestSuite उन्हीं तीन प्रकारों के लिए y_ / n_ / i_ उपसर्गों का उपयोग करता है (स्वीकार होना चाहिए / अस्वीकार होना चाहिए / कार्यान्वयन-निर्धारित), और आप वह नामकरण सम्मेलन सीधे उधार ले सकते हैं।
5. निर्धारकता: fixtures को diff-योग्य बनाएँ
अस्थिर fixtures कोड समीक्षा को बेकार बना देते हैं:
| शोर का स्रोत | कैसे संभालें |
|---|---|
| वर्तमान समय | इसे एक स्थिरांक पर तय करें, जैसे 2026-01-01T00:00:00Z |
| यादृच्छिक ID / UUIDs | वृद्धिशील अनुक्रम या निश्चित मानों का उपयोग करें |
| कुंजी क्रम | उत्पन्न करते समय वर्णमाला क्रम में सॉर्ट करें |
| फ़्लोट फ़ॉर्मेटिंग | समान रूप से दो दशमलव रखें या स्ट्रिंग में बदलें |
| समय क्षेत्र | हर जगह UTC का उपयोग करें |
| इंडेंटेशन | 2 स्पेस पर तय, CI में Prettier से जाँचें |
/**
* स्थिर, diff-योग्य JSON टेक्स्ट बनाने के लिए कुंजियों को पुनरावर्ती रूप से सॉर्ट करता है।
* @param value सीरियलाइज़ करने का मान
* @param indentWidth इंडेंटेशन चौड़ाई
* @returns स्थिर क्रम वाली JSON स्ट्रिंग
*/
function toStableJson(value: unknown, indentWidth = 2): string {
/**
* ऑब्जेक्ट कुंजियों को पुनरावर्ती रूप से सॉर्ट करता है।
* @param input कोई भी JSON मान
* @returns सॉर्ट की गई कुंजियों वाला नया मान
*/
const sortKeysDeep = (input: unknown): unknown => {
if (Array.isArray(input)) {
return input.map((item) => sortKeysDeep(item));
}
if (typeof input === 'object' && input !== null) {
return Object.fromEntries(
Object.keys(input)
.sort()
.map((entryKey) => [entryKey, sortKeysDeep((input as Record<string, unknown>)[entryKey])]),
);
}
return input;
};
return JSON.stringify(sortKeysDeep(value), null, indentWidth);
}
6. मास्किंग और अनुपालन
वास्तविक डेटा सीधे fixtures के रूप में उपयोग नहीं किया जा सकता:
- नाम → "John Doe" और "Jane Roe" जैसे प्लेसहोल्डर;
- फ़ोन नंबर → स्पष्ट रूप से काल्पनिक नंबर जैसे
13800138000; - ID कार्ड नंबर → सही फ़ॉर्मेट लेकिन अवैध चेक डिजिट वाले नकली नंबर;
- पते → प्रांत/शहर संरचना रखें, मकान नंबर हटाएँ;
- राशियाँ → परिमाण-कोटि रखें, लेकिन असली खाते की जानकारी कभी न रखें।
टीम के पास scripts/mask-fixture.mjs होना सबसे अच्छा है जो निर्यात किए गए उत्पादन नमूने पर चलकर मास्क किया गया संस्करण बनाए, जिससे "तीन जगह हाथ से बदलो, बाक़ी भूल जाओ" से बचा जाए।
7. माइग्रेशन और वर्ज़न
Fixtures भी पुराने हो जाते हैं। fixture में schemaVersion फ़ील्ड जोड़ें, या निर्देशिका में VERSION फ़ाइल रखें:
{
"schemaVersion": 3,
"id": "1001",
"amount": "89.50"
}
जब API कॉन्ट्रैक्ट अपग्रेड हो:
- नई fixture निर्देशिका (
v3/) जोड़ें; पुरानी fixtures को मौजूदा जगह संशोधित न करें; - पुराने API वर्ज़न के सेवानिवृत्त होने तक पुरानी fixtures को रिग्रेशन के लिए रखें;
- पश्च-संगतता की पुष्टि के लिए CI में दोनों वर्ज़न चलाएँ।
8. चेकलिस्ट
- [ ] तीन-परत कॉन्फ़िगरेशन संरचना स्पष्ट है, और स्थानीय ओवरराइड कमिट नहीं होते;
- [ ] सत्यापन स्टार्टअप पर होता है, और विफलता पर तुरंत बाहर निकलता है;
- [ ] रहस्य केवल पर्यावरण चर से आते हैं; कॉन्फ़िगरेशन फ़ाइलों में केवल प्लेसहोल्डर हैं;
- [ ] Fixtures
valid/invalid/edgeनिर्देशिकाओं में विभाजित हैं, और नाम परिदृश्य व्यक्त करते हैं; - [ ] Fixtures निर्धारक (निश्चित समय, ID, और क्रम) और diff-योग्य हैं;
- [ ] वास्तविक डेटा मास्किंग स्क्रिप्ट से संसाधित हुआ है;
- [ ] कॉन्ट्रैक्ट अपग्रेड नई वर्ज़न निर्देशिका जोड़ते हैं, मौजूदा जगह संपादित नहीं करते।
FAQ
कॉन्फ़िगरेशन परतों को कैसे क्रमबद्ध और मर्ज करना चाहिए?
तीन परतें उपयोग करें — डिफ़ॉल्ट, पर्यावरण कॉन्फ़िगरेशन, और स्थानीय ओवरराइड — डिफ़ॉल्ट → पर्यावरण → स्थानीय क्रम में मर्ज करें, हर बाद की परत पिछली को ओवरराइड करे। केवल व्यवसाय को जितनी गहराई चाहिए उतना ही मर्ज करें और ऐरे व स्केलर पूरी तरह बदलें, ताकि डीप मर्ज ऐरे को अप्रत्याशित रूप से न जोड़ दे। स्थानीय ओवरराइड फ़ाइल को gitignore करें, व्यक्तिगत सेटिंग्स कमिट न करें।
कॉन्फ़िगरेशन को स्टार्टअप पर क्यों सत्यापित करना चाहिए?
कॉन्फ़िगरेशन त्रुटि की लागत उसके देर से पकड़े जाने के साथ तेज़ी से बढ़ती है: स्टार्टअप पर एक लॉग पंक्ति बनाम सेटलमेंट के दिन मिलान की घटना। स्टार्टअप पर JSON Schema के विरुद्ध सत्यापित करें और अनिवार्य मद या प्रकार त्रुटियों पर तुरंत बाहर निकलें, "प्रशंसनीय दिखने वाला" डिफ़ॉल्ट देने के बजाय।
क्या उत्पादन डेटा सीधे परीक्षण fixtures के रूप में उपयोग करना चाहिए?
नहीं। नाम, फ़ोन नंबर, ID कार्ड नंबर, पते, और राशियाँ सभी को प्लेसहोल्डर या स्पष्ट रूप से काल्पनिक मान चाहिए, और टीम के पास scripts/mask-fixture.mjs होना चाहिए जो उत्पादन निर्यात पर चलकर मास्क किया गया संस्करण बनाए, जिससे कोई चीज़ छोड़ देने वाले हाथ के संपादन से बचा जाए।