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

جمعبندی: این موارد را یک بار در قرارداد تیم روشن کنید — پاکت {"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 |
معیارها:
- چه کسی این JSON را مصرف میکند: وقتی فرانتاند غالب است camelCase، وقتی خطوط داده و سرویسهای Python غالباند snake_case؛
- آیا مستقیماً به ستونهای پایگاهداده نگاشت میشود: snake_case هنگام عبور مستقیم ساختار جدول یک لایه تبدیل صرفهجویی میکند؛
- تداوم سیستمهای موجود: هر آنچه 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
}
سه درس:
- بدون منفیسازی:
isDisabled: falseخواننده را مجبور به یک بار وارونهسازی میکند؛isEnabled: trueمستقیمتر است. نفی مضاعف (isNotDisabled) فاجعه است؛ - از عدد برای بولی استفاده نکنید:
"enabled": 1هر مصرفکننده را مجبور به نوشتن یک بار=== 1میکند؛ - از رشته استفاده نکنید:
"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 };
}
به این ترتیب هر مورد آزمون جدید فقط باید فیلدهای متفاوت را بنویسد و تغییر یک فیلد پایه همه موارد را خودکار بهروز میکند.
سه قاعده:
- fixtureها باید اعتبارسنجی Schema را پاس کنند: خود fixture پایه باید داده معتبر باشد؛
- داده واقعی تولید را صادر نکنید: شماره تلفن، شماره شناسایی و آدرس همه اطلاعات شخصیاند؛
- fixtureها باید در کنترل نسخه باشند اما کوچک بمانند: یک فایل بیش از چند ده کیلوبایت ارزش هرس یا تولید دارد.
چگونه تعارضهای ادغام در فایلهای JSON را کم کنم؟
JSON بهراحتی تعارض مییابد: یک قالببند کل فایل را یکجا بازچینش میکند، پس دو نفر که هر یک یک خط تغییر دادهاند بر کل فایل تعارض مییابند.
چهار تاکتیک:
- یکسانسازی قالببندی و افزودن آن به
.editorconfig: تورفتگی، پایان خط و خط جدید انتهایی یکدست از تفاوتهای نامرتبط جلوگیری میکند؛ - مرتبسازی کلیدها: ترتیب الفبایی را توافق کنید تا فیلد جدید با نقطه درج دیگری برخورد نکند؛
- تقسیم فایلها: یک
config.jsonغولآسا را بر اساس دامنه به چند فایل بشکنید و سطح تعارض طبیعتاً کوچک میشود؛ - از کامیتهای «همه را دوباره قالببندی کن» پرهیز کنید: قالببندی را از تغییرات کسبوکار به دو کامیت جدا کنید تا بازبینی خوانا بماند.
اگر تیم از فایلهای قفل (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 اجرا کنید:
- اعتبار خود Schema: آیا اعلان
$schemaمعتبر است و آیا$refحل میشود؛ - تطبیق fixtureها با Schema: تضمین میکند داده آزمون منحرف نمیشود؛
- سازگاری رو به عقب قرارداد: با نسخه قبلی مقایسه کنید و فیلدهای حذفشده یا نوعهای تغییریافته را بررسی کنید.
مورد سوم پشتیبانی ابزار لازم دارد (مانند 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 |
| تعارض ادغام | یکسانسازی قالببندی، مرتبسازی کلیدها، تقسیم فایلها |
| سازگاری نسخه | اضافه کن، تغییر نده، حذف نکن؛ پیش از حذف منسوخ علامت بزن |