Soalan Lazim Kerjasama Pasukan: Kontrak, Konfigurasi dan Fixture
Inti pati: Gaya penamaan, format masa, semantik null, bentuk enum, format konfigurasi, organisasi fixture, dan konflik gabungan — dua belas tolak ansur yang benar-benar dipertengkarkan oleh pasukan.

Pokoknya: tetapkan perkara ini sekali dalam konvensyen pasukan — envelope {"data":…,"meta":…} dengan kod status HTTP, satu gaya penamaan di seluruh laman, cap masa RFC 3339 dengan zon waktu, dan enum rentetan — kemudian kembangkan setiap API baharu mengikut kontrak yang sama dengan hanya menambah medan, tidak pernah mengubah atau memadamnya.
Tiada satu jawapan betul untuk soalan-soalan ini, tetapi pasukan mesti mempunyai satu jawapan — jika tidak, setiap API baharu memulakan semula pertengkaran yang sama.
1. Kontrak API
Apakah struktur yang perlu dikembalikan oleh API?
Tiga bentuk biasa, masing-masing dengan kos:
| Bentuk | Contoh | Kebaikan | Keburukan |
|---|---|---|---|
| Data mentah | [{...}, {...}] |
Mudah | Tidak boleh membawa metadata |
| Envelope tetap | {"code":0,"message":"ok","data":{...}} |
Pengendalian ralat seragam | Kod perniagaan menduplikasi kod status HTTP |
| Envelope sumber | {"data":{...},"meta":{...},"links":{...}} |
Boleh dikembangkan, semantik jelas | Satu tahap bersarang tambahan |
Untuk API perniagaan dalaman, utamakan "envelope sumber + kod status HTTP yang menyatakan hasil":
{
"data": [{ "id": "1", "name": "示例" }],
"meta": { "total": 128, "page": 2, "pageSize": 20 }
}
Untuk ralat, gunakan kod status HTTP serta badan ralat berstruktur dan bukannya menyelaraskan code lain:
{
"error": {
"type": "validation_failed",
"message": "请求参数校验失败",
"details": [
{ "path": "/email", "reason": "格式不正确" }
]
}
}
Perkara utama: kod status HTTP sudah menyatakan kejayaan atau kegagalan, jadi kod perniagaan adalah berlebihan. Apabila kedua-dua sistem wujud bersama, ketidakkonsistenan seperti "201 tetapi code ialah 500" hampir tidak dapat dielakkan.
Elakkan tatasusunan kosong. Dengan tatasusunan pada tahap atas anda tidak boleh melampirkan penomboran halaman, jumlah, atau kursor, dan menambahnya kemudian bermakna mengubah setiap pemanggil.
Patutkah nama medan menggunakan snake_case atau camelCase?
Pilih satu, gunakan di seluruh laman, dan jangan sekali-kali mencampurkannya dalam satu dokumen JSON.
| Gaya | Ekosistem biasa | Contoh |
|---|---|---|
camelCase |
JavaScript / TypeScript / Java | userName, createdAt |
snake_case |
Python / Ruby / Go (sebahagian) / lajur pangkalan data | user_name, created_at |
kebab-case |
Jarang; kunci bertanda sempang kekok untuk diakses | user-name |
Kriteria:
- Siapa yang menggunakan JSON ini: pilih camelCase apabila frontend mendominasi, snake_case apabila saluran data dan perkhidmatan Python mendominasi;
- Adakah ia memetakan terus kepada lajur pangkalan data: snake_case menjimatkan lapisan penukaran apabila menyalurkan struktur jadual terus;
- Kesinambungan sistem sedia ada: kekalkan apa sahaja yang digunakan API sedia ada; konsistensi mengalahkan ketepatan.
Jika anda benar-benar perlu menukar, lakukan pada sempadan (gerbang atau lapisan permintaan frontend) dan bukannya menaburkan panggilan toCamelCase di seluruh kod perniagaan.
Format apa yang perlu digunakan untuk medan masa?
Gunakan rentetan RFC 3339 / ISO 8601 dengan offset zon waktu:
{
"createdAt": "2026-10-30T11:37:57+08:00",
"updatedAt": "2026-10-30T03:37:57Z"
}
Perkara utama:
- Sertakan zon waktu:
2026-10-30 11:37:57tanpa zon pasti akan rosak dalam senario merentas zon waktu; - Gunakan
Zatau offset secara konsisten: kedua-duanya betul, tetapi jangan mencampurkannya dalam satu API; - Elakkan cap masa berangka: sama ada saat atau milisaat, dan sama ada saat lompat disertakan, setiap pasukan perlu mentafsirnya semula. Apabila kekompakan diperlukan, gunakan integer milisaat, tetapi tetapkan unit dalam dokumentasi.
Utamakan nama medan dengan akhiran At seperti createdAt / updatedAt / deletedAt,
supaya jelas pada pandangan pertama bahawa nilai itu ialah titik masa dan bukannya tempoh. Untuk tempoh gunakan timeoutMs, ttlSeconds — letakkan unit dalam nama.
Patutkah null menjadi null atau medan yang ditinggalkan?
Ia bergantung pada sama ada "medan ini bermakna":
| Semantik | Bentuk | Nota |
|---|---|---|
| Medan wujud tetapi belum mempunyai nilai | "name": null |
Menyatakan secara eksplisit "ia telah disemak dan ia kosong" |
| Medan tidak berkaitan dengan sumber ini | Tinggalkan | Contohnya, pengguna biasa tidak sepatutnya mempunyai vipExpireAt |
| Kemas kini separa (PATCH) | Tinggalkan = jangan ubah | Hanya null bermaksud "kosongkan" di sini |
Kes ketiga paling kerap terlepas pandang. Dalam permintaan PATCH:
{ "nickname": null }
ini bermaksud "kosongkan nama panggilan", manakala meninggalkan nickname daripada badan permintaan bermaksud "jangan sentuh nama panggilan".
Jika anda memperlakukan kedua-duanya sebagai "kosongkan", pengguna yang menukar e-mel akan memadam nama panggilan mereka.
Sekali lagi: jangan gunakan undefined. Ia bukan nilai JSON yang sah, dan JSON.stringify membuang kunci sepenuhnya,
jadi tingkah laku sepadan dengan "tinggalkan" manakala kod kelihatan seperti "nilai kosong telah dihantar secara eksplisit".
Bagaimana medan boolean patut dinamakan?
Gunakan awalan is / has / can / should dan elakkan penafian.
{
"isActive": true,
"hasPassword": false,
"canEdit": true
}
Tiga pengajaran:
- Tiada penafian:
isDisabled: falsememaksa pembaca menyongsangkannya sekali;isEnabled: truelebih langsung. Penafian berganda (isNotDisabled) ialah bencana; - Jangan gunakan nombor untuk boolean:
"enabled": 1memaksa setiap pengguna menulis=== 1sekali; - Jangan gunakan rentetan:
"enable": "true"adalah benar dalam JavaScript, danif (data.enable)juga benar untuk"false"; bug sedemikian sangat sukar dikesan.
Patutkah medan enum menggunakan nombor atau rentetan?
Gunakan rentetan melainkan terdapat kekangan storan atau lebar jalur yang jelas.
{ "status": "active" }
Berbanding dengan { "status": 1 }, rentetan menang kerana:
- Log, tangkapan paket, dan pangkalan data boleh dibaca terus tanpa mencari jadual pemetaan;
- Menambah nilai enum tidak mengubah maksud nilai sedia ada (memasukkan nilai di tengah enum berangka menggeser semuanya);
- Frontend tidak perlu mengekalkan pemetaan
1 → 'active'secara selaras.
Kosnya ialah beberapa bait tambahan, yang hampir tidak ketara selepas pemampatan.
Amalan iringan: gunakan huruf kecil + garis bawah untuk nilai enum rentetan (pending_review), dan kekang mereka dengan enum dalam Schema
supaya nilai tidak sah ditolak pada peringkat pengesahan.
2. Konfigurasi dan fixture
Patutkah konfigurasi menggunakan JSON, YAML, atau TOML?
Pilih mengikut tujuan, bukan keutamaan:
| Format | Baik untuk | Buruk untuk |
|---|---|---|
| JSON | Pembacaan dan penulisan program, data berstruktur ketat, pengesahan Schema | Penyuntingan manual yang kerap (komen tidak dibenarkan) |
| YAML | Konfigurasi penghantaran bersarang dalam (K8s) | Inden tulisan tangan mudah salah; yes/no bertukar menjadi boolean |
| TOML | Konfigurasi kebergantungan dan alat yang rata (Cargo.toml, pyproject.toml) |
Kebolehbacaan lemah untuk bersarang dalam |
| JSONC | Konfigurasi editor yang memerlukan komen (tsconfig.json) |
Merentasi sempadan sistem |
Kesilapan biasa dalam pasukan ialah menyimpan konfigurasi perniagaan dalam YAML — penukaran jenis tersirat YAML (1.0 menjadi nombor,
NO menjadi boolean, 2026-10-30 menjadi tarikh) menghasilkan keputusan berbeza merentasi penghurai.
Jika konfigurasi mesti disahkan secara ketat oleh program, pilih JSON + Schema, dan dokumentasikan komen melalui medan _comment atau dokumen berasingan.
Bagaimana fixture ujian patut disusun?
Susun mengikut "satu garis dasar + perbezaan minimum", bukan dengan menyalin JSON besar untuk setiap kes ujian.
fixtures/
├─ users/
│ ├─ base.json # Objek pengguna garis dasar
│ ├─ no-email.json # Hanya e-mel yang hilang
│ └─ invalid-age.json # Hanya umur di luar julat
└─ fixtures.ts # Fungsi utiliti untuk memuat dan menggabungkan cetek
import baseUser from './users/base.json';
export function makeUser(overrides: Partial<User> = {}): User {
return { ...baseUser, ...overrides };
}
Dengan cara itu kes ujian baharu hanya perlu menulis medan yang berbeza, dan mengubah medan garis dasar mengemas kini setiap kes secara automatik.
Tiga peraturan:
- Fixture mesti lulus pengesahan Schema: fixture garis dasar itu sendiri patut menjadi data yang sah;
- Jangan eksport data sebenar daripada produksi: nombor telefon, nombor pengenalan, dan alamat semuanya maklumat peribadi;
- Fixture tergolong dalam kawalan versi tetapi mesti kekal kecil: satu fail melebihi beberapa puluh KB patut dipangkas atau dijana.
Bagaimana saya mengurangkan konflik gabungan dalam fail JSON?
JSON mudah berkonflik: pemformat mengalir semula keseluruhan fail serentak, jadi dua orang masing-masing mengubah satu baris berkonflik pada keseluruhan fail.
Empat taktik:
- Seragamkan pemformatan dan tambahkannya pada
.editorconfig: inden, akhir baris, dan baris baharu mengekor yang konsisten mengelakkan perbezaan tidak berkaitan; - Isih kunci: bersetuju dengan susunan abjad supaya medan baharu tidak bertembung dengan titik sisipan orang lain;
- Pecahkan fail: pecahkan satu
config.jsongergasi kepada beberapa fail mengikut domain, dan permukaan konflik menyusut secara semula jadi; - Elakkan komit "format semula semuanya": pisahkan pemformatan daripada perubahan perniagaan kepada dua komit supaya semakan kekal boleh dibaca.
Jika pasukan menggunakan fail kunci (pnpm-lock.yaml, package-lock.json), jangan gabungkannya secara manual; jana semula sebaliknya:
git checkout --theirs pnpm-lock.yaml
pnpm install
Bagaimana saya mengekalkan keserasian versi?
Prinsipnya ialah "tambah, jangan ubah, jangan buang".
| Perubahan | Keserasian | Pendekatan |
|---|---|---|
| Tambah medan pilihan | Serasi | Tambahkannya; pengguna mengabaikan medan yang tidak dikenali |
| Tambah medan wajib | Tidak serasi | Naikkan versi, atau sediakan nilai lalai |
| Buang medan | Tidak serasi | Tandakan sebagai lapuk dahulu, pantau pemanggil, kemudian buang |
| Tukar jenis medan | Tidak serasi | Tambah nama medan baharu dan tulis dua kali semasa peralihan |
| Tukar set nilai enum | Bergantung | Menambah nilai adalah serasi; membuangnya tidak |
Mekanisme sokongan:
- Pengguna menggunakan "penghuraian longgar": abaikan medan yang tidak dikenali dan sediakan nilai lalai untuk medan yang hilang;
- Pelayan mengekalkan medan lama untuk seketika dan mengembalikan pengepala
Deprecationdalam respons sebagai petunjuk; - Dalam Schema, jangan tandakan medan baharu sebagai
required, dan tanda medan lapuk dengandeprecated: true.
Bagaimana saya mengesahkan kontrak dalam CI?
Anggap Schema sebagai sebahagian daripada kod dan sahkan pada setiap komit.
# Sahkan data sampel terhadap Schema (menggunakan ajv-cli)
npx ajv-cli validate -s schemas/user.json -d "fixtures/users/*.json" --strict=false
Jalankan tiga perkara dalam CI:
- Kesahan Schema itu sendiri: sama ada pengisytiharan
$schemasah dan sama ada$refdiselesaikan; - Sama ada fixture sepadan dengan Schema: memastikan data ujian tidak terpesong;
- Sama ada kontrak serasi ke belakang: bandingkan dengan versi sebelumnya dan semak medan yang dibuang atau jenis yang diubah.
Perkara ketiga memerlukan sokongan perkakas (seperti openapi-diff atau json-schema-diff);
tanpanya, sekurang-kurangnya bandingkan perubahan dalam tatasusunan required dan set kunci properties dengan skrip.
3. Rujukan pantas
| Soalan | Cadangan |
|---|---|
| Struktur respons | {"data":..., "meta":...}, dengan kod status HTTP untuk ralat |
| Gaya penamaan | camelCase (frontend dahulu) atau snake_case (data dahulu), konsisten di seluruh laman |
| Format masa | Rentetan RFC 3339 dengan zon waktu, nama medan berakhir dengan At |
| Null | Gunakan null untuk tiada nilai, tinggalkan medan tidak berkaitan, peninggalan dalam PATCH bermaksud "jangan ubah" |
| Penamaan boolean | Awalan is/has/can, tiada penafian, tiada 0/1 |
| Enum | Rentetan, huruf kecil dengan garis bawah, tambah enum dalam Schema |
| Format konfigurasi | JSON untuk pengesahan program, TOML atau JSONC untuk penyuntingan manual |
| Fixture | Satu garis dasar + gantian perbezaan minimum, sah terhadap Schema |
| Konflik gabungan | Seragamkan pemformatan, isih kunci, pecahkan fail |
| Keserasian versi | Tambah, jangan ubah, jangan padam; tandakan lapuk sebelum membuang |