Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

🧩 Schemas — Guide PRO (Web & Dev)

“Schema” = contrat + structure + règles : DB schema, JSON Schema, OpenAPI, GraphQL schema, Avro/Proto, schema.org (SEO), config schemas, migrations, versioning, validation & governance.

1

Concept & vocabulaire

schema vs data, contract, constraints

Fondations
2

DB Schema (SQL)

tables, keys, constraints, namespace

DBProd
3

ORM & Migrations

model schema, migrations, drift, rollback

ORM
4

JSON Schema

validation, required, formats, $ref

Validation
5

OpenAPI / Swagger

contract API REST, codegen, mocks

API
6

GraphQL Schema

types, queries, mutations, federation

GraphQL
7

Avro / Protobuf

schemas event-driven, evolution rules

Events
8

Schema.org (SEO)

JSON-LD, rich snippets, entities

SEO
9

Config Schemas

YAML/JSON validation, policy, IaC

Ops
10

Versioning & compat

backward/forward, semver, deprecation

Gouvernance
11

Pipeline de validation

request → validate → persist → publish

Qualité
12

Outils & patterns

lint, tests de contrat, mocks, CI

Tooling
CONCEPT — Schema, contrat, validation, évolution
Définition “pro”
Schema = contrat + règles
  • Schema = description formelle de la forme des données + contraintes (types, required, invariants).
  • But : réduire l’ambiguïté FE/BE/DB/events et rendre les erreurs détectables tôt (shift-left).
  • Un schema “utile” répond à : Quoi (structure), Comment (règles), Quand (versions), Qui (owner).
Contract-first Validation Versioning Compatibility Registry
Schema ≠ Data
  • Data = instances (les valeurs réelles).
  • Schema = la règle qui dit si une instance est valide.
  • Si ton schema n’est pas testé et pas enforced, c’est juste de la doc → drift.
+-------------------------------+ | DATA (instances) | | {"email":"a@b.com","age":18} | +-------------------------------+ | v +-------------------------------+ | SCHEMA (rules) | | email required + format | | age >= 18 (optional) | +-------------------------------+
Où on le rencontre en Web/Dev
DB schema (SQL)
JSON Schema
OpenAPI
GraphQL SDL
Avro/Protobuf
schema.org (SEO)
Config schema (YAML/JSON)
Règle d’or : les schemas gagnent leur place en prod quand ils sont : versionnés + testés + appliqués (runtime/CI).
Glossaire indispensable
TermeDéfinition courteExempleErreur typique
Contract“Ce que j’accepte / ce que je renvoie”API request/responseDoc ≠ réalité
ValidationVérifier forme + règlesJSON Schema / OpenAPIValidation tardive → 500
InvariantRègle toujours vraieemail unique, age ≥ 18Règle uniquement en code
Backward compatibleAnciens consumers continuentajouter champ optionalrenommer champ
Forward compatibleNouveaux consumers lisent anciendefaults/optionalsuppression destructrice
Breaking changeCasse des clientsstring → intpas de version/deprecation
Schema registryRéférentiel central + règlesAvro/Proto sur Kafkaschemas “dans les apps”
DriftDivergence entre sourcesDB ≠ migrations ≠ codemodifs manuelles prod
Phrase entretien (CTO) : “Un schema, c’est un contrat versionné et testable qui évite les ambiguïtés et réduit les incidents.”
Matrices utiles (choisir le bon schema)
Quel schema pour quel besoin
BesoinSchema recommandéPourquoi
API RESTOpenAPI + JSON Schemacontrat + doc + codegen
Events (Kafka/PubSub)Avro/Protobuf + registryevolution + compat
DBConstraints SQLinvariants au plus près des données
GraphQLSDLcontrat central, introspection
SEOschema.org JSON-LDrich snippets, compréhension bots
Safe vs Breaking (heuristiques)
ChangementSouvent safeSouvent breaking
APIAjouter champ optional / endpointSupprimer/renommer champ, changer type
DBAjouter colonne nullableNOT NULL sans backfill, drop colonne
EventsAjouter champ avec defaultRenommer / réutiliser un field id (Proto)
Toujours vérifier avec tes consommateurs réels (inventaire des clients).
Diagrammes (pipeline & ownership)
Pipeline “schema enforced” (web → db → events)
Client
                    |
                    v
                    API (OpenAPI/JSON Schema)
                    |  (1) Validate input -> 400/422 (details)
                    v
                    Service (business rules + auth)
                    |
                    |  (2) Persist with DB constraints -> 409/constraint mapped
                    v
                    Database (PK/FK/UNIQUE/CHECK)
                    |
                    |  (3) Publish event with schema -> registry compat gate
                    v
                    Message Broker (Avro/Proto)
                    |
                    v
                    Consumers (validate/parse) -> storage/analytics
Ownership (qui “possède” le schema ?)
Producer owns schema:
                    - API team owns OpenAPI
                    - DB team owns DB constraints
                    - Event producer owns message schema
                    Consumers:
                    - can request changes
                    - must be notified for breaking/deprecation
Si personne n’est “owner”, tu auras : drift, breaking changes sauvages, et incidents inter-équipes.
Exemples concrets (même objet, plusieurs schemas)
Objet métier : User

Un même objet “User” peut avoir plusieurs représentations selon la couche : API DB Event SEO

CoucheSchemaExemple de règlePourquoi
APIOpenAPI/JSON Schemaemail required + formatcontrat client
DBConstraints SQLemail UNIQUE, NOT NULLinvariant stockage
EventAvro/Protofield ids stablescompat consumers
SEOschema.orgOrganization/Person markupcompréhension bots
Erreur typique “schema absent”
  • Front envoie age: "18" (string) → backend attend int → bug tardif
  • Un consumer Kafka casse car un champ est renommé sans compat
  • DB accepte des lignes invalides car aucune contrainte
La validation doit être un “gate” (entrée), pas un “patch” (après incident).
Snippets “ready to paste”
1) JSON Schema (validation request)
{
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "title": "User",
                    "type": "object",
                    "additionalProperties": false,
                    "properties": {
                    "id":    {"type":"integer"},
                    "email": {"type":"string","format":"email"},
                    "age":   {"type":"integer","minimum":18}
                    },
                    "required": ["email"]
                    }
2) OpenAPI (contract REST)
openapi: 3.1.0
                    info: { title: Users API, version: "1.0.0" }
                    paths:
                    /users:
                    post:
                    requestBody:
                    required: true
                    content:
                    application/json:
                    schema: { $ref: "#/components/schemas/UserCreate" }
                    responses:
                    "201":
                    description: Created
                    content:
                    application/json:
                    schema: { $ref: "#/components/schemas/User" }
                    components:
                    schemas:
                    UserCreate:
                    type: object
                    required: [email]
                    properties:
                    email: { type: string, format: email }
                    age: { type: integer, minimum: 18 }
                    User:
                    allOf:
                    - $ref: "#/components/schemas/UserCreate"
                    - type: object
                    required: [id]
                    properties:
                    id: { type: integer }
3) GraphQL SDL
type User { id: ID!, email: String!, age: Int }
                    input CreateUserInput { email: String!, age: Int }
                    type Query { user(id: ID!): User }
                    type Mutation { createUser(input: CreateUserInput!): User! }
4) Protobuf (events)
syntax = "proto3";
                    message UserCreated {
                    int64 id = 1;
                    string email = 2;
                    int32 age = 3;
                    }
                    // Règle: ne jamais réutiliser/renuméroter les IDs de champs.
5) schema.org JSON-LD (SEO)
<script type="application/ld+json">
                    {
                    "@context": "https://schema.org",
                    "@type": "Organization",
                    "name": "Ideo-Lab",
                    "url": "https://ideo-lab.com"
                    }
                    </script>
Astuce : garde un dossier fixtures/ avec JSON valides/invalides → tests de contrat simples.
Best Practices (très opérationnel)
Contract-first & CI gate
  • Écrire le schema avant si multi-clients / API publique.
  • CI : lint → validate fixtures → compat diff → publish artifact.
  • Bloquer les breaking changes (ou forcer une version majeure).
Enforcement (runtime)
  • Valider tôt (input) → erreurs propres (400/422)
  • Mapper erreurs DB (unique/fk/check) → 409 ou 422
  • Sur events : registry + compat checks avant publish
Règle d’or : un schema doit être un outil de production, pas un PDF oublié.
Anti-patterns (les pièges qui coûtent cher)
  • Schema décoratif : écrit mais pas testé / pas enforced.
  • Tout en “any/opaque” : plus aucune protection (dette cachée).
  • Breaking change silencieux : rename / type change sans versioning.
  • Pas d’inventaire consumers : tu ne sais même pas qui tu casses.
  • DB sans contraintes : tu repousses le problème… dans la data (c’est pire).
Un “schema absent” = bugs tardifs + incidents cross-team + data incohérente + migrations douloureuses.
KPIs (pilotage qualité)
KPIPourquoiCibleSignal faible
% endpoints couverts par schemacontrat explicite~100%docs non alignées
Ratio 4xx validation / 500erreurs captées tôt4xx ↑ / 500 ↓bugs tardifs, exceptions non gérées
Breaking changes / moisstabilité clients≈ 0hotfixes clients, rollback
Schema drift incidentsmigrations & prod health0diff DB vs migrations
KPI simple : “combien de fois un consumer casse à cause d’un changement de contrat ?” → si > 0, renforcer compat gate.
Checklist (à appliquer partout)
  1. Contrat explicite pour chaque objet métier (types + required + invariants).
  2. Exemples (valides & invalides) versionnés à côté du schema.
  3. Versioning + politique de dépréciation (deadline claire).
  4. Compat gate en CI (refuse breaking changes non gérés).
  5. Enforcement runtime : validation à l’entrée + mapping erreurs DB.
  6. Ownership : qui maintient le schema, qui valide les changements.
  7. Observabilité : métriques sur erreurs de validation et versions en prod.
Mini-règle : si tu ne peux pas expliquer ton schema en 30 secondes, il est trop flou (ou trop complexe).
DB SCHEMA — structure SQL, contraintes, index, namespaces, prod safety
Ce que veut dire “DB Schema” (dans la vraie vie)
Deux sens fréquents
  • 1) Structure : tables, colonnes, types, contraintes, index, vues.
  • 2) Namespace : regroupement logique d’objets (ex: PostgreSQL public, auth, reporting).
Tables PK / FK UNIQUE CHECK Index Views Grants
Pourquoi c’est critique (Prod)
  • Le schema DB est la dernière ligne de défense : invariants au plus près des données.
  • Il protège contre : bugs applicatifs, race conditions, imports foireux, scripts ad-hoc.
  • Il encode un “contrat” : ce qui est possible et impossible à stocker.
+-----------------------------+ | DB Schema = Storage Contract | +-----------------------------+ PK/FK/UNIQUE/CHECK -> integrity types -> meaning indexes -> performance contract grants -> security boundaries
Un bon principe : si une règle doit toujours être vraie, elle doit exister en DB (ou être justifiée explicitement).
Fondations : design, naming, namespaces
Design minimal solide
  • PK partout (surrogate ou naturelle, mais explicite).
  • Types précis (éviter text “par défaut” si un type existe).
  • Contraintes sur invariants : NOT NULL, UNIQUE, FK, CHECK.
  • Index = décision de perf (basée sur requêtes).
Namespace / Schemas (PostgreSQL)
  • Isoler par domaine : auth, billing, analytics
  • Gérer permissions par schema + rôles.
  • Réduire collisions de noms.
-- PostgreSQL
                            CREATE SCHEMA IF NOT EXISTS auth;
                            CREATE SCHEMA IF NOT EXISTS billing;

                            -- recherche d'objets
                            SHOW search_path;
                            SET search_path TO auth, public;
Conventions naming (pratiques)
ObjetConventionExemplePourquoi
Tablessnake_case, pluriel/collectif stableauth.userslisible, homogène
PKidusers.idstandard tooling
FK<ref>_idorders.user_idsimple et clair
Contraintesnoms explicitesusers_email_ukerreurs DB lisibles
Indexidx_*idx_orders_user_createddiagnostic facile
Contraintes : l’intégrité (le vrai “contrat”)
Palette d’intégrité
ContrainteRôleQuand l’utiliser
NOT NULLchamp obligatoireinvariant “toujours présent”
UNIQUEunicitéemail, clé métier, idempotency key
PRIMARY KEYidentité lignetoujours
FOREIGN KEYréférentielrelations, éviter orphelins
CHECKrègle localeage >= 18, status in (...)
FK : ON DELETE / ON UPDATE
  • RESTRICT/NO ACTION : empêche suppression si enfants (souvent par défaut).
  • CASCADE : supprime enfants automatiquement (attention prod).
  • SET NULL : garde enfants mais coupe le lien.
-- Exemple FK (PostgreSQL)
                            ALTER TABLE orders
                            ADD CONSTRAINT orders_user_fk
                            FOREIGN KEY (user_id) REFERENCES auth.users(id)
                            ON DELETE RESTRICT;
CASCADE est puissant, mais dangereux si tu supprimes “par erreur”. À réserver aux vraies compositions.
Triggers vs CHECK vs App code
Mécanisme+ Avantages- RisquesQuand
CHECKsimple, rapide, explicitelocal à la ligne/tableinvariants simples
FK/UNIQUEintégrité fortepeut impacter perf si pas d’indexrelations / unicité
Triggerlogique riche, multi-tablescomplexité, debug, surprisesaudit, soft delete, règles avancées
App codeflexible, testablerace conditions, driftrègles métier non “storage”
Index & performance : le “contrat de perf”
Rappels utiles
  • Un index accélère WHERE / JOIN / ORDER BY… mais coûte en INSERT/UPDATE/DELETE.
  • Index ≠ contrainte (mais souvent liés : UNIQUE → index unique).
  • Principe : indexer selon les requêtes réelles (et pas “au feeling”).
B-Tree Composite Partial Covering
Règle d’or des index composites
  • Ordre des colonnes = important : colonne la plus sélective / utilisée en filtre d’abord.
  • Ex : (user_id, created_at DESC) pour “orders d’un user triées par date”.
  • “Covering index” (selon DB) pour éviter des reads table (index-only scans).
-- PostgreSQL: index composite + tri
                            CREATE INDEX idx_orders_user_created
                            ON orders(user_id, created_at DESC);

                            -- index partiel (seulement les lignes utiles)
                            CREATE INDEX idx_orders_open
                            ON orders(user_id, created_at DESC)
                            WHERE status = 'OPEN';
Index vs contraintes : qui fait quoi ?
BesoinSolutionNote
Unicité emailUNIQUE(email)implique un index unique
Join rapide FKIndex sur FKsouvent indispensable
Filtre sur statusIndex (souvent partiel)éviter index trop large
Règle métier (age>=18)CHECKpas un index
Indexer “tout” est une anti-solution : tu tues l’écriture. Vise les requêtes critiques et mesure.
Migrations & changements online (sans casser prod)
Pattern safe (Expand → Backfill → Contract)
1) EXPAND:
                            - ajouter colonne nullable / nouvelle table
                            - ajouter index en mode non bloquant (si possible)
                            2) BACKFILL:
                            - remplir progressivement (batchs) + monitoring
                            3) CONTRACT:
                            - NOT NULL, contraintes finales
                            - switch code/queries
                            - supprimer ancien champ après fenêtre
Ce pattern est la base des migrations “zero-downtime”.
Exemple concret : rendre un champ NOT NULL
-- (1) colonne existe en NULL
                            ALTER TABLE users ADD COLUMN country text;

                            -- (2) backfill (en batch côté app/cron)
                            -- UPDATE users SET country='FR' WHERE country IS NULL LIMIT ...

                            -- (3) contrainte finale
                            ALTER TABLE users ALTER COLUMN country SET NOT NULL;
Le backfill doit être progressif sur grosse table (sinon locks & IO spikes).
Drift & “schema as code”
  • Tu dois pouvoir reconstruire la DB “from scratch” via migrations (CI).
  • Évite modifications manuelles en prod (ou alors : scriptées + commit dans repo).
  • Ajoute des checks automatiques : diff schema prod vs attendu.
Sécurité : rôles, grants, séparation des responsabilités
Principes
  • Least privilege : l’app n’a pas les droits “DBA”.
  • Rôles par usage : read-only, read-write, migrations.
  • Isoler les schemas sensibles (auth, secrets).
Exemple PostgreSQL (simplifié)
-- rôles
                            CREATE ROLE app_ro NOINHERIT;
                            CREATE ROLE app_rw NOINHERIT;

                            -- droits
                            GRANT USAGE ON SCHEMA auth TO app_ro, app_rw;
                            GRANT SELECT ON ALL TABLES IN SCHEMA auth TO app_ro;
                            GRANT SELECT,INSERT,UPDATE,DELETE ON ALL TABLES IN SCHEMA auth TO app_rw;

                            -- default privileges (pour nouvelles tables)
                            ALTER DEFAULT PRIVILEGES IN SCHEMA auth
                            GRANT SELECT ON TABLES TO app_ro;
Sépare un rôle “migrations” (puissant) du rôle runtime (limité). Ça réduit les dégâts en cas de compromission.
Diagrammes (contrat, perf, intégrité)
DB schema comme “contrat de stockage”
Application Layer
                    - business rules (complex)
                    - validations (shape)
                    |
                    v
                    Database Layer (hard guarantees)
                    - PK / FK / UNIQUE / CHECK  => integrity
                    - types                     => meaning
                    - indexes                   => performance contract
                    - grants                    => security boundaries
Intégrité vs performance (trade-off)
More constraints  -> more correctness  (usually good)
                    More indexes       -> faster reads      (but slower writes)
                    Goal:
                    keep constraints strong
                    keep indexes purposeful (query-driven)
Schéma logique (exemple)
auth.users (id PK, email UNIQUE, ...)
                    1
                    |
                    |  FK orders.user_id -> users.id
                    |
                    orders (id PK, user_id FK, created_at, status, ...)

                    Index:
                    idx_orders_user_created (user_id, created_at DESC)
                    Partial:
                    idx_orders_open WHERE status='OPEN'
Exemples (PostgreSQL + Oracle mindset)
PostgreSQL — schema + contraintes + index
CREATE SCHEMA IF NOT EXISTS auth;

                            CREATE TABLE auth.users (
                            id         bigserial PRIMARY KEY,
                            email      text NOT NULL,
                            created_at timestamptz NOT NULL DEFAULT now(),
                            age        int CHECK (age IS NULL OR age >= 18),
                            CONSTRAINT users_email_uk UNIQUE(email)
                            );

                            CREATE TABLE public.orders (
                            id         bigserial PRIMARY KEY,
                            user_id    bigint NOT NULL,
                            status     text NOT NULL CHECK (status IN ('OPEN','PAID','CANCELLED')),
                            created_at timestamptz NOT NULL DEFAULT now(),
                            CONSTRAINT orders_user_fk FOREIGN KEY(user_id) REFERENCES auth.users(id)
                            );

                            CREATE INDEX idx_orders_user_created ON public.orders(user_id, created_at DESC);
                            CREATE INDEX idx_orders_open ON public.orders(user_id, created_at DESC) WHERE status='OPEN';
Oracle — mêmes idées (syntaxe / options)
-- Oracle (extrait)
                            CREATE TABLE users (
                            id         NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
                            email      VARCHAR2(320) NOT NULL,
                            created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL,
                            age        NUMBER,
                            CONSTRAINT users_email_uk UNIQUE(email),
                            CONSTRAINT users_age_ck CHECK (age IS NULL OR age >= 18)
                            );

                            CREATE TABLE orders (
                            id         NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
                            user_id    NUMBER NOT NULL,
                            status     VARCHAR2(20) NOT NULL,
                            created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL,
                            CONSTRAINT orders_user_fk FOREIGN KEY(user_id) REFERENCES users(id),
                            CONSTRAINT orders_status_ck CHECK (status IN ('OPEN','PAID','CANCELLED'))
                            );

                            CREATE INDEX idx_orders_user_created ON orders(user_id, created_at);
Même logique : contraintes = intégrité, index = perf. La DB impose le contrat.
Snippets “diagnostic & audit schema”
PostgreSQL — lister contraintes / index
-- contraintes
                    SELECT conname, contype, conrelid::regclass AS table_name
                    FROM pg_constraint
                    WHERE connamespace = 'auth'::regnamespace
                    ORDER BY table_name, conname;

                    -- index d'une table
                    SELECT indexname, indexdef
                    FROM pg_indexes
                    WHERE schemaname='public' AND tablename='orders';
PostgreSQL — détecter colonnes critiques NULLables
SELECT table_schema, table_name, column_name, data_type, is_nullable
                    FROM information_schema.columns
                    WHERE table_schema IN ('auth','public')
                    AND column_name IN ('email','created_at','user_id')
                    ORDER BY table_schema, table_name;
Oracle — contraintes d’une table
SELECT constraint_name, constraint_type, status
                    FROM user_constraints
                    WHERE table_name = 'USERS'
                    ORDER BY constraint_type, constraint_name;
Best Practices (prod-grade)
Intégrité
  • PK partout + FKs là où la relation est réelle (sinon justifier).
  • UNIQUE pour clés métier (email, slug, idempotency key).
  • CHECK pour règles simples (enum, bornes, formats simples).
  • Nommer les contraintes → erreurs DB lisibles + debugging plus rapide.
Perf & opérations
  • Index selon requêtes (profiling), pas “par défaut”.
  • Index sur colonnes FK souvent nécessaire.
  • Migrations : expand/backfill/contract.
  • Rôles séparés (runtime vs migrations) + least privilege.
Le schema doit être “lisible” : si un nouveau dev/DBA ne comprend pas le contrat en 5 minutes, simplifier / documenter.
Anti-patterns (classiques)
  • Tout nullable + aucune contrainte → incohérences de data assurées.
  • “Pas de FK pour la perf” sans stratégie alternative → orphelins, deletes dangereux.
  • Types fourre-tout (text/json) sans validation → dette de données.
  • Index partout → writes lents + bloat + maintenance pénible.
  • Migrations destructrices sans plan (drop/rename direct) → downtime/rollback.
  • Rôle applicatif trop puissant (DROP/ALTER) → catastrophe si compromis.
En prod, la dette de schema devient de la dette de données… et ça coûte très cher.
KPIs (contrôle qualité DB)
KPICiblePourquoiAction
Tables sans PK0intégrité + perf joinsajouter PK
Colonnes critiques NULLables0invariantsbackfill + NOT NULL
Violations UNIQUE/FK0cohérenceclean data + constraints
Index inutilisés (long terme)faiblewrite overheadreview / drop
Migrations “long lock”0stabilité prodonline pattern
KPI “terrain” : % d’incidents prod causés par data incohérente → doit tendre vers 0 si contraintes bien posées.
Checklist (DB schema prod)
  1. PK sur chaque table.
  2. FK là où relation réelle + stratégie ON DELETE explicitée.
  3. UNIQUE sur clés métier (email/slug/idempotency key).
  4. CHECK pour invariants simples (enum, bornes, règles locales).
  5. Index selon requêtes + index sur FKs si nécessaire.
  6. Naming clair (constraints/index) pour diagnostic rapide.
  7. Migrations safe (expand/backfill/contract), surtout sur grosses tables.
  8. Rôles séparés (runtime vs migrations) + least privilege.
  9. Audit : détecter drift (prod vs repo) + monitoring erreurs contraintes.
Si tu veux, on densifie ensuite avec une section “Oracle vs PostgreSQL (différences pratiques)” dédiée, ou on passe à la modal ORM/Migrations.
ORM & MIGRATIONS
  • Un ORM (Django/SQLAlchemy/etc.) a son propre “schema” : models + migrations.
  • Risque : drift (DB réelle ≠ migrations ≠ code).
  • But : garder un seul source of truth et des déploiements reproductibles.
# Exemple mental (peu importe le framework)
                    Model User:
                    email: string unique not null
                    created_at: datetime default now
                    Migration:
                    add column / add constraint / add index
                    Drift typique:
                    prod a une colonne en plus ou une contrainte manquante
  • CI : appliquer migrations sur une DB “from scratch” + exécuter tests.
  • Pour prod : migration safe (add column nullable → backfill → set not null).
  • “Expand/Contract pattern” pour changements lourds (renommage, split table, etc.).
  • Monitoring : détecter les migrations lentes/verrouillantes.
Sur les grosses tables : éviter “ALTER type” bloquant, préférer shadow columns + swap.
  • Modifier la DB à la main en prod puis “oublier” de créer la migration.
  • Migration qui fait data + schema sans stratégie (rollback impossible).
  • Renommage champ = breaking change si clients / ETL / BI existent.
  1. Migrations linéaires et reproductibles.
  2. Stratégie rollback (ou forward-only documenté).
  3. Validation “schema drift” automatisée (CI + check prod).
  4. Changements “online” pour tables volumineuses.
JSON SCHEMA
  • JSON Schema décrit la forme d’un JSON : types, required, enum, formats, min/max, regex, etc.
  • Usage : validation d’inputs (API), config, documents, storage JSON, contracts inter-services.
  • Force : validation automatique + réutilisation via $ref.
{
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "additionalProperties": false,
                    "properties": {
                    "id":    {"type":"integer"},
                    "email": {"type":"string","format":"email"},
                    "age":   {"type":"integer","minimum":18}
                    },
                    "required": ["id","email"]
                    }
# points clés
                    - required = champs obligatoires
                    - additionalProperties=false = pas de champs “surprise”
                    - format=email = validation sémantique minimale
  • Verrouiller l’objet : additionalProperties=false (sinon dérive silencieuse).
  • Factoriser via $ref (User, Money, Address…).
  • Avoir des fixtures “valid/invalid” et tester en CI.
  • Générer documentation + exemples automatiquement.
  • “type: object” sans properties → useless.
  • Tout en anyOf complexes → schema incompréhensible.
  • Formats non testés (ou faux sentiment de sécurité).
  1. Types + required définis.
  2. Règles métier simples intégrées (min/max/enum).
  3. Interdiction de champs inconnus si besoin.
  4. Tests de validation en CI.
OPENAPI
  • OpenAPI décrit une API REST : endpoints, params, auth, request/response schemas, erreurs.
  • Valeur : doc vivante + codegen + mocks + contract tests.
Très utile dès que tu as plusieurs clients (web + mobile + partenaires).
openapi: 3.1.0
                    info: { title: Users API, version: "1.0.0" }
                    paths:
                    /users/{id}:
                    get:
                    parameters:
                    - in: path
                    name: id
                    required: true
                    schema: { type: integer }
                    responses:
                    "200":
                    description: OK
                    content:
                    application/json:
                    schema:
                    $ref: "#/components/schemas/User"
                    components:
                    schemas:
                    User:
                    type: object
                    required: [id, email]
                    properties:
                    id: { type: integer }
                    email: { type: string, format: email }
  • Définir des réponses d’erreur standard (400/401/403/404/409/422) avec schema commun.
  • Mettre des examples request/response (très important pour les clients).
  • Bloquer les breaking changes via tooling (diff de spec en CI).
  • Contract tests : serveur conforme à la spec + clients générés compilent.
  • Spec écrite “après” et jamais synchronisée → faux contrat.
  • “schema: {}” partout → doc inutile.
  • Changements sans versionning/deprecation → chaos côté clients.
  1. Chaque endpoint a params + réponses typées.
  2. Auth documentée (bearer, api key, oauth…).
  3. Erreurs standardisées avec schema.
  4. Spec versionnée + diff check en CI.
GRAPHQL SCHEMA
  • GraphQL a un schema central (SDL) : types, champs, queries/mutations, inputs, enums.
  • Contrat puissant : le client sait exactement ce qu’il peut demander.
  • Attention : la perf se gère (N+1, depth/complexity, caching).
type User { id: ID!, email: String!, age: Int }
                    type Query { user(id: ID!): User }
                    input CreateUserInput { email: String!, age: Int }
                    type Mutation { createUser(input: CreateUserInput!): User! }
  • Limiter la complexité : depth/complexity limits + timeouts.
  • Résoudre N+1 (DataLoader pattern, batching).
  • Déprécier proprement : @deprecated(reason: "...") + fenêtre de migration.
  • Registry/federation si multi-services.
  • Schémas trop “DB-like” (exposer tables internes) → couplage fort.
  • Aucune limite → requêtes monstrueuses / DoS applicatif.
  • Champs qui changent de sens sans versioning → clients cassés.
  1. Schema SDL versionné.
  2. Déprecations tracées.
  3. Limits + protections perf.
  4. Tests de contrat (introspection + snapshot).
AVRO / PROTOBUF
  • Pour l’event-driven (Kafka, Pub/Sub, RabbitMQ), le schema devient vital.
  • Protobuf : contrats stricts, codegen, perf (binaire), IDs de champs.
  • Avro : très utilisé en data pipelines (compat rules, schema registry).
  • Problème majeur : schema evolution (backward/forward/full).
// Protobuf (extrait)
                    message UserCreated {
                    int64 id = 1;
                    string email = 2;
                    int32 age = 3; // optional by presence semantics depending on syntax/version
                    }
# Règles d’évolution (simplifiées)
                    - ajouter un champ : OK (si optional / default)
                    - supprimer un champ : dangereux (préférer "deprecated")
                    - renuméroter un field id : NON (breaking)
  • Avoir un Schema Registry (même minimal) + validation à la publication.
  • Politique d’évolution : backward-only vs full-compat selon tes consommateurs.
  • Déprécier avant de supprimer, et attendre la fin de fenêtre de migration.
  • Tester compat en CI (ex: “new schema is backward compatible”).
  • Publier des events JSON sans schema → consumers fragiles.
  • Breaking change silencieux → incidents cross-services.
  • Pas d’ownership → “tout le monde change tout”.
  1. Schema versionné + registry.
  2. Compat checks en CI.
  3. Deprecation policy claire.
  4. Consumer tests (canaries) sur nouveaux schémas.
SCHEMA.ORG (SEO)
  • Ici “schema” = données structurées pour moteurs de recherche (souvent en JSON-LD).
  • But : aider Google/Bing à comprendre une page (Article, Product, Organization, FAQ…).
  • Impact : rich snippets, knowledge panels, meilleure compréhension sémantique.
<script type="application/ld+json">
                    {
                    "@context": "https://schema.org",
                    "@type": "Article",
                    "headline": "Schemas en dev : guide",
                    "author": { "@type": "Person", "name": "Guillaume" },
                    "datePublished": "2026-02-12"
                    }
                    </script>
  • Utiliser JSON-LD (simple à injecter server-side).
  • Rester fidèle au contenu visible (sinon risque de pénalité).
  • Tester régulièrement (données structurées) et versionner les templates.
  • Mettre du schema “fake” (FAQ inventée, reviews fausses) → mauvais plan.
  • Dupliquer/injecter plusieurs JSON-LD incohérents sur la même page.
  1. Type schema.org cohérent (Article/Product/Org…).
  2. Champs essentiels présents.
  3. Pas de divergence contenu vs markup.
CONFIG SCHEMAS
  • Config schema = valider des fichiers de config (YAML/JSON) avant déploiement.
  • Très utile en Ops/IaC : éviter un déploiement cassé pour une clé manquante.
  • Approche : JSON Schema / validation applicative / policies (OPA) selon le niveau.
# Exemple de config "attendue"
                    app:
                    env: "prod"
                    port: 8080
                    database_url: "postgres://..."
                    limits:
                    rate_per_min: 1200

                    # Un schema te permet d’empêcher :
                    # - port string au lieu d’int
                    # - database_url manquant
                    # - env hors {dev,staging,prod}
  • Valider en CI (pré-merge) et au démarrage applicatif.
  • Garder des defaults explicites (sinon comportement surprise).
  • Documenter les “breaking config changes” (nouvelle clé requise, clé supprimée).
  • Config “libre” sans validation → incidents “bêtes” en prod.
  • Secrets directement dans les configs versionnées.
  1. Schema de config (même minimal) + validation CI.
  2. Defaults + required clarifiés.
  3. Secrets externalisés (vault/variables d’env).
VERSIONING & COMPAT
  • Le vrai sujet des schemas en prod, c’est l’évolution.
  • Backward compatible : les anciens consumers continuent de fonctionner.
  • Forward compatible : les nouveaux consumers supportent d’anciens messages.
  • Breaking change : nécessite version majeure / endpoint v2 / migration plan.
# REST (typique)
                    - add field optional => OK
                    - remove field       => breaking
                    - change type string->int => breaking
                    - rename field       => breaking (sauf alias/deprecation)

                    # DB
                    - add column nullable => OK
                    - set NOT NULL sans backfill => risque (breaking en runtime)
                    - drop column => breaking pour ETL/BI/old code
  • Définir une policy deprecation : annonce + window + date de retrait.
  • Prefer “additive changes” (ajout) plutôt que modifications destructives.
  • Versionner les contrats : tags, releases, registry.
  • Mettre un “compat gate” en CI : refuser un breaking change non versionné.
  • “On change vite, les clients suivront” → non.
  • Deprecation sans date → personne ne migre.
  • Pas d’inventaire des consumers → impossible de savoir qui casse.
  1. Types & champs stables.
  2. Breaking change = version majeure ou endpoint v2.
  3. Deprecation documentée avec deadline.
  4. Compat checks automatisés.
VALIDATION PIPELINE
  • Le schema doit vivre dans le flux : entrée API → validations → DB → events → analytics.
  • Idéal : les erreurs de contrat deviennent des 422/400 propres, pas des 500.
Client
                    |
                    v
                    API Gateway / Router
                    |
                    v
                    (1) Validate request schema  -----> reject 400/422 (with details)
                    |
                    v
                    (2) Business rules + auth
                    |
                    v
                    (3) Persist with DB constraints  ---> reject 409/constraint error (mapped)
                    |
                    v
                    (4) Publish event with message schema -> registry compat gate
                    |
                    v
                    Consumers validate / parse -> store / act
  • Validation “shape” (types/required) tôt, règles métier ensuite.
  • Mapper proprement erreurs DB (unique/fk/check) vers erreurs API.
  • Centraliser les contrats (repo “schemas” / registry) pour éviter la divergence.
  • Tracer : quelles versions de schema sont utilisées en prod.
  1. Validation request/response (au moins request).
  2. DB constraints pour invariants critiques.
  3. Contrat event (schema evolution gérée).
  4. Observabilité des erreurs de validation.
TOOLS & PATTERNS
  • Pattern “contract-first” : spec → mocks → impl → tests → clients.
  • Pattern “contract-tests” : vérifier automatiquement la conformité (producer/consumer).
  • Pattern “schema registry” : un référentiel, un ownership, des règles d’évolution.
# Idées d’automatisation (CI)
                    - Lint schema (format + conventions)
                    - Validate fixtures (valid/invalid)
                    - Diff compat (old vs new)
                    - Generate docs (HTML/markdown)
                    - Generate clients (si besoin)
                    - Publish schema artifact (registry)
# “Contract tests” minimalistes
                    Given: OpenAPI/JSON Schema
                    When: run tests against API
                    Then: responses match schemas + required fields are present
  • Nommer les objets : User vs UserV2 (éviter V2 trop tôt, préférer compat).
  • Éviter les duplications : un schema “User” réutilisé partout via ref.
  • Limiter les “any/unknown” aux zones réellement extensibles.
  • Mettre des “examples” dans les schemas (accélère FE & QA).
  • Docs séparées du schema → drift.
  • Règles d’évolution implicites (“au feeling”).
  • Aucune ownership → personne n’ose dire non aux breaking changes.
  1. Repo/registry des schemas + ownership.
  2. Compat gate en CI.
  3. Fixtures valid/invalid.
  4. Docs auto + exemples.
RÉFÉRENCES
  • Quand tu dis “schema”, précise le contexte : DB / JSON / API / GraphQL / Events / SEO.
  • Le bon réflexe : contract + validation + versioning.
  1. Contrats centralisés + ownership.
  2. Validation automatisée (CI) + erreurs propres (runtime).
  3. Versioning & compat (deprecation policy).
CHEAT-SHEET
SCHEMA (web/dev) = règle qui décrit la structure des données.

                    DB schema      : tables/colonnes/types/contraintes (+ schema namespace)
                    JSON Schema    : validation de JSON (required, types, $ref)
                    OpenAPI        : contrat API REST (request/response/errors)
                    GraphQL schema : types/queries/mutations (+ deprecations)
                    Avro/Proto     : contrat events/messages + evolution rules
                    schema.org     : données structurées SEO (JSON-LD)
SAFE (souvent) :
                    - ajouter un champ optional
                    - ajouter une colonne nullable
                    - ajouter un nouvel endpoint / nouveau type

                    BREAKING (souvent) :
                    - supprimer/renommer un champ
                    - changer type string->int
                    - rendre obligatoire un champ sans default/backfill
                    - supprimer une colonne utilisée par ETL/BI
“Pour moi un schema, c’est un contrat formel :
                    1) il définit les types et contraintes,
                    2) il est validé automatiquement (CI + runtime),
                    3) il est versionné avec une politique de compatibilité.
                    Ça évite les ambiguïtés FE/BE/DB et réduit les incidents de prod.”