কনফিগারেশন, ফিক্সচার ও লোকাল ডেটার জন্য JSON সামলানো
সংক্ষেপে: কনফিগারেশন স্তরভিত্তিক ও যাচাইযোগ্য হতে হবে; ফিক্সচার নির্ধারক, পাঠযোগ্য ও ডিফযোগ্য হতে হবে। এই দুই ধরনের ফাইল খারাপভাবে সামলালে সমস্যা অনুসন্ধানের খরচ দীর্ঘদিন উঁচু থাকে।

মূল কথা: ফাইল ফরম্যাট নিয়ে ভাবনার আগেই কনফিগারেশনকে ডিফল্ট, এনভায়রনমেন্ট ও লোকাল ওভাররাইডে গড়ে স্টার্টআপে যাচাই করুন; ফিক্সচারকে সময়, ID, কি-ক্রম ও ইনডেন্টেশনে নির্ধারক করে ডিফযোগ্য রাখুন, আর কেবল তারপর মাস্কিং স্বয়ংক্রিয় করুন।
এই দুই ধরনের JSON ব্যবসায়িক লজিকে অংশ নেয় না, তবু তারাই ঠিক করে "নতুন সহকর্মীর চালু হতে কত সময় লাগে" আর "প্রোডাকশন সমস্যা খুঁজতে কত সময় লাগে"।
১. কনফিগারেশন: তিন স্তরের কাঠামো
| স্তর | উৎস | কে রক্ষণাবেক্ষণ করে | কমিট হয় |
|---|---|---|---|
| ডিফল্ট | কোডের ধ্রুবক / default.json |
ডেভেলপার | হ্যাঁ |
| এনভায়রনমেন্ট কনফিগ | config/production.json, এনভায়রনমেন্ট ভ্যারিয়েবল |
অপারেশন | হ্যাঁ (সিক্রেট ছাড়া) |
| লোকাল ওভাররাইড | .env.local, config/local.json |
ব্যক্তি | না (gitignored) |
মার্জ ক্রম: ডিফল্ট → এনভায়রনমেন্ট → লোকাল, প্রতিটি পরের স্তর আগেরটিকে ওভাররাইড করে। কেবল ব্যবসার প্রয়োজন যতটা, ততটা গভীরে মার্জ করুন; অন্ধভাবে গভীর মার্জ করবেন না — গভীর মার্জে "অ্যারে অপ্রত্যাশিতভাবে জোড়া লেগেছে" ধরনের সমস্যা ধরা অত্যন্ত কঠিন।
/**
* Merges configuration layers by priority; later layers override earlier ones, and arrays are replaced wholesale rather than concatenated.
* @param layers Configuration layers from lowest to highest priority
* @returns The merged configuration object
*/
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;
}
// Arrays and scalars are always replaced wholesale to avoid accidental element concatenation
mergedConfig[configKey] = configValue;
}
return mergedConfig;
}, {});
}
২. স্টার্টআপে যাচাই করুন, রানটাইম পর্যন্ত অপেক্ষা করবেন না
কনফিগারেশন ভুলের খরচ কখন ধরা পড়ছে তার সঙ্গে সূচকীয়ভাবে বাড়ে:
| কখন ধরা পড়ে | খরচ |
|---|---|
| স্টার্টআপে | একটি লগ লাইন, রিস্টার্ট করে এগিয়ে যাওয়া |
| প্রথম রিকোয়েস্টে | কিছু ফিচার এরর দেয়, এক ঘণ্টার ডিবাগ |
| নির্ধারিত জব চলার সময় | রাতের বেলা অ্যালার্ট |
| নিষ্পত্তির দিন | একটি মিলন সমস্যা |
import Ajv2020 from 'ajv/dist/2020';
import configSchema from './config.schema.json' with { type: 'json' };
const ajv = new Ajv2020({ allErrors: true, strict: true });
/**
* Validates the configuration and throws immediately on failure, implementing fail fast.
* @param rawConfig The raw merged configuration
* @returns The validated configuration
*/
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নয়, তাই এনভায়রনমেন্ট ভ্যারিয়েবল পড়ার পর স্পষ্টভাবে রূপান্তর করতে হবে; - সিক্রেট কখনো রিপোজিটরিতে নয়; কেবল এনভায়রনমেন্ট ভ্যারিয়েবল বা সিক্রেট ব্যবস্থাপনা সার্ভিস থেকে পড়ুন।
৩. কনফিগারেশন ফাইলে কোন ফরম্যাট
| ফাইল | ফরম্যাট | কারণ |
|---|---|---|
package.json |
স্ট্যান্ডার্ড JSON | প্যাকেজ ম্যানেজার কেবল এটি গ্রহণ করে |
tsconfig.json |
JSONC | কমেন্ট অনুমোদিত, অফিশিয়ালি সমর্থিত |
.vscode/settings.json |
JSONC | উপরের মতো |
| ব্যবসায়িক কনফিগারেশন | YAML বা JSONC | প্রতিটি আইটেমের অর্থ ব্যাখ্যা করা কমেন্ট দরকার |
| এনভায়রনমেন্ট ভ্যারিয়েবল তালিকা | .env |
সমতল কি-ভ্যালু, ইনজেক্ট করা সহজ |
একটি ব্যবহারিক পরীক্ষা: ফাইলটি কি মানুষের নয় এমন টুল পড়বে? হ্যাঁ → স্ট্যান্ডার্ড JSON; কেবল আপনার নিজের কোড পড়লে → ডায়ালেক্ট চলবে।
৪. টেস্ট ফিক্সচার: ডিরেক্টরি ও নামকরণ
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 ফাইল-নাম মাঝেমধ্যে ক্রস-প্ল্যাটফর্ম টুলচেইনে এনকোডিং সমস্যা করে, তাই ফাইল-নাম ইংরেজিতে রাখুন আর কন্টেন্ট বাংলা/চীনা থাকতে পারে।
৪.১ তিন ধরনের কেস, তিন ধরনের প্রত্যাশা
| ডিরেক্টরি | প্রত্যাশা | উদ্দেশ্য |
|---|---|---|
valid/ |
যাচাই পাস | মূল প্রবাহ, টাইপ জেনারেশন |
invalid/ |
যাচাই ব্যর্থ, আর এরর প্রত্যাশিত ফিল্ডে ইশারা করে | এরর হ্যান্ডলিং, বার্তার লেখা |
edge/ |
আচরণ স্পষ্ট (পাস বা ব্যর্থ যেকোনোটি ঠিক, তবে অবশ্যই দাবি করতে হবে) | রিগ্রেশন সুরক্ষা |
JSONTestSuite একই তিন ধরনের জন্য y_ / n_ / i_ প্রিফিক্স ব্যবহার করে (গ্রহণযোগ্য / প্রত্যাখ্যাত / বাস্তবায়ন-নির্ধারিত),
আর এই নামকরণ প্রথা সোজা ধারে নিতে পারেন।
৫. নির্ধারকতা: ফিক্সচার ডিফযোগ্য করুন
অস্থির ফিক্সচার কোড রিভিউ অর্থহীন করে:
| শব্দের উৎস | কীভাবে সামলাবেন |
|---|---|
| বর্তমান সময় | ধ্রুবকে স্থির করুন, যেমন 2026-01-01T00:00:00Z |
| দৈব ID / UUID | বর্ধমান ক্রম বা নির্দিষ্ট মান ব্যবহার করুন |
| কি-ক্রম | তৈরি করার সময় বর্ণানুক্রমে সাজান |
| ফ্লোট ফরম্যাটিং | সমানভাবে দুই দশমিক রাখুন বা স্ট্রিংয়ে বদলান |
| টাইম জোন | সর্বত্র UTC ব্যবহার করুন |
| ইনডেন্টেশন | ২ স্পেসে স্থির, CI-তে Prettier দিয়ে যাচাই |
/**
* Recursively sorts keys to produce stable, diffable JSON text.
* @param value The value to serialize
* @param indentWidth Indentation width
* @returns A JSON string with a stable order
*/
function toStableJson(value: unknown, indentWidth = 2): string {
/**
* Recursively sorts object keys.
* @param input Any JSON value
* @returns A new value with sorted keys
*/
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);
}
৬. মাস্কিং ও কমপ্লায়েন্স
বাস্তব ডেটা সোজা ফিক্সচার হিসেবে ব্যবহার করা যায় না:
- নাম → "করিম" ও "রহিম"-এর মতো প্লেসহোল্ডার;
- ফোন নম্বর → স্পষ্টভাবে কাল্পনিক নম্বর, যেমন
13800138000; - জাতীয় পরিচয়পত্র নম্বর → সঠিক ফরম্যাট কিন্তু অকার্যকর চেক-ডিজিটযুক্ত ভুয়া নম্বর;
- ঠিকানা → প্রভিন্স/শহরের কাঠামো রাখুন, বাড়ির নম্বর বাদ দিন;
- টাকার পরিমাণ → মাত্রার ক্রম রাখুন, কিন্তু কখনো প্রকৃত অ্যাকাউন্ট তথ্য রাখবেন না।
দলে একটি scripts/mask-fixture.mjs থাকা ভালো যা এক্সপোর্ট করা প্রোডাকশন নমুনার উপর চলে মাস্ক করা সংস্করণ বানায়,
"হাতে তিন জায়গা বদলে বাকিটা ভুলে যাওয়া" এড়াতে।
৭. মাইগ্রেশন ও সংস্করণ
ফিক্সচারও পুরোনো হয়। ফিক্সচারে একটি schemaVersion ফিল্ড দিন, বা ডিরেক্টরিতে একটি VERSION ফাইল রাখুন:
{
"schemaVersion": 3,
"id": "1001",
"amount": "89.50"
}
API চুক্তি আপগ্রেড হলে:
- নতুন ফিক্সচার ডিরেক্টরি যোগ করুন (
v3/); পুরোনো ফিক্সচার জায়গায় বদলাবেন না; - পুরোনো API সংস্করণ অবসর না হওয়া পর্যন্ত রিগ্রেশনের জন্য পুরোনো ফিক্সচার রাখুন;
- CI-তে দুই সংস্করণ চালিয়ে পশ্চাৎ-সামঞ্জস্য নিশ্চিত করুন।
৮. চেকলিস্ট
- [ ] তিন স্তরের কনফিগারেশন কাঠামো স্পষ্ট, আর লোকাল ওভাররাইড কমিট হয় না;
- [ ] যাচাই স্টার্টআপে হয়, আর ব্যর্থতা সঙ্গে সঙ্গে বন্ধ করে;
- [ ] সিক্রেট কেবল এনভায়রনমেন্ট ভ্যারিয়েবল থেকে; কনফিগারেশন ফাইলে কেবল প্লেসহোল্ডার;
- [ ] ফিক্সচার
valid/invalid/edgeডিরেক্টরিতে বিভক্ত, আর নাম পরিস্থিতি প্রকাশ করে; - [ ] ফিক্সচার নির্ধারক (স্থির সময়, ID ও ক্রম) ও ডিফযোগ্য;
- [ ] বাস্তব ডেটা মাস্কিং স্ক্রিপ্ট দিয়ে প্রসেস করা হয়েছে;
- [ ] চুক্তি আপগ্রেডে নতুন সংস্করণ ডিরেক্টরি যোগ হয়, জায়গায় সম্পাদনা নয়।
সাধারণ প্রশ্ন
কনফিগারেশন স্তর কীভাবে ক্রম ও মার্জ করা উচিত?
তিন স্তর ব্যবহার করুন — ডিফল্ট, এনভায়রনমেন্ট কনফিগ ও লোকাল ওভাররাইড — মার্জ ক্রম ডিফল্ট → এনভায়রনমেন্ট → লোকাল, প্রতিটি পরের স্তর আগেরটিকে ওভাররাইড করে। কেবল ব্যবসার প্রয়োজন যতটা গভীরে মার্জ করুন আর অ্যারে ও স্কেলার সম্পূর্ণরূপে প্রতিস্থাপন করুন, যাতে গভীর মার্জ অপ্রত্যাশিতভাবে অ্যারে জোড়া না লাগায়। লোকাল ওভাররাইড ফাইল gitignore করুন, ব্যক্তিগত সেটিং কমিট না করে।
স্টার্টআপে কনফিগারেশন যাচাই করা উচিত কেন?
কনফিগারেশন ভুলের খরচ কখন ধরা পড়ছে তার সঙ্গে সূচকীয়ভাবে বাড়ে: স্টার্টআপে একটি লগ লাইন বনাম নিষ্পত্তির দিনে একটি মিলন সমস্যা। স্টার্টআপে JSON Schema দিয়ে যাচাই করুন আর বাধ্যতামূলক আইটেম বা টাইপ ভুলে সঙ্গে সঙ্গে বন্ধ করুন, "বিশ্বাসযোগ্য দেখতে" ডিফল্ট না দিয়ে।
বাস্তব ডেটা কি সোজা টেস্ট ফিক্সচার হিসেবে ব্যবহার করা উচিত?
না। নাম, ফোন নম্বর, পরিচয়পত্র নম্বর, ঠিকানা ও টাকার পরিমাণ — সবের জন্যই প্লেসহোল্ডার বা স্পষ্টভাবে কাল্পনিক মান দরকার, আর দলে scripts/mask-fixture.mjs থাকা উচিত যা প্রোডাকশন এক্সপোর্টের উপর চলে মাস্ক করা সংস্করণ বানায়, হাতে সম্পাদনা করে কিছু বাদ পড়া এড়াতে।