L'évolution de schéma casse quand le chevauchement de versions n'est pas mesuré
Un changement de schéma casse en production non pas parce que la migration est mauvaise mais parce que le chevauchement de versions n'est pas mesuré. Theorem 3 sur la compatibilité ascendante et descendante, expand-and-contract, et le contrat wire à la frontière.

L'évolution de schéma casse quand le chevauchement de versions n'est pas mesuré
Un changement de schéma est généralement l'un des types de changement les plus difficiles pour un système logiciel, et l'article de ByteByteGo « Schema Evolution: Changing the Contract Without Breaking What Runs » ouvre avec la raison honnête du pourquoi : la migration tourne propre en staging, et des services sans rapport commencent à casser en production, et il n'y avait rien de mauvais avec la migration elle-même (ByteByteGo, « Schema Evolution: Changing the Contract Without Breaking What Runs », 20 août 2026, https://blog.bytebytego.com/p/schema-evolution-changing-the-contract). La panne n'est pas le changement. La panne est l'hypothèse qu'une seule version de schéma est en jeu. Des lignes écrites il y a des années sont lues par du code qui a depuis été remplacé. Des messages en file ont été publiés avant que le consommateur actuel ne soit écrit. Des versions d'app mobile d'il y a dix-huit mois sont encore installées et appellent encore l'API. La propriété « pas de casse lors d'un changement de schéma » est garantie exactement quand le mécanisme — compatibilité ascendante et descendante plus séquencement expand-and-contract — est implémenté et mesure si deux versions sont encore vivantes. Theorem 3 : une propriété est garantie exactement quand son mécanisme est implémenté et mesure.
Conclusions clés
- Les changements de schéma cassent la production non pas parce que la migration est mauvaise, mais parce qu'elle prend effet alors que deux versions de l'application tournent encore contre la même base de données, et qu'une seule de ces versions référençait le schéma modifié (ByteByteGo, 20 août 2026).
- Plus d'une version de schéma est toujours en jeu : lignes écrites il y a des années, messages en file avant le consommateur actuel, apps mobile âgées de dix-huit mois qui appellent encore l'API — des données écrites sous une version de schéma sont lues sous une autre.
- « Pas de casse » est une propriété qui tient exactement quand la compatibilité ascendante et descendante est implémentée comme mécanismes et que le chevauchement de versions est mesuré. Un changement de schéma sans mécanisme de compatibilité est un changement de contrat sans garantie.
- Theorem 3 : une propriété est garantie exactement quand son mécanisme est implémenté et mesure. Expand-and-contract est le mécanisme ; la timeline de déprecation qui trace le dernier vieux lecteur est la mesure.
Plus d'une version de schéma est toujours en jeu
L'intro de ByteByteGo nomme le fait structurel que chaque post-mortem de changement de schéma redécouvre : la migration se déroule sans accroc en staging, et des services sans rapport cassent en production, et l'investigation ne trouve rien de mauvais avec la migration — elle a pris effet alors que deux versions de l'application tournaient encore contre la même base de données, et qu'une seule de ces versions référençait le schéma modifié. Ce n'est pas un écart staging-production. C'est une hypothèse à une version qui rencontre une réalité à plusieurs versions. Le staging teste la migration contre le nouveau code. La production exécute la migration contre le nouveau code et le vieux code et les vieilles données que le vieux code a écrites et les messages en file que le vieux éditeur a écrits. L'environnement de staging est une topologie à version unique ; la production est une topologie à chevauchement de versions.
[UNIQUE INSIGHT] Le chevauchement de versions n'est pas borné par la fenêtre de déploiement. L'intro de ByteByteGo est explicite : des lignes écrites il y a des années peuvent être produites par du code d'application qui a depuis été remplacé. Des messages en file ont été publiés avant que la version actuelle du consommateur ne soit écrite. Des versions d'app mobile d'il y a dix-huit mois sont encore installées sur de vrais appareils et appellent encore l'API. La fenêtre de déploiement est le plus petit chevauchement ; le chevauchement d'état durable — vieilles lignes, messages en file, clients mobile installés — est le chevauchement qui vous casse réellement. Un plan de changement de schéma qui ne tient compte que de la fenêtre de déploiement mesure le plus petit chevauchement de versions et affirme la propriété sur le plus grand. C'est l'écart entre le mécanisme implémenté (coordination de fenêtre de déploiement) et le mécanisme qui garantirait la propriété (suivi complet du chevauchement de versions, y compris l'état durable et les clients installés).
Le Honest Architect lit la prétention de ByteByteGo comme une prétention topologique, pas une prétention de processus. « More than one schema version is always in play at the same time » est un énoncé sur la forme d'un système distribué : éditeurs et lecteurs sont découplés dans le temps, et le schéma est le contrat qui enjambe la lacune temporelle. Un changement du contrat n'est sûr que s'il enjambe chaque lecteur encore vivant, y compris les lecteurs qui étaient vivants quand les données ont été écrites et les lecteurs qui seront vivants quand les données seront lues. La compatibilité ascendante et descendante sont deux mécanismes différents, pas un seul.
Compatibilité ascendante et descendante sont deux mécanismes différents
La compatibilité ascendante (backward) est la propriété que le nouveau code lit les vieilles données. La compatibilité descendante (forward) est la propriété que le vieux code lit les nouvelles données. Elles paraissent symétriques de nom et ne sont pas symétriques de mécanisme. La compatibilité ascendante est une propriété que l'auteur du nouveau code peut garantir en tolérant les champs manquants — le nouveau code sait à quoi ressemblait le vieux schéma. La compatibilité descendante est une propriété que l'auteur du nouveau code ne peut pas garantir seul, parce que le vieux code est déjà déployé ; la seule garantie est de faire le changement d'une façon que le vieux code tolère déjà (ajouter des champs optionnels, ne pas retirer des champs que le vieux code lit, ne pas changer la sémantique des champs). La compatibilité ascendante est une garantie au moment de l'écriture ; la compatibilité descendante est une contrainte au moment de la conception sur le changement lui-même.
L'article de ByteByteGo promet de couvrir « which changes break consumers, which do not, and the qualifiers that decide it » — et les qualificatifs sont le mécanisme. Ajouter un champ optionnel est compatible ascendant (nouveau code lit vieilles données qui manquent le champ) et compatible descendant (vieux code lit nouvelles données qui ont un champ supplémentaire qu'il ignore). Renommer un champ n'est ni l'un ni l'autre : vieux code lit nouvelles données et cherche le vieux nom de champ, le trouve manquant, casse. Retirer un champ que le vieux code lit est incompatible descendant. Changer le type ou la sémantique d'un champ est à la fois incompatible ascendant et descendant même si les octets wire sont identiques, parce que le contrat est le sens, pas les octets. Le Honest Architect traite un renommage ou un changement de type comme une rupture de contrat, pas un changement de schéma — il viole une supposition qu'un lecteur vivant fait.
[PERSONAL EXPERIENCE] Les types wire d'Everythink sont définis une fois, en Zod, dans @everythink/types, et les réponses sont analysées à la frontière réseau. C'est un registre de schéma à la frontière, pas juste une définition de type. Le schéma Zod est le contrat ; l'analyse à la frontière est la mesure — un payload qui ne correspond pas au schéma remonte comme une ApiError typée, jamais un crash. Nous taguons cela Production ✅ parce que le mécanisme (parse-at-boundary) est implémenté et la mesure (l'erreur typée) tourne sur chaque réponse. Un changement de schéma backend sans changement correspondant du schéma Zod est un changement de contrat sans mesure côté consommateur — Partial ⚠️ jusqu'à ce que le schéma Zod soit mis à jour et que le parse-at-boundary attrape la dérive.
L'invariante de l'Oracle — les probabilités sont normalisées en un seul endroit, everythink-oracle::ensemble, de sorte que les consommateurs peuvent s'appuyer sur sum(probability) ≈ 1.0 — est un contrat de schéma que le code en aval lit. Un changement du site de normalisation ou de l'ordre de tri serait une rupture de contrat même si les octets wire paraissaient identiques, parce que le contrat est la garantie sur laquelle les consommateurs s'appuient. Nous taguons cette invariante Production ✅ parce que le mécanisme (site unique de normalisation) est implémenté et la mesure (les tests d'ensemble) tourne. La discipline d'évolution de schéma pour cette invariante est : ne jamais déplacer le site de normalisation sans une séquence expand-and-contract qui laisse les vieux consommateurs continuer à lire la vieille garantie pendant que les nouveaux lisent la nouvelle.
Expand and Contract — le move topologique
Expand and Contract est le mécanisme qui rend un changement de contrat sûr sous chevauchement de versions. Expand : ajouter le nouvel élément de schéma d'une façon à la fois compatible ascendante et descendante. Laisser les vieux lecteurs continuer à lire le vieux contrat et les nouveaux lecteurs commencer à lire le nouveau. Attendre que le chevauchement de versions se tarisse — vieux clients se mettre à jour, vieux messages consommés, vieilles lignes vieillir et sortir. Contract : retirer le vieil élément de schéma une fois qu'aucun lecteur vivant ne le référencie. La phase expand est l'expansion topologique (deux routes coexistent) ; la phase contract est la contraction topologique (une route reste).
La mesure qui pilote la phase contract est la timeline de déprecation qui trace le dernier vieux lecteur. ByteByteGo promet « versioning strategies and deprecation timelines » — et la timeline de déprecation est la mesure qui fait d'expand-and-contract une propriété garantie plutôt qu'un espoir. Le Honest Architect tague un plan expand-and-contract sans mesure du dernier vieux lecteur Partial ⚠️ : le mécanisme (expand, attendre, contract) est implémenté, mais la mesure qui pilote le contract ne l'est pas. Un plan qui trace le dernier vieux lecteur — par télémétrie de version de client, par âge de message en file, par tags de version de schéma de ligne — et ne contracte que quand ce compte atteint zéro est Production ✅.
[ORIGINAL DATA] La discipline de migration d'Everythink tague cela directement. Chaque migration .up.sql a une .down.sql correspondante — c'est le chemin de rollback, le contrat qu'un expand échoué peut être annulé. La .down.sql est le filet de sécurité de la phase expand : si l'expand casse un lecteur vivant, vous contractez le changement de schéma (exécutez la migration down) et les vieux lecteurs reprennent. Nous taguons le chemin de rollback Production ✅ parce que chaque migration en a un et que le migrateur impose le couplage. Une migration qui ajoute et droppe en une étape est un cutover, pas un expand-and-contract — Partial ⚠️ si le vieux code est encore vivant, parce que la phase contract court à l'intérieur du chevauchement de versions.
Le cache geo du World Monitor porte la même discipline sous une forme différente. Les ids des GeoSignal sont déterministes uuidv5(source, native_id) — le ré-ingest met à jour, ne crée jamais de doublons. C'est un mécanisme de compatibilité descendante pour le cache : un nouvel ingest du même signal met à jour la ligne au lieu de créer une seconde ligne, de sorte qu'un lecteur qui a vu le vieil id et un lecteur qui voit le nouvel id lisent la même ligne. La stabilité de l'id est le contrat ; l'uuidv5 déterministe est le mécanisme ; l'upsert est la mesure (le nombre de lignes ne croît pas au ré-ingest). Nous taguons cela Production ✅ parce que le mécanisme est implémenté et que la stabilité du nombre de lignes est observable. Un changement de schéma du schéma d'ids serait un renommage de la clé primaire — le changement le plus incompatible descendant qui soit — et exigerait un expand-and-contract qui écrit les deux ids et migre les lecteurs avant de dropper l'ancien.
Ce qu'un Honest Architect lit dans une intro derrière paywall
L'article de ByteByteGo est derrière paywall après le titre de section « Version Overlap », et le Honest Architect ne fabrique pas le corps. Ce qui est visible est la prétention structurelle — plus d'une version de schéma est toujours en jeu, la migration est propre en staging et casse en production, la panne n'est pas la migration mais le chevauchement de versions — et cette prétention suffit pour appliquer la discipline de taggage. L'intro visible donne : la réalité multi-version (Production ✅ comme fait structurel des systèmes distribués), l'écart staging-production comme différence de topologie version-unique-contre-multi-versions (Production ✅ comme cadrage), et la promesse de compatibilité ascendante/descendante, expand/contract, registres de schéma et timelines de déprecation comme mécanismes.
La règle du Honest Architect : citer la vraie source, ne jamais fabriquer d'URL ou de métrique, ne jamais prétendre que l'article a dit quelque chose qu'il n'a pas dit. L'intro dit que la migration tourne propre en staging et casse en production parce que deux versions tournent contre la même base de données. C'est la prétention citée. Le reste de l'analyse est le mécanisme appliqué à la propre stack d'Everythink, taggué contre notre propre implémentation, non attribué à ByteByteGo. La prétention cross-domain (expand-and-contract est le même move topologique dans un schéma de base de données et dans un ensemble de prévisions) est Partial ⚠️ parce que la forme est partagée et les domaines sont séparés.
La limite de périmètre : Everythink est une plateforme de prévision civile et défensive, pas un cabinet de conseil en bases de données. La leçon d'évolution de schéma est cross-domain — un changement de contrat n'est sûr que quand le chevauchement de versions est mesuré — et elle s'applique que le contrat soit un schéma de base de données, un payload d'API, un type Zod ou une invariante d'ensemble de prévisions. Aucun résultat de token, wallet ou community-credit n'est promis ; ceux-ci sont Roadmap 🔵, soumis à revue Howey. Un item Roadmap n'est jamais promu discrètement à Production sur la force d'une forme d'évolution de schéma.
Questions fréquemment posées
Pourquoi un changement de schéma casse-t-il la production quand la migration a tourné proprement en staging ?
Parce que le staging teste la migration contre le nouveau code uniquement, et la production exécute le nouveau code à côté du vieux code, des vieilles données, des messages en file et des clients mobile installés qui ont été écrits sous le vieux schéma. La migration est propre ; le chevauchement de versions ne l'est pas. La panne n'est pas le changement — c'est l'hypothèse qu'une seule version de schéma est en jeu. Theorem 3 : la propriété « pas de casse » n'est garantie que quand le mécanisme de chevauchement de versions est implémenté et mesure.
Quelle est la différence entre compatibilité ascendante et descendante ?
La compatibilité ascendante (backward) est la propriété que le nouveau code lit les vieilles données — l'auteur du nouveau code la garantit en tolérant les champs manquants. La compatibilité descendante (forward) est la propriété que le vieux code lit les nouvelles données — l'auteur du nouveau code ne peut pas changer le vieux code, donc la seule garantie est de faire le changement d'une façon que le vieux code tolère déjà (ajouter des champs optionnels, ne pas retirer ou renommer des champs que le vieux code lit). La compatibilité ascendante est une garantie au moment de l'écriture ; la compatibilité descendante est une contrainte au moment de la conception sur le changement lui-même.
Qu'est-ce qu'expand and contract ?
Expand : ajouter le nouvel élément de schéma d'une façon à la fois compatible ascendante et descendante. Laisser vieux et nouveaux lecteurs coexister. Attendre que le chevauchement de versions se tarisse — vieux clients se mettre à jour, vieux messages consommés, vieilles lignes vieillir et sortir. Contract : retirer le vieil élément de schéma une fois qu'aucun lecteur vivant ne le référencie. La phase expand est une expansion topologique (deux versions de contrat coexistent) ; la phase contract est une contraction topologique (une version reste).
Comment Everythink impose-t-il l'évolution de schéma à la frontière wire ?
Les types wire sont définis une fois, en Zod, dans @everythink/types. Les réponses sont analysées à la frontière réseau ; un payload qui ne correspond pas au schéma remonte comme une ApiError typée, jamais un crash. Le schéma Zod est le contrat ; le parse-at-boundary est la mesure. Un changement de schéma backend sans changement correspondant du schéma Zod est un changement de contrat sans mesure côté consommateur — Partial ⚠️ jusqu'à ce que le schéma Zod soit mis à jour.
Everythink promet-il que les changements de schéma ne cassent jamais les consommateurs ?
Non. Nous promettons le mécanisme : parse-at-boundary Zod, couplage .up.sql/.down.sql, invariante Oracle de site unique de normalisation, uuidv5 déterministe pour le cache du World Monitor. La propriété « pas de casse sur un changement donné » tient quand le changement suit expand-and-contract et que la timeline de déprecation trace le dernier vieux lecteur. Un changement qui cuttover à l'intérieur du chevauchement de versions est Partial ⚠️. Aucun résultat de token, wallet ou community-credit n'est promis ; ceux-ci sont Roadmap 🔵.
Sources
- ByteByteGo, « Schema Evolution: Changing the Contract Without Breaking What Runs », 20 août 2026, récupéré 2026-08-23, https://blog.bytebytego.com/p/schema-evolution-changing-the-contract
Si votre équipe est prête à mesurer le chevauchement de versions, pas seulement la fenêtre de déploiement, créez votre réseau — la topologie route deux versions de contrat, la frontière parse, le chemin de rollback annule.

La personnalisation est la séparation des mécanismes, pas les poids ouverts
Inkling est conçu pour être personnalisé non pas à cause de sa licence Apache 2.0 mais parce que chaque décision architecturale isole une propriété mesurable derrière son propre mécanisme. L'équilibrage de charge fondé sur le biais est l'instance la plus pure de Theorem 3 : une propriété garantie par un mécanisme qui ne concurrence pas l'objectif principal.
→ →
Géolocaliser une adresse MAC nécessite le mécanisme, pas l'identifiant
Une adresse MAC ne contient pas de GPS, mais une base de wardriving plus une fusion de centroïde pondérée par signal peut géolocaliser un point d'accès fixe. Theorem 3: la propriété vient du mécanisme, pas de l'identifiant.
→ →
L'enrichissement de données est cohérence, pas volume
Plus de données ne signifie pas automatiquement meilleur insight. Théorème 3 : la propriété (meilleur insight) vient du mécanisme (vérification de cohérence entre points de données), pas du volume. La valeur est de meilleures questions, pas de certitude.
→ →Construisez votre monde sur un moteur qui prouve ce qu'il affirme.
Créez votre propre réseau sur le moteur qui tourne depuis 2016 — ou parlez à l'équipe derrière les 21 articles.
