JSON Toolkit

پرسش‌های متداول همکاری تیمی: قرارداد، پیکربندی و fixture

جمع‌بندی: سبک نام‌گذاری، قالب زمان، معناشناسی null، شکل enum، قالب پیکربندی، سازمان‌دهی fixture و تعارض ادغام — دوازده مصالحه‌ای که تیم‌ها واقعاً بر سرشان بحث می‌کنند.

متوسطحدود 12 دقیقه مطالعهانتشار 2026-10-30به‌روزرسانی 2026-10-30
تصویر جلد پرسش‌های متداول همکاری تیمی

جمع‌بندی: این موارد را یک بار در قرارداد تیم روشن کنید — پاکت {"data":…,"meta":…} با کدهای وضعیت HTTP، یک سبک نام‌گذاری در کل سایت، مهرهای زمانی RFC 3339 با منطقه زمانی، و enumهای رشته‌ای — سپس هر API جدید را بر همان قرارداد با افزودن فیلد پیش ببرید، هرگز تغییر یا حذف نکنید.

برای این پرسش‌ها پاسخ درست واحدی وجود ندارد، اما تیم باید پاسخ واحدی داشته باشد — وگرنه هر API جدید همان بحث را از نو شروع می‌کند.

۱. قراردادهای API

API باید چه ساختاری برگرداند؟

سه شکل رایج، هر یک با هزینه‌ای:

شکل مثال مزیت‌ها عیب‌ها
داده خام [{...}, {...}] ساده نمی‌تواند فراداده حمل کند
پاکت ثابت {"code":0,"message":"ok","data":{...}} مدیریت خطای یکنواخت کد کسب‌وکار کد وضعیت HTTP را تکرار می‌کند
پاکت منبع {"data":{...},"meta":{...},"links":{...}} قابل توسعه، معناشناسی روشن یک سطح تودرتویی اضافه

برای APIهای کسب‌وکار داخلی، «پاکت منبع + کدهای وضعیت HTTP که نتیجه را بیان می‌کنند» را ترجیح دهید:

{
  "data": [{ "id": "1", "name": "示例" }],
  "meta": { "total": 128, "page": 2, "pageSize": 20 }
}

برای خطاها از کد وضعیت HTTP به‌همراه بدنه خطای ساختاریافته استفاده کنید نه تودرتو کردن code دیگر:

{
  "error": {
    "type": "validation_failed",
    "message": "请求参数校验失败",
    "details": [
      { "path": "/email", "reason": "格式不正确" }
    ]
  }
}

نکته کلیدی: کدهای وضعیت HTTP از قبل موفقیت یا شکست را بیان می‌کنند، پس کد کسب‌وکار زائد است. وقتی این دو سیستم هم‌زیستی کنند، ناسازگاری‌هایی مانند «201 اما code برابر 500 است» تقریباً اجتناب‌ناپذیرند.

از آرایه خالی پرهیز کنید. با آرایه در سطح بالا نمی‌توانید صفحه‌بندی، مجموع یا cursor ضمیمه کنید و افزودن آن‌ها بعداً یعنی تغییر همه فراخوان‌ها.

نام فیلدها باید snake_case باشد یا camelCase؟

یکی را انتخاب کنید، در کل سایت اعمال کنید و هرگز در یک سند JSON مخلوط نکنید.

سبک اکوسیستم رایج مثال
camelCase JavaScript / TypeScript / Java userName، createdAt
snake_case Python / Ruby / Go (تا حدی) / ستون‌های پایگاه‌داده user_name، created_at
kebab-case نادر؛ کلیدهای خط‌تیره‌دار دسترسی‌شان سخت است user-name

معیارها:

  1. چه کسی این JSON را مصرف می‌کند: وقتی فرانت‌اند غالب است camelCase، وقتی خطوط داده و سرویس‌های Python غالب‌اند snake_case؛
  2. آیا مستقیماً به ستون‌های پایگاه‌داده نگاشت می‌شود: snake_case هنگام عبور مستقیم ساختار جدول یک لایه تبدیل صرفه‌جویی می‌کند؛
  3. تداوم سیستم‌های موجود: هر آنچه API موجود استفاده می‌کند نگه دارید؛ یکدستی بر درستی مقدم است.

اگر واقعاً باید تبدیل کنید، آن را در مرز (دروازه یا لایه درخواست فرانت‌اند) انجام دهید نه اینکه فراخوانی‌های toCamelCase را در کد کسب‌وکار پراکنده کنید.

فیلدهای زمان چه قالبی داشته باشند؟

از رشته‌های RFC 3339 / ISO 8601 با offset منطقه زمانی استفاده کنید:

{
  "createdAt": "2026-10-30T11:37:57+08:00",
  "updatedAt": "2026-10-30T03:37:57Z"
}

نکات کلیدی:

  • منطقه زمانی را شامل شوید: 2026-10-30 11:37:57 بدون منطقه در سناریوهای بین‌منطقه‌ای ناگزیر می‌شکند؛
  • Z یا offset را یکدست استفاده کنید: هر دو درست‌اند، اما در یک API مخلوطشان نکنید؛
  • از مهر زمانی عددی پرهیز کنید: ثانیه است یا میلی‌ثانیه و آیا ثانیه کبیسه شامل است، هر تیم باید بازتفسیرش کند. وقتی فشردگی لازم است، از عدد صحیح میلی‌ثانیه استفاده کنید اما واحد را در مستندات سنجاق کنید.

نام فیلدهای دارای پسوند At مانند createdAt / updatedAt / deletedAt را ترجیح دهید، تا در یک نگاه روشن باشد مقدار یک نقطه در زمان است نه یک مدت. برای مدت‌ها از timeoutMs، ttlSeconds استفاده کنید — واحد را در نام بگذارید.

nullها باید null باشند یا فیلد حذف شود؟

بستگی دارد به اینکه «این فیلد معنادار است»:

معناشناسی شکل یادداشت
فیلد وجود دارد اما هنوز مقداری ندارد "name": null صریح می‌گوید «بررسی شد و خالی است»
فیلد به این منبع مربوط نیست حذف مثلاً کاربر عادی نباید vipExpireAt داشته باشد
به‌روزرسانی جزئی (PATCH) حذف = تغییر نده فقط null اینجا یعنی «پاک کن»

حالت سوم بیشترین موارد نادیده‌گرفته‌شده است. در یک درخواست PATCH:

{ "nickname": null }

این یعنی «نام مستعار را پاک کن»، در حالی که بیرون گذاشتن nickname از بدنه درخواست یعنی «به نام مستعار دست نزن». اگر هر دو را «پاک کن» بگیرید، کاربری که ایمیلش را عوض می‌کند نام مستعارش را از دست می‌دهد.

یک بار دیگر: از undefined استفاده نکنید. مقدار JSON معتبر نیست و JSON.stringify کلید را کلاً حذف می‌کند، پس رفتار با «حذف» یکی می‌شود در حالی که کد شبیه «مقدار خالی صریحاً فرستاده شد» به نظر می‌رسد.

فیلدهای بولی چگونه نام‌گذاری شوند؟

از پیشوندهای is / has / can / should استفاده کنید و از منفی‌سازی پرهیز کنید.

{
  "isActive": true,
  "hasPassword": false,
  "canEdit": true
}

سه درس:

  1. بدون منفی‌سازی: isDisabled: false خواننده را مجبور به یک بار وارونه‌سازی می‌کند؛ isEnabled: true مستقیم‌تر است. نفی مضاعف (isNotDisabled) فاجعه است؛
  2. از عدد برای بولی استفاده نکنید: "enabled": 1 هر مصرف‌کننده را مجبور به نوشتن یک بار === 1 می‌کند؛
  3. از رشته استفاده نکنید: "enable": "true" در جاوااسکریپت truthy است، و if (data.enable) برای "false" هم درست است؛ چنین باگ‌هایی ردیابی‌شان فوق‌العاده سخت است.

فیلدهای enum باید عدد باشند یا رشته؟

از رشته استفاده کنید مگر محدودیت واضحی در ذخیره‌سازی یا پهنای باند باشد.

{ "status": "active" }

در مقایسه با { "status": 1 }، رشته‌ها برنده‌اند چون:

  • لاگ‌ها، ضبط بسته و پایگاه‌داده بدون نگاه به جدول نگاشت مستقیماً خوانا هستند؛
  • افزودن یک مقدار enum معنای مقادیر موجود را تغییر نمی‌دهد (درج یک مقدار در میانه enum عددی همه را جابه‌جا می‌کند)؛
  • فرانت‌اند نیاز به هم‌گام نگه داشتن نگاشت 1 → 'active' ندارد.

هزینه چند بایت اضافه است که پس از فشرده‌سازی تقریباً ناچیز می‌شود.

رویه همراه: برای مقادیر enum رشته‌ای از حروف کوچک + زیرخط استفاده کنید (pending_review) و آن‌ها را با enum در Schema محدود کنید تا مقادیر نامعتبر در مرحله اعتبارسنجی رد شوند.

۲. پیکربندی و fixture

پیکربندی باید JSON باشد، YAML یا TOML؟

بر اساس هدف انتخاب کنید، نه سلیقه:

قالب مناسب برای نامناسب برای
JSON خواندن و نوشتن برنامه‌ای، داده با ساختار سخت‌گیر، اعتبارسنجی Schema ویرایش دستی مکرر (کامنت مجاز نیست)
YAML پیکربندی استقرار عمیقاً تودرتو (K8s) تورفتگی دست‌نویس خطاخیز است؛ yes/no به بولی تبدیل می‌شوند
TOML پیکربندی مسطح وابستگی و ابزار (Cargo.toml، pyproject.toml) خوانایی ضعیف برای تودرتویی عمیق
JSONC پیکربندی ویرایشگر که کامنت لازم دارد (tsconfig.json) عبور از مرز سیستم‌ها

اشتباه رایج تیم‌ها ذخیره پیکربندی کسب‌وکار در YAML است — تبدیل نوع ضمنی YAML (1.0 به عدد تبدیل می‌شود، NO به بولی، 2026-10-30 به تاریخ) نتایج متفاوت بین پارسرها می‌دهد.

اگر پیکربندی باید توسط برنامه سخت‌گیرانه اعتبارسنجی شود، JSON + Schema انتخاب کنید و کامنت‌ها را از طریق فیلد _comment یا مستندات جدا ثبت کنید.

fixtureهای آزمون چگونه سازمان‌دهی شوند؟

بر اساس «یک خط پایه + تفاوت‌های حداقلی» سازمان دهید، نه با کپی کردن یک JSON عظیم برای هر مورد آزمون.

fixtures/
├─ users/
│  ├─ base.json              # Baseline user object
│  ├─ no-email.json          # Only email is missing
│  └─ invalid-age.json       # Only age is out of range
└─ fixtures.ts               # Utility functions for loading and shallow merging
import baseUser from './users/base.json';

export function makeUser(overrides: Partial<User> = {}): User {
  return { ...baseUser, ...overrides };
}

به این ترتیب هر مورد آزمون جدید فقط باید فیلدهای متفاوت را بنویسد و تغییر یک فیلد پایه همه موارد را خودکار به‌روز می‌کند.

سه قاعده:

  1. fixtureها باید اعتبارسنجی Schema را پاس کنند: خود fixture پایه باید داده معتبر باشد؛
  2. داده واقعی تولید را صادر نکنید: شماره تلفن، شماره شناسایی و آدرس همه اطلاعات شخصی‌اند؛
  3. fixtureها باید در کنترل نسخه باشند اما کوچک بمانند: یک فایل بیش از چند ده کیلوبایت ارزش هرس یا تولید دارد.

چگونه تعارض‌های ادغام در فایل‌های JSON را کم کنم؟

JSON به‌راحتی تعارض می‌یابد: یک قالب‌بند کل فایل را یکجا بازچینش می‌کند، پس دو نفر که هر یک یک خط تغییر داده‌اند بر کل فایل تعارض می‌یابند.

چهار تاکتیک:

  1. یکسان‌سازی قالب‌بندی و افزودن آن به .editorconfig: تورفتگی، پایان خط و خط جدید انتهایی یکدست از تفاوت‌های نامرتبط جلوگیری می‌کند؛
  2. مرتب‌سازی کلیدها: ترتیب الفبایی را توافق کنید تا فیلد جدید با نقطه درج دیگری برخورد نکند؛
  3. تقسیم فایل‌ها: یک config.json غول‌آسا را بر اساس دامنه به چند فایل بشکنید و سطح تعارض طبیعتاً کوچک می‌شود؛
  4. از کامیت‌های «همه را دوباره قالب‌بندی کن» پرهیز کنید: قالب‌بندی را از تغییرات کسب‌وکار به دو کامیت جدا کنید تا بازبینی خوانا بماند.

اگر تیم از فایل‌های قفل (pnpm-lock.yaml، package-lock.json) استفاده می‌کند، آن‌ها را دستی ادغام نکنید؛ دوباره تولیدشان کنید:

git checkout --theirs pnpm-lock.yaml
pnpm install

چگونه سازگاری نسخه را حفظ کنم؟

اصل «اضافه کن، تغییر نده، حذف نکن» است.

تغییر سازگاری رویکرد
افزودن فیلد اختیاری سازگار اضافه کنید؛ مصرف‌کننده‌ها فیلدهای ناشناخته را نادیده می‌گیرند
افزودن فیلد الزامی ناسازگار نسخه را بالا ببرید یا مقدار پیش‌فرض بدهید
حذف فیلد ناسازگار اول منسوخ علامت بزنید، فراخوان‌ها را زیر نظر بگیرید، سپس حذف کنید
تغییر نوع فیلد ناسازگار نام فیلد جدید اضافه کنید و در دوران گذار هم‌زمان بنویسید
تغییر مجموعه مقادیر enum بستگی دارد افزودن مقادیر سازگار است؛ حذفشان نه

مکانیسم‌های پشتیبان:

  • مصرف‌کننده‌ها از «تجزیه سهل‌گیر» استفاده کنند: فیلدهای ناشناخته را نادیده بگیرند و برای گم‌شده‌ها پیش‌فرض بدهند؛
  • سرور فیلدهای قدیمی را مدتی نگه دارد و سرآیند Deprecation در پاسخ به‌عنوان اشاره برگرداند؛
  • در Schema، فیلدهای جدید را required علامت نزنید و فیلدهای منسوخ را با deprecated: true برچسب بزنید.

چگونه قراردادها را در CI اعتبارسنجی کنم؟

Schema را بخشی از کد بدانید و در هر کامیت اعتبارسنجی کنید.

# Validate sample data against the Schema (using ajv-cli)
npx ajv-cli validate -s schemas/user.json -d "fixtures/users/*.json" --strict=false

سه چیز را در CI اجرا کنید:

  1. اعتبار خود Schema: آیا اعلان $schema معتبر است و آیا $ref حل می‌شود؛
  2. تطبیق fixtureها با Schema: تضمین می‌کند داده آزمون منحرف نمی‌شود؛
  3. سازگاری رو به عقب قرارداد: با نسخه قبلی مقایسه کنید و فیلدهای حذف‌شده یا نوع‌های تغییر‌یافته را بررسی کنید.

مورد سوم پشتیبانی ابزار لازم دارد (مانند openapi-diff یا json-schema-diff)؛ بدون آن، حداقل تغییرات آرایه‌های required و مجموعه کلیدهای properties را با اسکریپت مقایسه کنید.

۳. مرجع سریع

پرسش توصیه
ساختار پاسخ {"data":..., "meta":...}، با کدهای وضعیت HTTP برای خطاها
سبک نام‌گذاری camelCase (فرانت‌اند-اول) یا snake_case (داده-اول)، یکدست در کل سایت
قالب زمان رشته‌های RFC 3339 با منطقه زمانی، نام فیلدهای پایانیافته با At
nullها null برای بدون مقدار، حذف فیلدهای نامرتبط، حذف در PATCH یعنی «تغییر نده»
نام‌گذاری بولی پیشوندهای is/has/can، بدون منفی‌سازی، بدون ۰/۱
enumها رشته، حروف کوچک با زیرخط، افزودن enum در Schema
قالب پیکربندی JSON برای اعتبارسنجی برنامه‌ای، TOML یا JSONC برای ویرایش دستی
fixtureها یک خط پایه + بازنویسی‌های حداقل‌تفاوت، معتبر بر اساس Schema
تعارض ادغام یکسان‌سازی قالب‌بندی، مرتب‌سازی کلیدها، تقسیم فایل‌ها
سازگاری نسخه اضافه کن، تغییر نده، حذف نکن؛ پیش از حذف منسوخ علامت بزن