JSON Toolkit

FAQ collaboration d'équipe : contrats, configuration et fixtures

L’essentiel : Style de nommage, formats de temps, sémantique de null, formes d'énumération, formats de configuration, organisation des fixtures et conflits de fusion — douze compromis sur lesquels les équipes se disputent réellement.

Intermédiaire~12 min de lecturePublié le 2026-10-30Mis à jour le 2026-10-30
Illustration de la FAQ collaboration d'équipe

L'essentiel : tranchez ces points une fois dans une convention d'équipe — une enveloppe {"data":…,"meta":…} avec des codes de statut HTTP, un seul style de nommage sur tout le site, des horodatages RFC 3339 avec fuseau horaire, et des énumérations en chaînes — puis faites évoluer chaque nouvelle API contre le même contrat en ajoutant seulement des champs, jamais en les modifiant ou en les supprimant.

Il n'y a pas de réponse unique correcte à ces questions, mais une équipe doit avoir une réponse unique — sinon chaque nouvelle API relance le même débat.

1. Contrats d'API

Quelle structure une API doit-elle renvoyer ?

Trois formes courantes, chacune avec un coût :

Forme Exemple Avantages Inconvénients
Données brutes [{...}, {...}] Simple Ne peut pas porter de métadonnées
Enveloppe fixe {"code":0,"message":"ok","data":{...}} Gestion d'erreur uniforme Le code métier duplique le statut HTTP
Enveloppe de ressource {"data":{...},"meta":{...},"links":{...}} Extensible, sémantique claire Un niveau d'imbrication de plus

Pour les API métier internes, préférez « enveloppe de ressource + codes de statut HTTP qui expriment le résultat » :

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

Pour les erreurs, utilisez un code de statut HTTP plus un corps d'erreur structuré au lieu d'imbriquer un autre code :

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

Point clé : les codes de statut HTTP expriment déjà le succès ou l'échec, donc un code métier est redondant. Quand les deux systèmes coexistent, des incohérences comme « 201 mais code 500 » sont presque inévitables.

Évitez les tableaux nus. Avec un tableau au niveau supérieur, vous ne pouvez pas attacher pagination, totaux ou curseurs, et les ajouter plus tard signifie changer chaque appelant.

Les noms de champs doivent-ils utiliser snake_case ou camelCase ?

Choisissez-en un, appliquez-le sur tout le site, et ne les mélangez jamais dans un même document JSON.

Style Écosystème courant Exemple
camelCase JavaScript / TypeScript / Java userName, createdAt
snake_case Python / Ruby / Go (en partie) / colonnes de base de données user_name, created_at
kebab-case Rare ; les clés avec tirets sont malcommodes à accéder user-name

Critères :

  1. Qui consomme ce JSON : choisissez camelCase quand le frontend domine, snake_case quand les pipelines de données et les services Python dominent ;
  2. Est-ce mappé directement sur des colonnes de base de données : snake_case économise une couche de conversion quand on passe directement des structures de table ;
  3. Continuité des systèmes existants : gardez ce que l'API existante utilise ; la cohérence bat la correction.

S'il faut vraiment convertir, faites-le à la frontière (passerelle ou couche de requête frontend) plutôt que de disperser des appels toCamelCase dans le code métier.

Quel format les champs de temps doivent-ils utiliser ?

Utilisez des chaînes RFC 3339 / ISO 8601 avec un offset de fuseau horaire :

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

Points clés :

  • Incluez le fuseau horaire : 2026-10-30 11:37:57 sans zone cassera inévitablement dans les scénarios inter-fuseaux ;
  • Utilisez Z ou un offset de façon cohérente : les deux sont corrects, mais ne les mélangez pas dans une même API ;
  • Évitez les horodatages numériques : qu'il s'agisse de secondes ou de millisecondes, et que les secondes intercalaires soient incluses, chaque équipe doit le réinterpréter. Quand la compacité est requise, utilisez un entier en millisecondes, mais épinglez l'unité dans la documentation.

Préférez des noms de champs avec un suffixe At comme createdAt / updatedAt / deletedAt, pour qu'on voie au premier coup d'œil que la valeur est un instant plutôt qu'une durée. Pour les durées, utilisez timeoutMs, ttlSeconds — mettez l'unité dans le nom.

Les nulls doivent-ils être null ou un champ omis ?

Cela dépend si « ce champ a du sens » :

Sémantique Forme Remarques
Le champ existe mais n'a pas encore de valeur "name": null Dit explicitement « il a été vérifié et c'est vide »
Le champ est sans rapport avec cette ressource Omettre Par exemple, un utilisateur ordinaire ne devrait pas avoir vipExpireAt
Mise à jour partielle (PATCH) Omettre = ne pas changer Seul null signifie « effacer » ici

Le troisième cas est le plus souvent négligé. Dans une requête PATCH :

{ "nickname": null }

cela signifie « effacer le pseudo », tandis qu'omettre nickname du corps de requête signifie « ne pas toucher au pseudo ». Si vous traitez les deux comme « effacer », un utilisateur qui change son e-mail effacera son pseudo.

Encore une fois : n'utilisez pas undefined. Ce n'est pas une valeur JSON valide, et JSON.stringify supprime entièrement la clé, donc le comportement correspond à « omettre » tandis que le code ressemble à « une valeur vide a été passée explicitement ».

Comment nommer les champs booléens ?

Utilisez les préfixes is / has / can / should et évitez les négations.

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

Trois leçons :

  1. Pas de négations : isDisabled: false oblige le lecteur à l'inverser une fois ; isEnabled: true est plus direct. La double négation (isNotDisabled) est un désastre ;
  2. N'utilisez pas de nombres pour les booléens : "enabled": 1 oblige chaque consommateur à écrire === 1 une fois ;
  3. N'utilisez pas de chaînes : "enable": "true" est vrai en JavaScript, et if (data.enable) est aussi vrai pour "false" ; ces bugs sont extrêmement difficiles à tracer.

Les champs d'énumération doivent-ils utiliser des nombres ou des chaînes ?

Utilisez des chaînes sauf s'il y a une contrainte claire de stockage ou de bande passante.

{ "status": "active" }

Comparé à { "status": 1 }, les chaînes gagnent parce que :

  • Les journaux, captures de paquets et bases de données sont directement lisibles sans consulter une table de correspondance ;
  • Ajouter une valeur d'énumération ne change pas le sens des valeurs existantes (insérer une valeur au milieu d'une énumération numérique décale tout) ;
  • Le frontend n'a pas besoin de maintenir une correspondance 1 → 'active' en synchronisation.

Le coût est de quelques octets supplémentaires, presque négligeable après compression.

Pratique associée : utilisez minuscules + underscores pour les valeurs d'énumération en chaînes (pending_review), et contraignez-les avec enum dans le Schema pour que les valeurs invalides soient rejetées à l'étape de validation.

2. Configuration et fixtures

La configuration doit-elle être en JSON, YAML ou TOML ?

Choisissez selon l'objectif, pas selon la préférence :

Format Bon pour Mauvais pour
JSON Lecture et écriture programmatiques, données strictement structurées, validation de Schema Édition manuelle fréquente (commentaires interdits)
YAML Configuration de déploiement profondément imbriquée (K8s) L'indentation écrite à la main est sujette aux erreurs ; yes/no deviennent des booléens
TOML Configuration plate de dépendances et d'outils (Cargo.toml, pyproject.toml) Lisibilité médiocre pour l'imbrication profonde
JSONC Configuration d'éditeur nécessitant des commentaires (tsconfig.json) Franchir les frontières système

Une erreur courante des équipes est de stocker de la configuration métier en YAML — la conversion de type implicite de YAML (1.0 devient un nombre, NO devient un booléen, 2026-10-30 devient une date) produit des résultats différents selon les parseurs.

Si la configuration doit être strictement validée par un programme, choisissez JSON + Schema, et documentez les commentaires via un champ _comment ou une doc séparée.

Comment organiser les fixtures de test ?

Organisez par « une base + des différences minimales », pas en copiant un énorme JSON pour chaque cas de test.

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 };
}

Ainsi, un nouveau cas de test n'a besoin d'écrire que les champs différents, et modifier un champ de base met à jour chaque cas automatiquement.

Trois règles :

  1. Les fixtures doivent passer la validation de Schema : la fixture de base elle-même devrait être des données valides ;
  2. N'exportez pas de vraies données de production : numéros de téléphone, numéros d'identité et adresses sont tous des informations personnelles ;
  3. Les fixtures appartiennent au contrôle de version mais doivent rester petites : un fichier unique au-delà de quelques dizaines de Ko mérite d'être élagué ou généré.

Comment réduire les conflits de fusion dans les fichiers JSON ?

JSON entre facilement en conflit : un formateur réécrit tout le fichier d'un coup, donc deux personnes changeant chacune une ligne entrent en conflit sur tout le fichier.

Quatre tactiques :

  1. Unifiez le formatage et ajoutez-le à .editorconfig : indentation, fins de ligne et retour à la ligne final cohérents évitent les diffs sans rapport ;
  2. Triez les clés : convenez d'un ordre alphabétique pour qu'un nouveau champ n'entre pas en collision avec le point d'insertion d'un autre ;
  3. Découpez les fichiers : brisez un géant config.json en plusieurs fichiers par domaine, et la surface de conflit se réduit naturellement ;
  4. Évitez les commits « tout reformater » : séparez le formatage des changements métier en deux commits pour que la revue reste lisible.

Si l'équipe utilise des lock files (pnpm-lock.yaml, package-lock.json), ne les fusionnez pas à la main ; régénérez-les plutôt :

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

Comment maintenir la compatibilité de version ?

Le principe est « ajouter, ne pas modifier, ne pas supprimer ».

Changement Compatibilité Approche
Ajouter un champ facultatif Compatible Ajoutez-le ; les consommateurs ignorent les champs inconnus
Ajouter un champ obligatoire Incompatible Incrémentez la version, ou fournissez un défaut
Supprimer un champ Incompatible Marquez-le d'abord déprécié, surveillez les appelants, puis supprimez
Changer le type d'un champ Incompatible Ajoutez un nouveau nom de champ et écrivez les deux pendant la transition
Changer l'ensemble des valeurs d'énumération Dépend Ajouter des valeurs est compatible ; les retirer ne l'est pas

Mécanismes de soutien :

  • Les consommateurs utilisent un « parsing indulgent » : ignorer les champs inconnus et fournir des défauts pour les manquants ;
  • Le serveur garde les anciens champs un certain temps et renvoie un en-tête Deprecation dans la réponse comme indice ;
  • Dans le Schema, ne marquez pas les nouveaux champs comme required, et taguez les champs dépréciés avec deprecated: true.

Comment valider les contrats en CI ?

Traitez le Schema comme une partie du code et validez-le à chaque commit.

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

Lancez trois choses en CI :

  1. Validité du Schema lui-même : la déclaration $schema est-elle valide et $ref se résout-il ;
  2. Les fixtures correspondent-elles au Schema : garantit que les données de test ne dérivent pas ;
  3. Le contrat est-il rétrocompatible : comparez avec la version précédente et vérifiez les champs supprimés ou les types changés.

Le troisième élément a besoin d'un support d'outillage (comme openapi-diff ou json-schema-diff) ; sans cela, comparez au moins les changements dans les tableaux required et les ensembles de clés properties avec un script.

3. Aide-mémoire

Question Recommandation
Structure de réponse {"data":..., "meta":...}, avec des codes de statut HTTP pour les erreurs
Style de nommage camelCase (frontend d'abord) ou snake_case (données d'abord), cohérent sur tout le site
Format de temps Chaînes RFC 3339 avec fuseau horaire, noms de champs finissant par At
Nulls Utilisez null pour pas de valeur, omettez les champs sans rapport, l'omission dans PATCH signifie « ne pas changer »
Nommage booléen Préfixes is/has/can, pas de négations, pas de 0/1
Énumérations Chaînes, minuscules avec underscores, ajoutez enum dans le Schema
Format de configuration JSON pour la validation programmatique, TOML ou JSONC pour l'édition manuelle
Fixtures Une base + des surcharges à différence minimale, valides contre le Schema
Conflits de fusion Unifier le formatage, trier les clés, découper les fichiers
Compatibilité de version Ajouter, ne pas modifier, ne pas supprimer ; marquer déprécié avant de retirer