JSON Toolkit

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.

Pertengahan~12 min bacaanDiterbitkan 2026-10-30Dikemas kini 2026-10-30
Kulit untuk soalan lazim kerjasama 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:

  1. Siapa yang menggunakan JSON ini: pilih camelCase apabila frontend mendominasi, snake_case apabila saluran data dan perkhidmatan Python mendominasi;
  2. Adakah ia memetakan terus kepada lajur pangkalan data: snake_case menjimatkan lapisan penukaran apabila menyalurkan struktur jadual terus;
  3. 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:57 tanpa zon pasti akan rosak dalam senario merentas zon waktu;
  • Gunakan Z atau 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:

  1. Tiada penafian: isDisabled: false memaksa pembaca menyongsangkannya sekali; isEnabled: true lebih langsung. Penafian berganda (isNotDisabled) ialah bencana;
  2. Jangan gunakan nombor untuk boolean: "enabled": 1 memaksa setiap pengguna menulis === 1 sekali;
  3. Jangan gunakan rentetan: "enable": "true" adalah benar dalam JavaScript, dan if (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:

  1. Fixture mesti lulus pengesahan Schema: fixture garis dasar itu sendiri patut menjadi data yang sah;
  2. Jangan eksport data sebenar daripada produksi: nombor telefon, nombor pengenalan, dan alamat semuanya maklumat peribadi;
  3. 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:

  1. Seragamkan pemformatan dan tambahkannya pada .editorconfig: inden, akhir baris, dan baris baharu mengekor yang konsisten mengelakkan perbezaan tidak berkaitan;
  2. Isih kunci: bersetuju dengan susunan abjad supaya medan baharu tidak bertembung dengan titik sisipan orang lain;
  3. Pecahkan fail: pecahkan satu config.json gergasi kepada beberapa fail mengikut domain, dan permukaan konflik menyusut secara semula jadi;
  4. 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 Deprecation dalam respons sebagai petunjuk;
  • Dalam Schema, jangan tandakan medan baharu sebagai required, dan tanda medan lapuk dengan deprecated: 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:

  1. Kesahan Schema itu sendiri: sama ada pengisytiharan $schema sah dan sama ada $ref diselesaikan;
  2. Sama ada fixture sepadan dengan Schema: memastikan data ujian tidak terpesong;
  3. 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