Retour d’expérience
Du monolithe au contrat
Comment la spécification OpenAPI est devenue le contrat structurant de mon workflow AI.
Un an d’évolution progressive — et un script de 30 lignes qui a cristallisé tout le reste.
01 — L’application qui faisait tout
Mi-2025. Je lance Ezkey, un projet MFA open source, en mode solo dev. Greenfield complet. Une seule application Spring Boot qui fait tout — admin, authentification, intégration. Pas de frontière parce qu’il n’y en a qu’une : un seul module, une seule déployable, tout est là.
Ce n’est pas une erreur de conception. En phase d’exploration, on ne sait pas encore ce que le système doit devenir. Mieux vaut garder tout ensemble et laisser émerger la structure naturelle avant de découper arbitrairement.
Ce que je savais dès le départ, c’est que je pense en API. Quand j’imagine une fonctionnalité, mon premier réflexe est de définir l’endpoint, le contrat JSON, les codes HTTP. L’interface utilisateur, l’expérience mobile — viendront, évidemment. Mais mon point de départ est toujours le même : à quoi ressemble le contrat backend ?
02 — La première fracture : suivre la logique de confiance
La première raison de fractionner n’est pas architecturale — elle est sécuritaire. L’admin ne sera jamais exposé publiquement. Il opère derrière un VPN, avec des contrôles d’accès complètement différents. C’est une frontière évidente.
L’Auth API, c’est une autre logique : un backend-for-frontend destiné à être consommé par l’application mobile. Sa surface d’exposition, son modèle d’authentification, ses contraintes — tout diffère de l’admin. L’Integration API, elle, expose un contrat vers des intégrateurs tiers : une autre audience, un autre cycle de vie.
Deux backends deviennent trois, puis quatre avec le Crypto API en mode développement. Et sur chaque backend, les consommateurs se multiplient : admin UI, application mobile, demo device, CLI Python, SDK partagé. Plus tard, des projets expérimentaux — ezkey-dart pour la cryptographie multiplateforme, ezkey-pam pour l’authentification système. Pas pour demain, mais déjà là, et ils auront eux aussi besoin d’une spécification pour démarrer.
La séparation ne suit pas un diagramme d’architecture idéal. Elle suit le modèle de confiance du système. C’est une distinction qui compte.
03 — Javadoc, OpenAPI : la discipline non-négociable
Dès la première ligne de code, deux non-négociables : Javadoc impeccable, partout, sans exception. Contrôleurs REST entièrement documentés pour OpenAPI.
Cette discipline n’était pas un luxe réservé à un stade « quand le projet sera stable ». C’était une contrainte fondatrice, acceptée dès le départ. SpringDoc génère la spécification OpenAPI dynamiquement à partir des annotations — un contrat vivant, toujours en phase avec le code, accessible à une URL stable sur chaque backend en cours d’exécution.
Quand les backends se multiplient, cette discipline devient de la valeur concrète. Chaque backend devient auto-descriptif. Un agent de codage, un nouveau collaborateur, un générateur de code — tous peuvent comprendre la surface d’un backend sans lire une seule ligne d’implémentation.
Mais un contrat n’a de valeur que s’il est accessible, frais, et prêt à être consommé. L’avoir généré dynamiquement sur un backend actif, c’est un début. Le distribuer aux consommateurs — c’est une autre étape.
04 — Le script update-specs : extraire, formater, dispatcher
La réalisation vient naturellement : si chaque backend expose son OpenAPI en direct, pourquoi ne pas l’extraire et le dispatcher automatiquement ?
C’est le rôle du script update-specs. Sa responsabilité : se connecter à chaque backend actif, télécharger la spécification exposée, la valider en tant que JSON, la formater, puis la copier dans chaque sous-projet consommateur. Le mapping est explicite dans le script :
- admin-api →
ezkey-admin-ui,ezkey-demo-app-acme,ezkey-sdk - auth-api →
ezkey-mobile,ezkey-demo-device,ezkey-sdk,ezkey-cli
La spécification est versionnée dans le sous-dossier du consommateur. C’est un artefact du dépôt, commité, tracé, visible dans chaque diff. Quand un consommateur doit regénérer son code client — Orval pour l’admin UI et pour l’application mobile, un plugin Maven pour le demo device — il travaille à partir de ce fichier local, disponible et versionné.
Un détail utile en passant : Spring expose son OpenAPI en JSON monoligne par défaut. Le script applique jq . pour l’indenter proprement. Ce n’est pas révolutionnaire, mais un grep dans un fichier multi-lignes permet à un agent de codage de cibler un endpoint en quelques lignes — plutôt que de recevoir la totalité du fichier.
05 — Slow AI : je suis le gatekeeper
On pourrait se demander : pourquoi ne pas laisser l’agent invoquer le script lui-même ? Techniquement, c’est faisable. Un agent peut exécuter des commandes shell. Rien ne l’en empêche.
Je ne le fais pas. Délibérément. J’appelle cette approche le slow AI.
Je ne cherche pas l’autonomie maximale de l’agent. Je cherche une interaction humain-AI de qualité. Le moment du dispatch est un moment de gating : c’est l’instant où moi, l’humain, je déclare que l’API est assez solide pour devenir le contrat d’un consommateur.
Avant d’invoquer update-specs, j’ai complété l’implémentation, validé avec les tests unitaires, passé les tests fonctionnels, parcouru la collection Postman contre le backend actif. J’ai lu la spécification générée. J’ai vérifié que le contrat reflète bien l’intention de départ.
Ce moment de lecture — « est-ce que ça ressemble à ce que j’avais en tête ? » — n’est pas automatisé. C’est un jugement humain, et je tiens à le garder tel quel.
06 — Deux sessions, deux contextes
La conséquence immédiate de ce workflow : le travail se découpe naturellement en deux types de sessions.
La session backend commence avec un plan de fonctionnalité, couvre la conception, l’implémentation, les tests unitaires, les tests fonctionnels, la validation Postman. Quand tout est stable et le jugement humain positif, j’invoque update-specs. La spec est extraite, formatée, dispersée dans les sous-dossiers concernés. La session se clôt là. Le contrat backend est « gelé » — versionné dans le dépôt, placé où il doit l’être.
La session consommateur démarre fraîche. Pour l’admin UI : nouvelle session, lecture du plan de la session précédente, puis lecture du openapi-spec.json déjà présent dans ezkey-admin-ui. L’agent travaille entièrement dans ce sous-projet. Le backend n’est pas dans son contexte. Le contrat est défini par la spec — sans négociation, sans approximation.
Même logique pour l’application mobile, le CLI Python, ezkey-dart. Chaque sous-projet a son propre openapi-spec.json local, prêt à l’emploi.
Ce qui rend ce découpage efficace : le plan de la session précédente joue le rôle de contexte condensé, peu coûteux en tokens mais riche en substance. Il contient les décisions, les contraintes, les choix de conception — déjà synthétisés, sans bruit. L’agent arrive dans la nouvelle session avec le tableau complet sans avoir à traverser toute la codebase.
07 — L’API d’abord, vraiment
Je ne commence jamais une fonctionnalité par une maquette. Je commence par une vision qui prend forme à travers des API et un schéma de base de données. L’interface utilisateur, l’ergonomie mobile — elles arrivent, et elles comptent. Mais mon drive-out du départ est toujours le contrat backend. C’est ce que « developer-first, backend-first » signifie en pratique chez Ezkey — pas juste un positionnement marketing, un réflexe ancré.
Le workflow de dispatch de spécification matérialise cette philosophie de façon structurelle. La spec est l’acte fondateur de chaque tranche verticale. Tant qu’elle n’est pas validée et dispersée, rien d’autre ne commence : pas de travail admin UI, pas de développement mobile, pas de génération SDK.
OpenAPI, spec-first, bounded contexts — recommandés dans les livres depuis des années. Rarement suivis rigoureusement en pratique, souvent différés au « quand le projet sera plus stable ». Ici, ces pratiques cessent d’être optionnelles par construction : il n’y a pas de session consommateur sans spec validée. Le contrat précède tout.
Les projets expérimentaux confirment la même logique : ezkey-dart partira d’une spécification générée pour construire son SDK. ezkey-pam idem. Même philosophie, même point de départ. Ce n’est plus une bonne intention. C’est la contrainte qui structure tout le reste.
08 — Ce que ça m’a appris pour de vrai
« Context engineering » — belle expression. On la voit souvent présentée avec des plugins de compression, des pipelines de synthèse automatique, des frameworks entiers dédiés à l’optimisation du contexte. Tout ça peut avoir du sens dans certains contextes d’équipe.
Ma traduction pragmatique, pour un solo dev qui valorise la simplicité : le contexte est un choix architectural, pas un produit de configuration. Sessions fraîches, sujets bien bornés, contrats versionnés — voilà les leviers qui ont réellement fait la différence.
Le slow AI n’est pas une limitation. C’est un positionnement qui préserve la qualité des décisions. L’agent est puissant — mais il est le plus puissant quand il sait exactement où il est et où s’arrêter. Et c’est moi qui décide où se trouve cette frontière.
Le meilleur contexte n’est pas le plus complet. C’est celui qui sait s’arrêter.
English: From Monolith to Contract
Voir aussi : Du code à l’intention — un an de workflow avec l’IA
Voir aussi : Le manifeste du codage assisté — bounded contexts et agent mode
Voir aussi : IA générative et No-Code — la constante de l’ingénierie de contexte