Chris Richardson précise ce qui distingue réellement un pattern logiciel d’une simple technique, pratique, règle ou conception : un problème récurrent dans un contexte donné, des forces à équilibrer, une solution applicable, ses conséquences et ses relations avec d’autres patterns. Il explique aussi qu’un « pattern language » suppose des relations riches entre patterns, et montre pourquoi cette rigueur améliore les décisions d’architecture, limite le battage technologique et fournit un vocabulaire commun aux équipes.
L’IA rend désormais le code beaucoup moins coûteux à écrire et à réécrire, mais cela ne le rend pas moins important. Au contraire, les agents de codage s’appuient fortement sur les conventions et abstractions déjà présentes dans un projet : un code cohérent produit de meilleurs résultats, tandis que les défauts se propagent. Le rôle du développeur se déplace donc vers la recherche des bonnes abstractions, de la meilleure architecture et d’un modèle de domaine solide, que l’IA peut ensuite appliquer rapidement partout.
L’auteur soutient que les mauvais résultats d’un agent de code ne viennent pas toujours du modèle : une architecture ambiguë lui fournit aussi de mauvais repères. À partir d’un exemple Symfony, il montre qu’une séparation claire entre domaine, application, infrastructure et interface, complétée par du DDD, des tests et des règles de dépendances vérifiables, réduit les choix implicites. L’objectif n’est pas de sur-architecturer, mais de rendre les responsabilités et contraintes suffisamment explicites pour les humains comme pour l’IA.
Le problème majeur du code généré par IA n’est pas nécessairement sa qualité moyenne, mais la perte progressive de compréhension du système par les équipes. Quand spécifications, code, tests et décisions passent presque entièrement par des LLM, l’architecture, l’intention et les choix techniques deviennent opaques. L’auteur insiste sur l’importance des fondamentaux, du jugement humain et surtout de la maintenabilité : générer vite est facile, mais maintenir un système que personne ne comprend devient le véritable risque.
Une pipeline de quatre subagents Claude Code accompagne le développement en privilégiant l’apprentissage : cadrage produit, design, génération du squelette et revue. L’IA prépare les interfaces et le code répétitif, tandis que la développeuse implémente la logique métier à partir de TODOs explicites. Les décisions sont documentées et la revue devient progressivement plus fine, par fichier. Cette approche favorise la compréhension du code, mais reste coûteuse en temps et en tokens ; son rapport coût/bénéfice et les garde-fous pédagogiques restent à évaluer.
Face aux défis de la vélocité des décisions dans les grands projets logiciels, le modèle mental des "portes à sens unique" (one-way doors) contre les "portes à double sens" (two-way doors) permet d'éviter l'accumulation de dette technique. Une décision est une porte à sens unique si elle est rare, conséquente et difficile à inverser, comme le choix d'une base de données. Inversement, une porte à double sens concerne des décisions à faible risque et facilement réversibles, tel qu'un nom de variable locale.
Pour maintenir une haute vélocité décisionnelle en toute sécurité, il est crucial d'intégrer la réversibilité dans le code grâce à des patterns architecturaux spécifiques. L'objectif est de concevoir des "trappes de sortie" intégrées plutôt que de se retrouver enfermé dans un coin du code. Cette approche délibérée permet d'éviter la paralysie d'analyse et de garantir que l'équipe puisse progresser rapidement et de manière fiable.
Parmi les patterns favorisant les portes à double sens, on trouve les feature flags pour découpler le déploiement de la mise en production, la limitation de l'exposition des API pour permettre des itérations sur les interfaces sans impacter les clients en aval, le découplage des formats de données pour isoler les modifications des structures de stockage permanentes, et les dark launches pour valider de nouvelles logiques en parallèle avec un impact nul sur les utilisateurs.
L'article aborde la problématique du "code sloppé" généré par l'intelligence artificielle, en soulignant que cette notion englobe divers types d'échecs sans cause unique ni solution universelle. Il identifie des schémas récurrents tels que des validations redondantes, la création de fonctions quasi-identiques ou des vérifications excessives pour des entrées impossibles, qui nuisent à la qualité et à la maintenabilité du code.
Une cause majeure de cette accumulation de code de mauvaise qualité réside dans le manque de contexte pour les agents d'IA. Ces derniers, ne disposant pas des informations complètes sur les validations précédentes ou les fonctions existantes, ont tendance à synthétiser des solutions locales et redondantes, exacerbant le "problème de localité" où le code qui devrait évoluer ensemble est difficile à trouver et à modifier.
Pour remédier à ce problème, l'article propose d'identifier précisément les comportements ou problèmes récurrents, de préférer des descriptions de problèmes spécifiques plutôt que des termes vagues comme "code sloppy". L'utilisation d'outils comme des règles de linting ciblées, inspirées par des travaux comme ceux de Dillon Mulroy, peut aider à identifier ces schémas de code problématiques et orienter les efforts de nettoyage, qu'il s'agisse de corrections locales ou de refontes architecturales.
Le projet Agojie vise à automatiser le jeu de société Dice Throne. Le développement a débuté par des décisions fondamentales avant même la première ligne de code, abordant la structure du modèle du jeu, la mécanique de défense des héros, la découpe des tâches de développement et le choix de la pile technologique. L'objectif initial est de modéliser le cœur du jeu en un contre un, en se concentrant sur la gestion des dés, avant d'intégrer les cartes et les effets de statut.
Trois idées d'architecture ont été envisagées dès le départ : la séparation entre la définition d'un héros et l'état d'une partie, la modélisation du jeu comme un "Aggregate" pour garantir l'intégrité des règles métier, et l'utilisation d'"Domain Events" pour enregistrer les faits importants du domaine. Cependant, ces idées n'ont pas été appliquées immédiatement, privilégiant une approche où les décisions sont justifiées par les règles du jeu et non par l'intuition.
Ce projet est structuré en quatre étapes principales : la mise en place du cœur du jeu sans cartes, le développement d'une interface utilisateur basique avec sauvegarde des parties, l'intégration de la couche de cartes pour influencer le jeu, et enfin la gestion des effets de statut qui affectent les héros. La première phase consiste en la rédaction d'une spécification détaillée des règles du jeu de Dice Throne, incluant des exemples concrets pour guider le développement du code.
Résumé pour Shaarli:
Le projet Agojie se lance dans l'automatisation de Dice Throne, un jeu de dés et de combat stratégique. Les premières étapes se concentrent sur les décisions architecturales et la modélisation des règles fondamentales, sans écrire de code. L'objectif est de développer le cœur du jeu, puis d'ajouter progressivement des fonctionnalités comme les cartes et les effets de statut, en s'appuyant sur des principes de conception logiciel rigoureux.
L'article soutient que la tendance actuelle à diviser les systèmes d'IA en de multiples agents spécialisés, à l'instar des microservices, peut être prématurée et coûteuse. Il souligne que de nombreux agents médiocres ne surpassent pas un seul agent performant, et que la complexité croissante d'un agent unique conduit à une dégradation progressive. Les problèmes majeurs incluent la difficulté de sélectionner le bon outil parmi une liste étendue, la contradiction des instructions dans un unique prompt, et la difficulté de modifier le système sans affecter l'ensemble.
Le moment idéal pour opérer une scission en agents multiples est lorsque ces difficultés deviennent mesurables et nuisibles, et non sur la base d'une esthétique ou d'une tendance. La solution proposée est le schéma "supervisor pattern", où un agent superviseur centralise la classification et le routage des requêtes vers des agents spécialisés. Chaque agent spécialiste est confiné à un domaine unique, disposant de son propre prompt clair, d'un ensemble d'outils limité et distinct, et gérant son propre état de conversation.
Ce modèle permet de retrouver la précision dans le choix des outils et d'éviter les conflits d'instructions, offrant ainsi une meilleure maintenabilité et évolutivité. L'idée principale est de ne pas commencer avec une architecture multi-agents avant que la nécessité ne soit clairement établie par des problèmes concrets liés à la complexité d'un agent unique, puis d'adopter une approche structurée avec un superviseur et des spécialistes.
Cet article met en évidence la convergence architecturale entre des frameworks web populaires tels que Spring Boot (Java), Symfony et Laravel (PHP), ainsi que NestJS (Node.js). Malgré leurs langages distincts, ces outils partagent une philosophie commune visant à faciliter la maintenance des applications serveur sur le long terme. Ils s'appuient sur des concepts clés tels que l'injection de dépendances, le routage déclaratif, les patrons de conception de type "repository" et une gestion de configuration adaptable aux différents environnements.
L'auteur illustre cette similarité en comparant des exemples de contrôleurs dans chaque framework. Il observe que l'injection par constructeur, l'isolement des services métiers et la déclaration des routes directement dans le code de la méthode sont des pratiques récurrentes. Cette approche, notamment celle de Spring Boot avec ses "starters" qui simplifient la configuration initiale, rappelle des concepts déjà présents dans l'écosystème PHP, suggérant une influence mutuelle et une standardisation des solutions architecturales pour les applications web modernes.
Dans une architecture CQRS, la synchronisation entre la base de données de lecture et celle d'écriture est un défi majeur pour éviter la présentation d'informations obsolètes. Une stratégie de synchronisation en deux étapes est nécessaire pour équilibrer la performance de la base de données de lecture et la cohérence de la base de données d'écriture.
Face à des volumes importants de données et à des besoins de recherche performante, comme dans le cas d'une recherche de disponibilités hôtelières, une base de données unique avec de nombreux index peut devenir inefficace. L'adoption d'une architecture CQRS, séparant la gestion des commandes et des requêtes, permet d'optimiser ces aspects en utilisant une base de données dédiée à la lecture, telle que MongoDB, enrichie d'index appropriés.
Pour maintenir la cohérence entre la base de données principale (PostgreSQL) et la base de données de recherche (MongoDB), une approche asynchrone est privilégiée. Les modifications apportées à PostgreSQL sont publiées sur un broker de messages, et MongoDB les traite lorsque sa charge de lecture le permet, garantissant ainsi que les opérations d'écriture restent rapides. Pour pallier les risques liés aux opérations asynchrones, des reconstructions planifiées de la base de données de lecture sont effectuées, généralement pendant les périodes de faible activité, afin d'assurer la mise à jour des données.
-
La distinction entre les "sub-agents" et le "Master Control Panel" (MCP) est cruciale pour l'architecture des systèmes d'IA. Historiquement, la confusion a conduit à mélanger la fonction de raisonnement ("qui pense") et celle d'exécution ("qui agit"), entraînant des problèmes de gouvernance, de traçabilité et de permissions.
Un sub-agent est conçu pour un rôle cognitif spécialisé, recevant un prompt, un périmètre et des règles pour produire une sortie structurée et utile. Il se concentre sur la pensée critique et la formulation. À l'inverse, un serveur MCP est une infrastructure dont le rôle est d'exécuter des actions de manière contrôlée, en exposant des capacités et des données avec des contrats clairs et une traçabilité.
Cette séparation est particulièrement pertinente lors de la gestion d'incidents. Dans un modèle bien conçu, un orchestrateur délègue des tâches à des sub-agents spécialisés (diagnostic, mitigation), qui formulent des hypothèses et proposent des actions. L'exécution réelle de ces actions est ensuite gérée par les serveurs MCP appropriés, laissant une trace écrite et permettant de comprendre précisément qui a décidé quoi et pourquoi.
L’article remet en question l’idée reçue selon laquelle l’utilisation de microservices serait un signe de compétence avancée en ingénierie logicielle. L’auteur, Devrim Ozcay, souligne que la complexité n’est pas une preuve de qualité, mais plutôt un choix facile à faire, souvent au détriment de la maintenabilité et de la fiabilité. Il explique que les microservices, bien qu’utiles dans certains cas, génèrent des coûts opérationnels et techniques importants, comme des problèmes de réseau, de duplication de requêtes ou de gestion de dépendances, transformant des opérations simples en défis de systèmes distribués.
L’auteur critique l’"Arms Race" (course à l’armement) architecturale, où les équipes adoptent des microservices par mimétisme (ex. : "Netflix le fait") sans évaluer si cette solution répond à un besoin réel. Il insiste sur la nécessité de se demander quel problème les microservices résoudront mieux qu’une architecture monolithique bien conçue, laquelle peut offrir des avantages comme une cohérence transactionnelle, un déploiement simplifié et une complexité opérationnelle réduite. Un monolithe modulaire, avec des frontières de domaine claires, peut ainsi servir de base solide avant toute éventuelle décomposition.
Enfin, l’article rappelle que la maîtrise des systèmes distribués, et non leur simple utilisation, distingue les ingénieurs expérimentés. L’auteur met en garde contre la tentation de créer de la complexité artificielle, soulignant que la véritable compétence réside dans la capacité à évaluer quand une solution simple (comme un monolithe) est préférable, et à anticiper les problèmes inhérents aux architectures distribuées, comme les pannes de réseau, les données obsolètes ou les comportements inattendus des consommateurs.
L’article relate une expérience où des agents d’IA chargés de refondre une architecture logicielle ont involontairement effacé des mois de savoir-faire intégré dans le code, sans enfreindre aucune règle formelle. Les régressions apparues ensuite ont révélé que ces outils, bien que produisant un code propre et conforme aux patterns, avaient ignoré des ajustements empiriques cruciaux, non documentés et non testés, car sans valeur apparente pour un algorithme. Leur comportement par défaut, sans garde-fous explicites, a ainsi détruit une connaissance implicite accumulée sur des années.
Pour éviter de reproduire cette situation, l’équipe a restructuré son approche en s’appuyant sur des contraintes techniques strictes, notamment via ESLint, seul outil capable de bloquer efficacement les agents. L’architecture du nouveau dépôt a été repensée non pour son élégance conceptuelle, mais pour imposer des règles d’import et de modularité que les IA ne peuvent contourner, transformant ainsi des recommandations en obligations incontournables.
L’expérience illustre les limites des agents de codage en production : leur efficacité dépend entièrement des contraintes qu’on leur impose, et leur manque de compréhension contextuelle les rend incapables d’intégrer des savoirs informels. La solution retenue repose donc sur une délégation encadrée, où la qualité du code est garantie par des mécanismes automatiques plutôt que par une revue humaine systématique.
L’article critique l’approche traditionnelle des systèmes multi-agents en IA, qui se concentre souvent sur l’orchestration des agents plutôt que sur la modélisation du domaine métier. L’auteur souligne que les règles et contraintes (comme les seuils d’approbation) ne devraient pas être intégrées dans les prompts des agents, mais plutôt encapsulées dans un modèle de domaine bien défini. Cela permet aux agents d’agir dans un environnement structuré, recevant des retours immédiats en cas d’erreur, et favorise une autonomie plus robuste.
Plutôt que de concevoir des workflows complexes entre agents, l’article plaide pour une architecture où le domaine est modélisé en premier : processus, état, invariants et règles métier. Les agents interagissent ensuite avec ce modèle partagé, sans avoir à gérer eux-mêmes les contraintes. Cette séparation réduit les risques liés aux erreurs de compréhension des règles et évite les systèmes soit trop risqués, soit trop restrictifs.
L’auteur présente Mozaik, un framework qui adopte cette approche en introduisant un état partagé et concurrentiel pour les agents, tandis que le modèle de domaine garantit le respect des règles. L’objectif est de construire des systèmes plus fiables et évolutifs, en s’appuyant sur une modélisation précise du monde réel plutôt que sur des workflows artificiels.
L’auteur, Derek Mwale, explique qu’il conçoit désormais des logiciels en anticipant leur maintenance sur dix ans, ce qui influence ses choix techniques : structure des dossiers, nommage des variables, documentation ou encore conception des API. Cette approche vise à faciliter la compréhension du code par son "moi futur", souvent moins familier avec le contexte initial, et à éviter les solutions trop complexes ou abstraites qui deviennent rapidement incompréhensibles.
Il compare le logiciel à un jardin plutôt qu’à un bâtiment, soulignant que sans entretien, il se dégrade avec le temps (dépendances obsolètes, changements de frameworks, etc.). Pour lui, la lisibilité n’est pas un simple bonus mais une fonctionnalité essentielle, car un code trop astucieux ou obscur devient un fardeau pour les développeurs futurs, voire pour lui-même quelques mois plus tard.
Enfin, il met en garde contre le "code trop malin", qui peut sembler élégant à court terme mais se transforme en dette technique. Il privilégie des solutions simples et claires, car elles résistent mieux à l’épreuve du temps, contrairement aux abstractions génériques ou aux fonctionnalités superflues.
L’auteur partage sa réflexion sur la conception d’un outil logiciel personnel simple, un logiciel de bureau pour usage exclusif, rejetant les solutions SaaS ou cloud. Il opte pour une application macOS développée en Flutter, avec une base de données SQLite locale, évitant ainsi toute dépendance à internet ou infrastructure serveur. La simplicité prime, avec une gestion automatique des données pour éviter toute maintenance utilisateur.
Pour étendre l’application à mobile (Android et iOS) sans dupliquer le code, il sépare la logique métier des données utilisateur, comparant cela à partager une recette de gâteau plutôt que les ingrédients. Flutter permet de compiler une seule base de code Dart en applications natives pour chaque plateforme, sans recourir à une architecture client-serveur coûteuse ou complexe.
L’article explique comment appliquer correctement le pattern MVC en Symfony pour séparer la logique métier des contrôleurs et des vues, afin d’améliorer la maintenabilité du code. Il souligne que le Model ne se résume pas à une seule classe, mais se répartit entre entités, repositories et services, chacun ayant un rôle précis : les entités définissent la structure des données, les repositories gèrent les requêtes à la base de données, et les services encapsulent la logique métier.
L’auteur illustre cette séparation avec un exemple concret de contrôleur Symfony qui délègue la récupération d’un article à un repository et affiche les données via une vue Twig. Il met en garde contre l’illusion d’un MVC "propre" en rappelant que la logique métier ne doit pas être dispersée dans les contrôleurs ou les templates, mais centralisée dans des services dédiés.
Enfin, le billet insiste sur l’importance de cette discipline pour éviter un code désorganisé, tout en reconnaissant que la séparation idéale est souvent compromise dans la pratique. Il conclut que le MVC est davantage un vocabulaire commun qu’un plan rigide, et que sa mise en œuvre repose sur une réflexion constante sur l’architecture.
L’article explore l’application des principes du Domain-Driven Design (DDD) pour structurer une architecture front-end plus claire et alignée sur les besoins métiers. L’auteur, Othniel Visky, partage son expérience sur un projet de refonte d’une application de calcul de charge explosive, où le DDD a permis de créer une base de code plus compréhensible et évolutive. Le DDD résout notamment le problème de l’organisation chaotique des composants front-end, souvent classés de manière arbitraire (comme dans Atomic Design), en les regroupant par domaine métier, reflétant ainsi leur fonction réelle.
Le cœur du DDD repose sur deux concepts clés : le modèle (représentation des règles métiers) et le langage ubiquitaire (vocabulaire commun entre développeurs et experts métiers). Ce dernier évite les malentendus en standardisant la terminologie, facilitant la collaboration. Par exemple, dans le projet cité, le DDD a permis de structurer le code autour des concepts spécifiques au calcul de charge explosive, rendant les discussions avec les experts métiers plus fluides et les implémentations plus précises.
Enfin, l’auteur souligne que le DDD ne se limite pas à l’architecture technique : il transforme la façon dont les équipes conçoivent et maintiennent un produit. En adoptant cette approche, les développeurs front-end gagnent en clarté sur les attentes métiers, tandis que les experts non techniques voient leurs besoins mieux traduits en solutions logicielles. Une méthode particulièrement utile pour les applications complexes où la compréhension du domaine est cruciale.
L'article propose une approche simplifiée du CQRS (Command Query Responsibility Segregation) en séparant clairement les opérations d'écriture (modification de données) et de lecture (récupération de données) au sein d'un service Node.js, sans recourir à des architectures complexes comme l'event sourcing ou des bases de données distinctes. L'idée centrale est de scinder un service monolithique en deux parties distinctes : une dédiée aux commandes (écritures) et une autre aux requêtes (lectures), afin d'éviter les conflits de responsabilités et d'améliorer la maintenabilité.
L'auteur illustre ce concept avec un exemple concret, comme un OrderService qui mélange des méthodes de gestion des commandes (validation, règles métier) et des méthodes de récupération de données (requêtes, transformations pour l'interface utilisateur). Cette séparation permet de faire évoluer indépendamment les deux parties en fonction des besoins changeants de l'application, réduisant ainsi la complexité et les risques d'erreurs. L'approche reste légère et applicable rapidement dans un projet existant.