JSON Toolkit

FAQ Kolaborasi Tim: Kontrak, Konfigurasi, dan Fixture

Intinya: Gaya penamaan, format waktu, semantik null, bentuk enum, format konfigurasi, organisasi fixture, dan konflik merge — dua belas pertimbangan trade-off yang benar-benar diperdebatkan tim.

Menengah±12 menit bacaDiterbitkan 2026-10-30Diperbarui 2026-10-30
Sampul untuk FAQ kolaborasi tim

Intinya: sepakati sekali dalam konvensi tim — envelope {"data":…,"meta":…} dengan kode status HTTP, satu gaya penamaan di seluruh situs, timestamp RFC 3339 dengan zona waktu, dan enum string — lalu kembangkan setiap API baru terhadap kontrak yang sama hanya dengan menambah field, tidak pernah mengubah atau menghapusnya.

Tidak ada satu jawaban benar tunggal untuk pertanyaan-pertanyaan ini, tetapi sebuah tim harus punya satu jawaban — jika tidak, setiap API baru memulai perdebatan yang sama lagi.

1. Kontrak API

Struktur apa yang sebaiknya dikembalikan API?

Tiga bentuk umum, masing-masing dengan biayanya:

Bentuk Contoh Kelebihan Kekurangan
Data mentah [{...}, {...}] Sederhana Tidak dapat membawa metadata
Envelope tetap {"code":0,"message":"ok","data":{...}} Penanganan error seragam Kode bisnis menduplikasi status HTTP
Envelope resource {"data":{...},"meta":{...},"links":{...}} Dapat diperluas, semantik jelas Satu level bersarang tambahan

Untuk API bisnis internal, utamakan "envelope resource + kode status HTTP yang mengungkapkan hasil":

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

Untuk error, gunakan kode status HTTP plus body error terstruktur daripada menyematkan code lain:

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

Poin kunci: kode status HTTP sudah menyatakan sukses atau gagal, jadi kode bisnis menjadi redundan. Ketika kedua sistem hidup berdampingan, ketidakonsistenan seperti "201 tetapi code-nya 500" hampir tak terhindarkan.

Hindari array telanjang. Dengan array di level teratas Anda tidak dapat menyertakan paginasi, total, atau kursor, dan menambahkannya nanti berarti mengubah setiap pemanggil.

Haruskah nama field memakai snake_case atau camelCase?

Pilih satu, terapkan di seluruh situs, dan jangan pernah mencampurnya dalam satu dokumen JSON.

Gaya Ekosistem umum Contoh
camelCase JavaScript / TypeScript / Java userName, createdAt
snake_case Python / Ruby / Go (sebagian) / kolom basis data user_name, created_at
kebab-case Jarang; key bertanda hubung canggung untuk diakses user-name

Kriteria:

  1. Siapa yang mengonsumsi JSON ini: pilih camelCase bila frontend mendominasi, snake_case bila pipeline data dan layanan Python mendominasi;
  2. Apakah ia memetakan langsung ke kolom basis data: snake_case menghemat lapisan konversi bila struktur tabel diteruskan apa adanya;
  3. Kesinambungan sistem yang ada: pertahankan apa pun yang dipakai API yang ada; konsistensi mengalahkan kebenaran.

Jika Anda benar-benar harus mengonversi, lakukan di batas (gateway atau lapisan permintaan frontend) alih-alih menyebarkan panggilan toCamelCase di seluruh kode bisnis.

Format apa yang sebaiknya dipakai untuk field waktu?

Gunakan string RFC 3339 / ISO 8601 dengan offset zona waktu:

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

Poin kunci:

  • Sertakan zona waktu: 2026-10-30 11:37:57 tanpa zona pasti akan rusak dalam skenario lintas zona waktu;
  • Gunakan Z atau offset secara konsisten: keduanya benar, tetapi jangan mencampurnya dalam satu API;
  • Hindari timestamp numerik: apakah itu detik atau milidetik, dan apakah detik kabisat disertakan, setiap tim harus menafsirkannya ulang. Bila keringkasan dibutuhkan, gunakan bilangan bulat milidetik, tetapi patok satuannya dalam dokumentasi.

Utamakan nama field berakhiran At seperti createdAt / updatedAt / deletedAt, agar sekilas jelas bahwa nilainya adalah titik waktu bukan durasi. Untuk durasi gunakan timeoutMs, ttlSeconds — taruh satuannya di nama.

Haruskah null menjadi null atau field yang dihilangkan?

Tergantung apakah "field ini bermakna":

Semantik Bentuk Catatan
Field ada tetapi belum bernilai "name": null Menyatakan secara eksplisit "sudah diperiksa dan kosong"
Field tidak relevan bagi resource ini Hilangkan Misalnya, pengguna biasa tidak semestinya punya vipExpireAt
Pembaruan parsial (PATCH) Hilangkan = jangan ubah Hanya null yang berarti "kosongkan" di sini

Kasus ketiga paling sering terlewat. Dalam permintaan PATCH:

{ "nickname": null }

ini berarti "kosongkan nickname", sedangkan tidak menyertakan nickname di body permintaan berarti "jangan sentuh nickname". Jika Anda memperlakukan keduanya sebagai "kosongkan", pengguna yang mengubah email akan menghapus nickname-nya.

Sekali lagi: jangan gunakan undefined. Itu bukan nilai JSON yang valid, dan JSON.stringify membuang key sepenuhnya, sehingga perilakunya sama dengan "hilangkan" sementara kodenya tampak seperti "nilai kosong dikirim secara eksplisit".

Bagaimana sebaiknya field boolean dinamai?

Gunakan prefiks is / has / can / should dan hindari negasi.

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

Tiga pelajaran:

  1. Tanpa negasi: isDisabled: false memaksa pembaca membaliknya sekali; isEnabled: true lebih langsung. Negasi ganda (isNotDisabled) adalah bencana;
  2. Jangan gunakan angka untuk boolean: "enabled": 1 membuat setiap konsumen menulis === 1 sekali;
  3. Jangan gunakan string: "enable": "true" truthy di JavaScript, dan if (data.enable) juga bernilai true untuk "false"; bug seperti ini sangat sulit dilacak.

Haruskah field enum memakai angka atau string?

Gunakan string kecuali ada batasan penyimpanan atau bandwidth yang jelas.

{ "status": "active" }

Dibandingkan dengan { "status": 1 }, string unggul karena:

  • Log, packet capture, dan basis data langsung terbaca tanpa mencari tabel pemetaan;
  • Menambah nilai enum tidak mengubah makna nilai yang ada (menyisipkan nilai di tengah enum numerik menggeser semuanya);
  • Frontend tidak perlu memelihara pemetaan 1 → 'active' secara sinkron.

Biayanya beberapa byte ekstra, yang hampir dapat diabaikan setelah kompresi.

Praktik pendamping: gunakan huruf kecil + garis bawah untuk nilai enum string (pending_review), dan batasi dengan enum di Schema agar nilai tidak valid ditolak pada tahap validasi.

2. Konfigurasi dan fixture

Haruskah konfigurasi berupa JSON, YAML, atau TOML?

Pilih berdasarkan tujuan, bukan preferensi:

Format Baik untuk Buruk untuk
JSON Pembacaan dan penulisan programatik, data berstruktur ketat, validasi Schema Pengeditan manual yang sering (komentar tidak diperbolehkan)
YAML Konfigurasi deployment bersarang dalam (K8s) Indentasi yang ditulis tangan rawan kesalahan; yes/no berubah menjadi boolean
TOML Konfigurasi dependensi dan alat yang datar (Cargo.toml, pyproject.toml) Keterbacaan buruk untuk bersarang dalam
JSONC Konfigurasi editor yang membutuhkan komentar (tsconfig.json) Melintasi batas sistem

Kesalahan umum di tim adalah menyimpan konfigurasi bisnis dalam YAML — konversi tipe implisit YAML (1.0 menjadi angka, NO menjadi boolean, 2026-10-30 menjadi tanggal) menghasilkan hasil berbeda antar parser.

Jika konfigurasi harus divalidasi ketat oleh program, pilih JSON + Schema, dan dokumentasikan komentar melalui field _comment atau dokumen terpisah.

Bagaimana sebaiknya fixture pengujian diorganisasi?

Organisasikan dengan prinsip "satu baseline + diff minimal", bukan dengan menyalin JSON raksasa untuk setiap kasus uji.

fixtures/
├─ users/
│  ├─ base.json              # Objek pengguna baseline
│  ├─ no-email.json          # Hanya email yang hilang
│  └─ invalid-age.json       # Hanya age yang di luar rentang
└─ fixtures.ts               # Fungsi utilitas untuk memuat dan merge dangkal
import baseUser from './users/base.json';

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

Dengan begitu kasus uji baru hanya perlu menulis field yang berbeda, dan mengubah field baseline otomatis memperbarui setiap kasus.

Tiga aturan:

  1. Fixture harus lolos validasi Schema: fixture baseline itu sendiri harus berupa data yang valid;
  2. Jangan mengekspor data asli dari produksi: nomor telepon, nomor identitas, dan alamat semuanya adalah informasi pribadi;
  3. Fixture termasuk dalam kontrol versi tetapi harus tetap kecil: satu berkas di atas beberapa puluh KB layak dipangkas atau dihasilkan.

Bagaimana cara mengurangi konflik merge pada berkas JSON?

JSON mudah berkonflik: formatter mengalirkan ulang seluruh berkas sekaligus, sehingga dua orang yang masing-masing mengubah satu baris berkonflik pada seluruh berkas.

Empat taktik:

  1. Seragamkan formatting dan tambahkan ke .editorconfig: indentasi, akhir baris, dan baris baru di akhir yang konsisten menghindari diff yang tidak terkait;
  2. Urutkan key: sepakati urutan alfabetis agar field baru tidak bertabrakan dengan titik penyisipan orang lain;
  3. Pecah berkas: pecah satu config.json raksasa menjadi beberapa berkas berdasarkan domain, dan permukaan konflik menyusut secara alami;
  4. Hindari commit "format ulang semuanya": pisahkan formatting dari perubahan bisnis menjadi dua commit agar tinjauan tetap terbaca.

Jika tim memakai lock file (pnpm-lock.yaml, package-lock.json), jangan merge manual; regenerasikan saja:

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

Bagaimana cara memelihara kompatibilitas versi?

Prinsipnya adalah "tambah, jangan ubah, jangan hapus".

Perubahan Kompatibilitas Pendekatan
Menambah field opsional Kompatibel Tambahkan; konsumen mengabaikan field yang tidak dikenal
Menambah field wajib Tidak kompatibel Naikkan versi, atau sediakan nilai default
Menghapus field Tidak kompatibel Tandai deprecated dulu, pantau pemanggil, lalu hapus
Mengubah tipe field Tidak kompatibel Tambahkan nama field baru dan tulis ganda selama transisi
Mengubah himpunan nilai enum Tergantung Menambah nilai kompatibel; menghapusnya tidak

Mekanisme pendukung:

  • Konsumen menggunakan "parsing longgar": abaikan field yang tidak dikenal dan sediakan default untuk yang hilang;
  • Server menyimpan field lama untuk sementara dan mengembalikan header Deprecation dalam respons sebagai petunjuk;
  • Dalam Schema, jangan tandai field baru sebagai required, dan tandai field deprecated dengan deprecated: true.

Bagaimana cara memvalidasi kontrak di CI?

Perlakukan Schema sebagai bagian dari kode dan validasi pada setiap commit.

# Validasi data sampel terhadap Schema (menggunakan ajv-cli)
npx ajv-cli validate -s schemas/user.json -d "fixtures/users/*.json" --strict=false

Jalankan tiga hal di CI:

  1. Validitas Schema itu sendiri: apakah deklarasi $schema valid dan apakah $ref dapat diselesaikan;
  2. Apakah fixture cocok dengan Schema: memastikan data uji tidak melenceng;
  3. Apakah kontrak kompatibel ke belakang: bandingkan dengan versi sebelumnya dan periksa field yang dihapus atau tipe yang berubah.

Butir ketiga membutuhkan dukungan perkakas (seperti openapi-diff atau json-schema-diff); tanpanya, setidaknya bandingkan perubahan pada array required dan himpunan key properties dengan sebuah skrip.

3. Rujukan cepat

Pertanyaan Rekomendasi
Struktur respons {"data":..., "meta":...}, dengan kode status HTTP untuk error
Gaya penamaan camelCase (frontend-first) atau snake_case (data-first), konsisten di seluruh situs
Format waktu String RFC 3339 dengan zona waktu, nama field berakhiran At
Null Gunakan null untuk tanpa nilai, hilangkan field yang tidak relevan, penghilangan dalam PATCH berarti "jangan ubah"
Penamaan boolean Prefiks is/has/can, tanpa negasi, tanpa 0/1
Enum String, huruf kecil dengan garis bawah, tambahkan enum di Schema
Format konfigurasi JSON untuk validasi programatik, TOML atau JSONC untuk pengeditan manual
Fixture Satu baseline + override diff minimal, valid terhadap Schema
Konflik merge Seragamkan formatting, urutkan key, pecah berkas
Kompatibilitas versi Tambah, jangan ubah, jangan hapus; tandai deprecated sebelum menghapus