Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

La carte, la légende, et le compilateur mou

La Grande Vague — Hokusai
La Grande Vague — Hokusai

Hokusai a essayé de fixer la vague. La vague a continué. La carte n'est pas le territoire — mais ici, la carte est devenue un événement plus durable que la plupart des territoires qu'elle prétendait décrire.

« We must be systematic, but we should keep our systems open. » — Alfred North Whitehead, cité par John F. Sowa dans sa Top-Level Ontology

« Tout ça me parle beaucoup en 2009 du fait que la carte — le YAML — n'est pas le territoire — le code généré, exécuté. » — Stéphane Erard, en avril 2026, en relisant Korzybski quinze ans après l'avoir vécu sans le nommer.

Avertissement au lecteur

Cette page est un article d'onramp. Elle est en français parce que la formule de départ ne se traduit pas sans perte, et parce que je voulais une fois écrire dans ma langue ce que j'écris d'habitude en anglais sur le reste de ce blog. Elle sert à entrer plus doucement dans le diptyque que j'ai déjà publié sur ce site : la version longue technique, en anglais, est dans MDE and Métacratie, et la version longue politique-philosophique, en français, est dans Appareil et compilateur. Cette page-ci est plus courte, plus imagée, et elle n'essaie pas de remplacer les deux autres. Elle essaie de poser, en quelques pages, la formule récursive qui m'occupe depuis quinze ans sans que je l'aie écrite en clair.

La voix est assertive, première personne quand nécessaire. Je préfère engager les références (Korzybski, Tarski, Hofstadter, Sowa, Brian Cantwell Smith) directement plutôt que de les citer en notes elliptiques. Je préfère également raconter ma propre expérience — Diem, 2009, le moment où j'ai vu sans le nommer ce que cet article dit. Le lecteur qui veut le détail technique trouvera tout dans le diptyque ; le lecteur qui veut juste comprendre pourquoi cet article s'appelle « la carte, la légende, et le compilateur mou » devrait pouvoir le faire en vingt minutes ici.


Korzybski et Tarski, 1933 — la coïncidence

L'année 1933 est une de ces années que les historiens des idées devraient étudier pour elles-mêmes. La même année où Alfred Korzybski publie Science and Sanity à Lakeville, Connecticut, et frappe la formule qui fera fortune — « A map is not the territory » —, Alfred Tarski publie à Varsovie The Concept of Truth in Formalized Languages, et démontre formellement qu'on ne peut pas définir la vérité d'un langage formel dans ce langage : il faut un méta-langage strictement plus expressif, et ce méta-langage a besoin à son tour d'un méta-méta-langage pour parler de sa propre vérité. La hiérarchie est infinie en droit, mais en pratique on s'arrête au niveau qu'on peut tenir.

Deux hommes. Deux disciplines apparemment éloignées : la sémantique générale d'un côté, la logique formelle de l'autre. Et pourtant le même problème : la non-identité structurelle entre un système qui désigne et ce qu'il désigne, et l'impossibilité de fermer le geste sur lui-même sans monter d'un cran. Korzybski écrit en philosophe-thérapeute pour des lecteurs qui souffrent du mot pris pour la chose ; Tarski écrit en logicien pour des collègues qui veulent éviter le paradoxe du menteur. Les deux disent la même chose. Et les deux atteignent par la même route le même mur : il existe une régression méta, et elle n'est pas un défaut, c'est une propriété structurelle du fait d'avoir un langage du tout.

Korzybski a fait fortune et s'est usé. « La carte n'est pas le territoire » est devenu un slogan de séminaire de management, vidé de sa substance, jusqu'à devenir le genre de phrase qu'on cite pour avoir l'air profond sans rien dire de profond. C'est dommage, parce que la formule originelle est précise : Korzybski parle d'une non-identité structurelle, et il en tire une thérapie linguistique entière. Tarski, lui, est resté dans les départements de logique mathématique, et personne en dehors de ces départements ne sait qu'il a démontré, en 1933, ce que les sémanticiens généraux essayaient de dire dans un autre vocabulaire. C'est dommage aussi, parce que les deux s'éclairent mutuellement, et que l'argument que je veux développer ici a besoin des deux.

La lignée que je veux nommer maintenant, pour qu'on sache d'où je parle, va plus loin. Korzybski (1933) pose la non-identité carte/territoire. Tarski (1933) la démontre côté logique formelle, en montrant que la vérité d'un langage est indéfinissable en interne. Gregory Bateson, dans Steps to an Ecology of Mind (1972), reprend Korzybski et fait observer le détail crucial que Korzybski lui-même n'avait pas creusé : la carte qui se redessine est elle-même un événement dans le territoire qu'elle décrit. Le geste cartographique change le territoire, ne serait-ce que parce qu'il y ajoute une carte. Douglas Hofstadter, dans Gödel, Escher, Bach (1979), nomme ce qui se passe quand un système se prend lui-même pour objet : les boucles étranges. C'est un autre vocabulaire pour la même chose, et c'est le vocabulaire le plus pédagogique qu'on ait. Brian Cantwell Smith, dans Reflection and Semantics in Lisp (1984), donne le texte canonique de l'informatique réflexive : un reflective compiler est un compilateur qui se prend lui-même comme objet de calcul. C'est exactement ce que veut dire « compilateur mou » dans ma langue à moi, et sans Smith dans la lignée j'aurais l'air d'inventer ce qui existe depuis quarante ans. John F. Sowa, enfin, dans Conceptual Structures: Information Processing in Mind and Machine (1984 — la même année que Smith), reprend les graphes conceptuels de Peirce et construit la première ontologie formelle qui se décrit elle-même de manière implémentable. C'est l'ancêtre direct du Meta-Object Facility (MOF) de l'OMG qui structure tout le travail de ce blog. Et derrière Sowa, en arrière-plan permanent, il y a Whitehead, dont la maxime que Sowa met en exergue de sa Top-Level Ontology« We must be systematic, but we should keep our systems open » — est, en sept mots, l'éthique de tout cet article.

Cette lignée n'est pas une parade d'autorités. C'est la chaîne par laquelle la même intuition descend, étage par étage, du discours philosophique vers le code compilable. Korzybski parlait à des humains qui souffraient d'identifier le mot et la chose ; Tarski parlait à des logiciens qui voulaient éviter les paradoxes ; Smith et Sowa parlent à des informaticiens qui veulent construire des systèmes capables de se relire eux-mêmes. C'est la même histoire à chaque étage, et l'étage où je travaille moi est celui de Smith et Sowa.


La récursion qu'on n'a pas pu éviter

J'arrive au paragraphe central de cet article. La formule qui m'occupe depuis quinze ans, et que je n'avais jamais écrite en clair, tient en deux questions :

Qu'est-ce qu'un modèle, sinon une carte avec une légende ?

Et qu'est-ce qu'une légende, sinon une carte, avec une légende, décrivant une carte utilisant cette légende ?

La question fait peur la première fois qu'on la pose, parce qu'elle a l'air de tourner sur elle-même comme un serpent qui se mord la queue. Elle ne tourne pas. Elle descend, étage par étage, et elle se stabilise. Trois étages suffisent pour que l'affaire tienne debout, et le troisième étage est celui où la chose remarquable arrive.

Premier étage — la carte. C'est votre domaine particulier : un schéma de base de données, un workflow d'approvisionnement, une API REST, une page web avec ses formulaires. La carte est l'objet sur lequel vous travaillez tous les jours. Elle a un nom, elle a des contraintes, elle a des comportements. Elle est concrète, et c'est par elle que vous gagnez votre vie.

Deuxième étage — la légende. C'est ce qui dit comment lire la carte du premier étage. Pour la base de données, la légende c'est l'Entity-Relationship — la grammaire qui dit qu'il existe des entités, des attributs, des relations un-à-plusieurs et plusieurs-à-plusieurs, des clés primaires et étrangères. Pour le workflow, la légende c'est BPMN ou un équivalent — la grammaire qui dit qu'il existe des étapes, des transitions, des conditions, des acteurs. Pour l'API, la légende c'est OpenAPI — la grammaire qui dit qu'il existe des routes, des verbes HTTP, des schémas de requête et de réponse, des codes d'erreur. La légende est elle-même un objet formel, et c'est là le point qu'on rate souvent : la légende n'est pas un commentaire en marge de la carte, c'est un objet à part entière, qu'on peut décrire, comparer, valider, faire évoluer.

Troisième étage — la légende des légendes. Et ici, la chose remarquable arrive. Si la légende est un objet, alors elle a elle-même besoin d'une grammaire qui dit ce qu'est une légende en général : un vocabulaire qui parle d'entités, d'attributs, de relations, de contraintes, de l'héritage entre concepts. Ce vocabulaire est ce qu'on appelle dans la tradition MOF un meta-meta-modèle, et il est composé d'une poignée minimale de primitives : [MetaConcept], [MetaProperty], [MetaReference], [MetaConstraint], [MetaInherits]. Cinq primitives suffisent. Et ces cinq primitives sont elles-mêmes décrites en utilisant ces cinq primitives. M3 se décrit lui-même. Le point fixe est atteint. La récursion s'arrête, non parce qu'on l'a forcée, mais parce qu'on a trouvé le niveau auto-suffisant.

C'est exactement le geste de MOF (Meta-Object Facility, OMG, standardisé en 1997 puis en 2003), et c'est le geste central de tout le travail MDE qui structure ce blog. Korzybski avait nommé le problème (la non-identité de la carte et du territoire) ; Hofstadter en avait nommé la forme (les boucles étranges qui se stabilisent par auto-référence) ; MOF en a livré la construction industrielle vingt ans plus tard, en proposant un standard implémentable qui dit en clair comment on construit un méta-méta-modèle qui se décrit lui-même.

Je veux poser ici un caveat honnête, parce que c'est exactement ce qu'exige la maxime de Whitehead. On ne sort pas de la régression au sens absolu. Tarski et Gödel l'ont prouvé en 1931–1933 : un système formel suffisamment riche pour parler de lui-même ne peut pas démontrer sa propre cohérence depuis l'intérieur. M3 ne se justifie pas lui-même. Il pose. La récursion ne se ferme pas, elle se comprime — jusqu'à un point fixe opérationnel qui suffit pour que le compilateur tourne, mais qui n'est pas un absolu logique. C'est tout ce qu'un compilateur fait, et c'est tout ce qu'on peut honnêtement promettre. Sowa lui-même, dans sa Top-Level Ontology, écrit cette phrase admirable d'honnêteté intellectuelle : « the other axioms cannot be stated formally until a great deal more has been fully formalized ». Il reconnaît la régression infinie sans la résoudre, et c'est précisément parce qu'il la reconnaît qu'on peut bâtir sur lui sans illusion.

Voici la récursion sous forme de diagramme. Trois étages plus le runtime, avec la self-loop sur M3 qui marque le point fixe :

Diagram
La récursion MOF en quatre étages — M0 runtime, M1 carte du domaine, M2 légende DDD/Workflow, M3 légende des légendes qui se décrit elle-même au point fixe.

La flèche qui revient sur M3 est ce que Hofstadter appelle une boucle étrange. Elle n'est pas une faute logique — c'est la condition même de possibilité d'avoir un système qui ne se mente pas à lui-même.


La pile en termes de cartes — et son écho politique

Je viens de poser MOF en quatre niveaux. Je vais le reformuler une dernière fois en langage cartographique pour que le lecteur non-MDE l'attrape sans avoir à lire la spec OMG. M3 est la légende des légendes, le minimum auto-suffisant. M2 est l'ensemble des légendes spécialisées que je construis sur M3 — dans le CMF de ce blog, ce sont les six DSLs cartographiés ailleurs : DDD.DSL, Content.DSL, Admin.DSL, Pages.DSL, Workflow.DSL, Requirements.DSL. M1, c'est la carte de votre domaine particulier — le code que vous écrivez en utilisant les attributs M2. M0, c'est le territoire en marche, l'instance runtime qui vit dans la mémoire d'une JVM ou d'un processus dotnet, et qui répond à de vraies requêtes pour de vrais utilisateurs.

Cette pile a une propriété qui n'est pas un détail : elle est asymétriquement mobile. M3 ne bouge presque jamais — le standard MOF de l'OMG est figé depuis 2003, et y toucher reviendrait à recompiler toute la pile MDE de la planète. M2 bouge à l'année — vous redessinez un DSL quand votre domaine évolue de manière structurelle, ce qui arrive plusieurs fois par an dans une équipe vivante mais pas tous les jours. M1 bouge à la semaine — chaque ajout d'attribut sur une classe, chaque nouvelle entité, chaque nouvelle règle métier est une modification de M1. M0 bouge à la milliseconde — c'est le runtime, c'est la requête HTTP en cours, c'est l'événement qui passe sur le bus. Chaque couche a sa propre cadence, et la santé du système dépend du fait que ces cadences sont respectées.

Et voici le pont — celui que je développe en long dans le diptyque, et que je nomme ici en bref. La pile MOF a le même profil de mobilité asymétrique que la pile SUFRA de la métacratie. SUFRA, dans le vocabulaire que j'ai construit dans le dossier philosophique de ce site, est un acronyme pour cinq couches superposées d'un système politique : SUPRA (les mythes fondateurs, la doxa qu'on n'a pas besoin de nommer parce qu'elle est partagée par défaut), SUPER (le discours constitutionnel et juridique explicite, les lois écrites), INTER (les corps intermédiaires, les réseaux d'acteurs, les milieux), INFRA (les substrats matériels — câbles, réseaux de paiement, sols, climats), et SUFRA (la couche de l'opacité contemporaine, ce qui se passe maintenant mais qui ne devient lisible qu'avec dix ou vingt-cinq ans de retard, par déclassification ou aveu volontaire). Les cadences sont elles aussi asymétriques : INFRA bouge en décennies, SUPRA est silencieusement recadrée plutôt que réécrite, SUPER joue le rattrapage avec ce que INTER a déjà fait, INTER se reconfigure en premier dans toute crise.

Diagram
L'analogie entre piles MOF et SUFRA — les deux structures partagent une mobilité asymétrique, où chaque couche a sa propre cadence (du siècle à la milliseconde) qu'il faut respecter pour que le système ne mente pas.

L'analogie n'est pas une homothétie cosmétique. Ce qui rend les deux piles compatibles, c'est précisément la mobilité asymétrique des couches — et c'est aussi ce qui justifie l'argument central de cet article : la carte doit pouvoir bouger à la vitesse de sa propre couche, sinon elle ment. Si M2 ne peut pas évoluer parce que son support technologique est figé (un format binaire propriétaire, un compilateur monolithique, une chaîne de génération hors-band), alors votre légende prend du retard sur les besoins de votre territoire, et le système entier finit par mentir poliment à ses utilisateurs. La maturité d'un écosystème MDE se mesure exactement à ça : à la liberté de mouvement qu'il laisse à chacune de ses couches sans casser les couches voisines. Et c'est ici que la question du compilateur arrive, parce que le compilateur, dans cette pile, est le seul mécanisme qui peut tenir l'ensemble cohérent quand une couche bouge.


Le compilateur mou — thèse, code, limites, contre-exemple Diem

J'arrive à la deuxième moitié de l'article, celle qui justifie le mot bizarre du titre. Pourquoi « mou » ? Le mot est délibérément choisi. En français informatique on dit normalement souple ou malléable ; je dis mou parce que la métaphore de matière compte pour ce que je veux dire. Le hardware est gravé dans le silicium, et le silicium est dur. Le software est pétri par un compilateur, et la pétrissabilité a des degrés. Mou contre dur, c'est la métaphore matérielle ; souple contre rigide, ce serait la métaphore mécanique. Je préfère la matérielle parce qu'elle dit mieux ce qui se passe quand un système est assez plastique pour être reformé par un nouveau geste, et trop dur pour l'être quand il ne l'est pas.

Mais voici la précision importante : tous les softs ne sont pas également mous. Un binaire C++ compilé statiquement à la chaîne d'outils GCC d'il y a vingt ans, déployé sans symboles, sans réflexion, sans plugin runtime, est presque aussi dur qu'un ASIC. Vous voulez ajouter une fonctionnalité ? Vous recompilez tout, vous redéployez tout, vous redémarrez tout. Le coût marginal est énorme. À l'autre extrême, un système Lisp réflexif (au sens du Lisp 1.5 originel ou de Smalltalk) est presque aussi mou que de la pâte : vous modifiez une classe à chaud, l'objet existant continue à vivre, le runtime se reconfigure, et personne n'a relancé quoi que ce soit. La gamme complète va de l'ASIC au Lisp, et la position que prend un écosystème sur cette gamme n'est pas un détail — c'est une décision architecturale fondamentale.

Et voici le point que je veux poser, parce qu'il est faux de représenter cette gamme par une ligne. L'axe est en réalité un plan, pas une ligne, et c'est ce que le diagramme suivant essaie de rendre lisible. Deux dimensions, pas une :

  • X — Mobilité du méta-modèle : à quel point peut-on changer la légende sans casser la carte ? À quel point le système est-il prêt à se reformer ?
  • Y — Surface de vérification statique : à quel point le compilateur refuse-t-il les incohérences au moment du build, plutôt qu'au moment du runtime ? À quel point est-on protégé contre les erreurs avant qu'elles arrivent à l'utilisateur ?

Le piège classique, et c'est la raison pour laquelle l'axe est un plan, est qu'on ne gagne pas l'un sans payer l'autre. Lisp est en bas-droite : très mobile, peu vérifié au compile-time. Un binaire C++ statique est en haut-gauche : très vérifié, mais immobile. Java est quelque part au milieu, avec une vérification correcte mais un méta-modèle pauvre (la réflexion Java existe mais elle est verbeuse, et les annotations ne sont pas vraiment des objets de première classe). Et C# + Roslyn + Source Generators + analyzers est dans le coin haut-droite — et c'est rare. C'est ça que je veux dire quand j'écris « industriellement mature » : tenir les deux dimensions à la fois, sur 57 projets en monorepo, sans rupture de chaîne, sans phase de pré-traitement séparée, sans fichiers générés versionnés à la main, sans drift entre la carte et le territoire.

Diagram
Le plan (mobilité × vérification) — le coin haut-droite « mou et vérifié » est rare, et C# + Roslyn est aujourd'hui l'un des rares écosystèmes industriels à y tenir.

Le coin haut-droite est rare parce qu'il est cher à occuper. Pour y être, il faut que le compilateur soit lui-même un objet manipulable depuis le programme — un compilateur-bibliothèque, pas un exécutable monolithique. Il faut que les attributs soient des objets de première classe, vus à la fois par le générateur et par le code qui les porte. Il faut que la phase de génération se fasse dans la même compilation que le code écrit à la main, sans pipeline externe à orchestrer. Et il faut que tout cela reste vérifié — c'est-à-dire que le compilateur garde son refus de compiler un programme incohérent même quand une partie du programme a été générée par un autre programme. C'est beaucoup à demander. Et pourtant, c'est exactement ce que Roslyn fait depuis 2017 environ, et c'est ce que je vis tous les jours sur mon monorepo.

Un mini-exemple en code

Pour qu'un lecteur technique voie ce que « le compilateur se relit lui-même » veut dire concrètement, voici un bloc de C# court. Un attribut [Feature], un IIncrementalGenerator qui scanne tous les types portant cet attribut, et qui génère une classe Features exposant la liste des identifiants comme des constantes typées. Le code généré est compilé dans la même passe que le code qui le porte ; il est typé, vérifié, et relu par le compilateur sans phase intermédiaire :

[AttributeUsage(AttributeTargets.Class)]
public sealed class FeatureAttribute(string id) : Attribute
{
    public string Id { get; } = id;
}

[Feature("FEATURE-042")]
public partial class CarteLegendeBlogPost { }

// ─── Source Generator (extrait) ────────────────────────────────────
[Generator]
public sealed class FeatureRegistryGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext ctx)
    {
        var features = ctx.SyntaxProvider.ForAttributeWithMetadataName(
            "FeatureAttribute",
            predicate: static (_, _) => true,
            transform: static (g, _) =>
                (string)g.Attributes[0].ConstructorArguments[0].Value!);

        ctx.RegisterSourceOutput(features.Collect(), (spc, ids) =>
        {
            var consts = string.Join("\n    ",
                ids.Select(id => $"public const string {id.Replace('-','_')} = \"{id}\";"));
            spc.AddSource("Features.g.cs",
                $"public static class Features {{\n    {consts}\n}}");
        });
    }
}

Trois choses à dire en commentaire. Premièrement : l'attribut et le générateur sont compilés ensemble, dans la même passe Roslyn. Il n'y a pas de pré-processeur à lancer, pas de fichier généré versionné dans Git, pas de phase CI/CD séparée. Deuxièmement : Features.FEATURE_042 est désormais une constante typée que le compilateur peut vérifier ailleurs dans le code — n'importe quelle référence à cette constante est validée à la compilation, pas au runtime. Troisièmement — et c'est le point le plus important : si vous renommez l'attribut sur la classe, ou si vous le supprimez, la constante disparaît au prochain build, et tout le code qui l'utilisait casse au compile-time. Le compilateur refuse de produire un binaire incohérent. C'est ça, le « refus mécanisé » dont parle le diptyque, et c'est exactement ce que la pile MOF promet en théorie : la cohérence entre les couches est tenue par la chaîne de compilation, pas par la discipline humaine.

Ce mini-exemple n'est pas un jouet. C'est exactement le pattern qu'utilise le système [FeatureImplements] / [FeatureTest] du CMF Requirements DSL que je documente en long dans feature-tracking-ts, et qui fait partie de la série CMF qui couvre l'ensemble du framework. Ce que je décris dans cet article n'est pas une promesse théorique — c'est ce que mon monorepo de 57 projets fait tous les jours.

Limites honnêtes

Avant d'aller au contre-exemple Diem, je dois nommer ce que C# Roslyn ne fait pas qu'un Lisp réflexif fait. C'est important parce que sans cette honnêteté, j'aurais l'air de prétendre que C# a résolu un problème ouvert depuis 1958. Il ne l'a pas résolu. Il l'a déplacé.

La limite est la suivante : les Source Generators tournent au build, pas au runtime. Une fois que le binaire est compilé et déployé, le compilateur n'est plus dans la boucle. Si vous voulez ajouter une feature, vous recompilez et vous redéployez. C'est mou-au-build, ce n'est pas mou-au-runtime. Un Lisp réflexif, lui, est mou-au-runtime : vous modifiez la définition d'une fonction, l'image en cours d'exécution se reconfigure, les objets existants continuent à vivre, et personne n'a perdu d'état. C'est une propriété fondamentalement différente, et elle a un coût en vérification statique : Lisp ne peut pas vous garantir que la nouvelle définition est compatible avec l'ancienne, parce qu'il accepte par construction que le système se reforme à chaud. Le trade-off est explicite, et il a quarante ans.

C# n'a pas effacé ce trade-off. Il l'a déplacé du runtime vers le build, en disant : « je vous donne la mobilité maximale possible, à condition que vous acceptiez de payer le prix d'un build entre chaque mouvement. » C'est un compromis honnête, et il marche pour la grande majorité des cas où la vitesse de mouvement utile est de l'ordre de la minute (un build incrémental Roslyn typique), pas de l'ordre du milliseconde (le hot-reload d'un Lisp).

Le contre-exemple Diem — j'ai déjà vécu Korzybski sans le nommer, en 2009, en PHP

Et maintenant, le détour qui ferme l'argument. Le mou-au-runtime, je l'ai déjà fait fonctionner, en production, mais pas en C#. Je l'ai fait en PHP, sur le projet Diem, entre 2009 et 2011. Il faut que je raconte cette histoire, parce qu'elle n'est pas incidente à cet article — elle en est l'origine biographique.

Diem est un Content Management Framework (CMF), pas un Content Management System. La distinction est essentielle, et elle est une marque de fabrique de toute ma carrière depuis. Un CMS — WordPress, Drupal, Joomla en mode classique — vous livre un produit fini : il sait déjà faire des articles, des pages, des commentaires, des utilisateurs, et vous l'adaptez par configuration et par thèmes. Un CMF — Diem, Apostrophe, Symfony-CMF — vous livre un framework vide : vous décrivez votre domaine, et le framework vous fournit la pile méta. C'est la même différence philosophique qu'entre un appartement meublé et un terrain à bâtir avec une grue, du béton, et des plans d'architecte. Un CMS livre du contenu pré-pensé. Un CMF livre la machine qui sait fabriquer un CMS. Pour qui veut comprendre pourquoi mon CMF C# actuel s'appelle « CMF » et non « CMS » : c'est parce que le mot est revendiqué, comme continuité directe de Diem.

Diem a été créé en 2007 par Thibault Duplessis, alors développeur dans l'agence Intuiti à Nantes. Duplessis est aussi celui qui a écrit lichess (lichess.org), la plus grande plateforme d'échecs en ligne au monde — c'est dire que ce n'est pas un débutant. Diem est PHP 5, basé sur Symfony 1.4, avec Zend Framework et jQuery par-dessus. Il devient open-source en novembre 2009 à partir de la version 5. Et la propriété que je veux pointer ici, celle qui me concerne directement, c'est la suivante : Diem démarre vide, et les modules, le backoffice et le frontoffice sont autogénérés à partir de descriptions YAML. Vous écrivez un fichier YAML qui dit « voici une entité Article, elle a un titre, un contenu, un auteur, une date de publication », et Diem génère pour vous : la classe Doctrine de l'entité, les migrations SQL, les contrôleurs CRUD, les formulaires d'admin, les listings, les filtres de recherche, les permissions, les routes, les vues frontoffice. Tout ça depuis une description. C'est du Model-Driven Engineering authentique, en 2007, dans le monde PHP, à un moment où l'expression « MDE » n'était pas encore à la mode dans cet écosystème. Diem est précurseur. Il l'est tellement que la plupart des frameworks « modernes » (low-code, headless CMS, schema-first development) redécouvrent en 2020+ ce que Diem faisait dès 2007.

Fin 2010, Thibault Duplessis quitte Diem et part co-fonder Symfony-CMF sur la base de Symfony 2 et Doctrine 2 — la nouvelle vague des CMF PHP. Et c'est moi qui reprends le lead sur Diem. Je porte les versions 5.1 et 5.2, je maintiens, j'étends. Et parmi mes contributions techniques sur cette période, j'ai notamment implémenté les objets NestedTree dans Diem via Doctrine — le pattern SQL classique pour stocker des arbres efficacement (lft/rgt indices) qu'on utilise pour les hiérarchies de pages, de catégories, de menus. Soit exactement ce dont un CMF a besoin. J'ai aussi codé un sous-système d'installation dynamique de plugins à chaud, qui permettait d'installer un nouveau plugin avec ses procédures d'installation sans relancer le processus PHP. La nature naturellement réflexive de PHP rendait ça faisable : pas de phase de compilation à invalider, chargement de classe dynamique par défaut, eval() disponible si nécessaire. Diem prouve donc que le mou-au-runtime, en tant que pattern produit, fonctionne et a fonctionné en production il y a quinze ans. J'en suis le témoin direct.

Et voici pourquoi j'ouvre cet article par la phrase « Tout ça me parle beaucoup en 2009 du fait que la carte n'est pas le territoire » : parce qu'en travaillant sur Diem, je vivais Korzybski au quotidien sans le nommer. Le YAML était la carte. Le code généré, exécuté dans le navigateur de l'utilisateur, était le territoire. Et toute la classe de bugs que rencontre un développeur Diem est exactement celle dont parle Korzybski : vous pensez que le YAML est correct, et vous découvrez que le territoire — l'admin générée, le formulaire qui s'affiche, le SQL produit — ne fait pas ce que vous croyiez avoir décrit. Le YAML disait « champ de type texte », mais Doctrine a interprété le type comme varchar(255) au lieu de text, et votre article s'est tronqué silencieusement. Le YAML disait « relation un-à-plusieurs », mais la migration n'a pas été rejouée, et la base est dans un état où la relation existe en code mais pas en SQL. Le YAML a été modifié, la régénération est passée, mais le cache d'opcode PHP a gardé l'ancienne version, et l'utilisateur voit encore l'ancien formulaire pendant cinq minutes. Chaque développeur Diem devient un korzybskien par expérience. Il sait, dans sa chair, qu'il y a un écart entre la carte qu'il a écrite et le territoire qui tourne, et que cet écart est précisément ce qui fait que les bugs existent.

C'est cette expérience — vécue, pas lue — qui m'a structuré pour le travail que je fais aujourd'hui en C#. Quand j'écris en 2026 un Source Generator Roslyn qui prend des attributs [Entity] ou [Feature] et qui génère des classes Doctrine-équivalent et des constantes typées, je fais le même geste qu'en 2009 sur Diem, mais dans un écosystème vérifié. Le geste est identique : description → génération → exécution. Le compilateur a changé. Et ce qui a changé avec lui, c'est précisément la localisation du refus. Sur Diem, le refus arrivait au runtime, quand l'utilisateur voyait quelque chose qui ne correspondait pas à ce que j'avais décrit. Sur Roslyn, le refus arrive au build, quand le compilateur dit « cette classe ne compile pas parce que la constante que tu utilises n'existe plus, parce que tu as supprimé l'attribut qui la générait ». Le geste est le même. Le moment où le système dit « non » a juste été déplacé de quelques minutes plus tôt dans la chaîne. Et ces quelques minutes sont, exactement, la différence entre un bug en production et un bug rattrapé sur la machine du développeur.

Et maintenant, le pari sur C#

La question intéressante, après ce détour Diem, n'est plus « est-ce que C# peut faire ce que PHP/Lisp font nativement ? », c'est « est-ce que C# peut faire ce que PHP/Lisp font, en gardant la vérification statique ? ». Et la réponse est : oui, à un coût d'orchestration. Embarquer une image dotnet SDK dans un conteneur Docker, instancier Roslyn comme un service à l'intérieur du runtime de l'application, builder à la volée, déployer à chaud. Le compilateur Roslyn est un compilateur-bibliothèque ; il est déjà prêt à être instancié à n'importe quel moment du cycle de vie de l'application, et à recompiler un fragment de code à la demande. Le pari technique est résolu — Roslyn est mûr pour ça depuis plusieurs années. Le pari opérationnel (la mémoire d'un dotnet SDK embarqué, la latence d'un build à chaud, l'idempotence du déploiement, le redémarrage propre des dépendances) reste à monter en démonstration, et c'est exactement ce que je veux faire sur le CMF dans une prochaine itération.

Diagram
Deux boucles du même geste — Roslyn build-time (mou au build) et l'hypothèse Diem-style runtime (dotnet SDK embarqué recompilant à chaud) ; la différence n'est plus le geste mais le moment du refus.

La leçon des deux moitiés de cette section, et c'est par elle que je veux refermer l'argument du compilateur : C# n'est pas plus mou que PHP en valeur absolue. Il est mou différemment. PHP achète la mobilité au prix de la vérification statique ; C# garde la vérification et achète la mobilité au prix d'orchestration. C'est un trade-off, pas une supériorité, et c'est précisément pour cette raison que l'axe est un plan et pas une ligne. Le coin haut-droite du quadrant — vérifié et mou — n'est pas un endroit où on arrive par accident. C'est un endroit où on arrive en payant les deux dimensions à la fois, et la maturité de C# Roslyn aujourd'hui est exactement la maturité de cet investissement.


Ce qui suit, et la phrase qui ferme

Cet article était un onramp. Il pose la formule récursive et il propose une lecture cartographique de MOF, mais il ne développe pas le pont vers la métacratie — ce pont est dans le diptyque. Le lecteur qui veut entrer dans la version politique-philosophique longue, écrite en français pour les lecteurs du dossier métacratie, devrait lire Appareil et compilateur. Le lecteur qui veut la version technique longue, écrite en anglais pour les lecteurs MDE de ce blog, devrait lire MDE and Métacratie. Le lecteur qui veut voir le pattern Source Generator décrit ici en action sur un système réel devrait lire feature-tracking-ts, et la série complète qui le contient est dans le sous-dossier CMF. Pour le contexte biographique, et notamment la rencontre avec John F. Sowa à dix-huit ans qui a fixé tout ce que j'ai fait depuis, Age 18 — discovering the model-driven academic world est probablement le bon point d'entrée.

Et voici la phrase par laquelle je voudrais fermer cet article. Elle reprend le titre, et elle est aussi honnête que je peux la faire :

La carte n'est pas le territoire. La légende n'est pas la carte. Le compilateur n'est pas la légende. Mais quand le compilateur est assez mou pour se relire lui-même, on tient les quatre à la fois — et c'est tout ce qu'on peut honnêtement promettre.

Korzybski a eu raison en 1933, et il l'a toujours en 2026. Tarski a eu raison la même année, et la régression méta qu'il a démontrée est toujours là. Sowa a eu raison en 1984 quand il a écrit que les axiomes ne pourraient pas être posés tant que beaucoup d'autres choses ne seraient pas formalisées. Hofstadter a eu raison sur les boucles étranges. Smith a eu raison sur les compilateurs réflexifs. Et moi j'ai vécu la même chose en 2009 sur Diem, en PHP, sans en avoir le vocabulaire. La seule chose que je fais aujourd'hui sur le CMF C# est de continuer ce geste — description → génération → exécution — dans un écosystème qui m'autorise à le faire en gardant la vérification statique. Le geste est identique. Le compilateur a changé. Et c'est précisément parce qu'il est devenu assez mou que le geste devient industriellement tenable.


Pour aller plus loin

Une bibliographie courte, huit titres, dans l'ordre où ils sont entrés dans cet article. Ils sont tous lisibles, tous utiles, et ils forment ensemble la lignée que j'assume.

  • Alfred Korzybski, Science and Sanity, 1933 — la formule originelle, sortie de son contexte usuel. Long, daté, idiosyncrasique, mais on n'a pas trouvé mieux pour dire la non-identité structurelle entre la carte et le territoire.
  • Alfred Tarski, The Concept of Truth in Formalized Languages, 1933 — la même intuition côté logique formelle, avec la démonstration en plus. Le paragraphe court qui démontre que la vérité est indéfinissable en interne est l'un des moments les plus économiques de la logique du XXe siècle.
  • Gregory Bateson, Steps to an Ecology of Mind, 1972 — Bateson reprend Korzybski et observe que la carte qui se redessine est elle-même un événement dans le territoire. Le détail change tout, et la plupart des korzybskiens l'oublient.
  • Douglas Hofstadter, Gödel, Escher, Bach: an Eternal Golden Braid, 1979 — les boucles étranges, le vocabulaire le plus pédagogique qu'on ait sur l'auto-référence productive. Long mais joyeux, et c'est par lui que beaucoup d'informaticiens entrent dans la question méta.
  • Brian Cantwell Smith, Reflection and Semantics in a Procedural Language, MIT thèse 1982 puis articles 1984 — le texte canonique CS du compilateur réflexif. Sans Smith dans la lignée, on parle de « compilateur mou » en croyant inventer ce qui existe depuis quarante ans.
  • John F. Sowa, Conceptual Structures: Information Processing in Mind and Machine, 1984 — l'ancêtre direct du Meta-Object Facility de l'OMG, et le texte qui a fixé ma façon de penser depuis mes dix-huit ans. La Top-Level Ontology en ligne sur jfsowa.com est un complément essentiel.
  • Michel Foucault, L'Archéologie du savoir, 1969 — pour le théorème d'expressivité (« on ne peut spécifier que ce qu'on peut vérifier ») dans son autre vocabulaire, celui de l'épistémè et des conditions de possibilité d'un énoncé. Le pont avec MOF est rigoureux, pas métaphorique.
  • Bruno Latour, Nous n'avons jamais été modernes, 1991 — pour le parlement des choses, que MDE opérationnalise plutôt que de le décrire. Latour a passé sa vie à dire que les non-humains parlent ; les Source Generators sont précisément des non-humains qui disent non dans le compilé.

Si je devais en garder un seul pour quelqu'un qui n'aurait que cinq jours de lecture, ce serait probablement Hofstadter. Si je devais en garder un seul pour quelqu'un qui code déjà et qui veut comprendre la pile méta, ce serait Sowa. Et si je devais en garder un seul pour quelqu'un qui a déjà tout lu mais qui n'a jamais relié la formule de Korzybski au geste de Smith, ce serait cet article-ci.

⬇ Download