Communitygithub.com

Conception de modules profonds

Vocabulaire partagé pour concevoir des modules profonds. À utiliser lorsque l'utilisateur souhaite concevoir ou améliorer l'interface d'un module, trouver des opportunités d'approfondissement, décider où placer une jointure, rendre le code plus testable ou navigable par IA, ou lorsqu'une autre compétence a besoin de ce vocabulaire.

Qu'est-ce que Conception de modules profonds ?

Cette compétence fournit un vocabulaire et des principes précis pour concevoir des **modules profonds** en génie logiciel : des modules qui exposent une petite interface tout en encapsulant une implémentation complexe. Elle s'inspire des concepts de *Deep Modules* de John Ousterhout mais les affine en se concentrant sur l'effet de levier (*leverage*) pour les appelants et la localité (*locality*) pour les mainteneurs. La compétence définit des termes clés comme **Module**, **Interface**, **Implémentation**, **Profondeur**, **Jointure** et **Adaptateur**, et explique comment ces concepts s'articulent pour améliorer la testabilité, la navigabilité par IA et la maintenabilité du code. Elle propose également des principes de conception (comme le test de suppression ou la règle des deux adaptateurs) et des conseils pratiques pour concevoir des interfaces testables. La compétence peut être utilisée avec n'importe quel langage de programmation et sur n'importe quelle plateforme ; elle est conçue pour guider les développeurs et les agents IA lors de la conception ou du refactoring de code, et peut être combinée avec d'autres compétences (comme DEEPENING.md ou DESIGN-IT-TWICE.md) pour approfondir encore davantage.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Castle-Solutions/landing/tree/HEAD/.agents/skills/codebase-design

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Conception de codebase

Concevez des modules profonds : beaucoup de comportement derrière une petite interface, placée à une jointure propre, testable via cette interface. Utilisez ce langage et ces principes partout où du code est conçu ou restructuré. L'objectif est l'effet de levier pour les appelants, la localité pour les mainteneurs et la testabilité pour tout le monde.

Glossaire

Utilisez ces termes exactement : ne substituez pas « composant », « service », « API » ou « frontière ». Un langage cohérent est tout l'intérêt.

Module : tout ce qui possède une interface et une implémentation. Délibérément indépendant de l'échelle : une fonction, une classe, un package ou une tranche couvrant plusieurs niveaux. Évitez : unité, composant, service.

Interface : tout ce qu'un appelant doit connaître pour utiliser correctement le module : la signature de type, mais aussi les invariants, les contraintes d'ordre, les modes d'erreur, la configuration requise et les caractéristiques de performance. Évitez : API, signature (trop étroits, ils ne désignent que la surface au niveau des types).

Implémentation : ce qui se trouve à l'intérieur d'un module, son corps de code. Distincte d'Adaptateur : une chose peut être un petit adaptateur avec une grande implémentation (un dépôt Postgres) ou un grand adaptateur avec une petite implémentation (un faux en mémoire). Utilisez « adaptateur » lorsque la jointure est le sujet ; « implémentation » sinon.

Profondeur : effet de levier à l'interface. La quantité de comportement qu'un appelant (ou un test) peut exercer par unité d'interface qu'il doit apprendre. Un module est profond lorsqu'une grande quantité de comportement se trouve derrière une petite interface, peu profond lorsque l'interface est presque aussi complexe que l'implémentation.

Jointure (Michael Feathers) : un endroit où vous pouvez modifier le comportement sans éditer à cet endroit ; l'emplacement où vit l'interface d'un module. Où placer la jointure est une décision de conception en soi, distincte de ce qui se trouve derrière elle. Évitez : frontière (surchargé avec le contexte délimité du DDD).

Adaptateur : une chose concrète qui satisfait une interface à une jointure. Décrit le rôle (quel emplacement il remplit), pas la substance (ce qu'il y a à l'intérieur).

Effet de levier : ce que les appelants obtiennent de la profondeur. Plus de capacité par unité d'interface qu'ils apprennent. Une seule implémentation est remboursée sur N sites d'appel et M tests.

Localité : ce que les mainteneurs obtiennent de la profondeur. Les changements, les bogues, les connaissances et la vérification se concentrent en un seul endroit plutôt que de se propager chez les appelants. Corrigé une fois, corrigé partout.

Profond vs peu profond

Module profond = petite interface + beaucoup d'implémentation :

┌─────────────────────┐
│  Petite Interface   │  ← Peu de méthodes, paramètres simples
├─────────────────────┤
│                     │
│ Implémentation      │  ← Logique complexe cachée
│ Profonde            │
│                     │
└─────────────────────┘

Module peu profond = grande interface + peu d'implémentation (à éviter) :

┌─────────────────────────────────┐
│    Grande Interface             │  ← Beaucoup de méthodes, paramètres complexes
├─────────────────────────────────┤
│ Implémentation fine             │  ← Seulement des passages
└─────────────────────────────────┘

Lors de la conception d'une interface, demandez-vous :

  • Puis-je réduire le nombre de méthodes ?
  • Puis-je simplifier les paramètres ?
  • Puis-je cacher plus de complexité à l'intérieur ?

Principes

  • La profondeur est une propriété de l'interface, pas de l'implémentation. Un module profond peut être composé intérieurement de petites parties mockables et échangeables ; elles ne font simplement pas partie de l'interface. Un module peut avoir des jointures internes (privées à son implémentation, utilisées par ses propres tests) ainsi qu'une jointure externe à son interface.
  • Le test de suppression. Imaginez supprimer le module. Si la complexité disparaît, c'était un simple passage. Si la complexité réapparaît chez N appelants, il méritait sa place.
  • L'interface est la surface de test. Les appelants et les tests traversent la même jointure. Si vous voulez tester au-delà de l'interface, le module a probablement la mauvaise forme.
  • Un adaptateur signifie une jointure hypothétique. Deux adaptateurs signifient une jointure réelle. N'introduisez pas une jointure à moins que quelque chose ne varie réellement à travers elle.

Concevoir pour la testabilité

De bonnes interfaces rendent les tests naturels :

  1. Acceptez les dépendances, ne les créez pas.

    // Testable
    function processOrder(order, paymentGateway) {}
    
    // Difficile à tester
    function processOrder(order) {
      const gateway = new StripeGateway();
    }
    
  2. Retournez des résultats, ne produisez pas d'effets secondaires.

    // Testable
    function calculateDiscount(cart): Discount {}
    
    // Difficile à tester
    function applyDiscount(cart): void {
      cart.total -= discount;
    }
    
  3. Petite surface. Moins de méthodes = moins de tests nécessaires. Moins de paramètres = configuration de test plus simple.

Relations

  • Un Module a exactement une Interface (la surface qu'il présente aux appelants et aux tests).
  • La Profondeur est une propriété d'un Module, mesurée par rapport à son Interface.
  • Une Jointure est l'endroit où se trouve l'Interface d'un Module.
  • Un Adaptateur se trouve à une Jointure et satisfait l'Interface.
  • La Profondeur produit un Effet de levier pour les appelants et une Localité pour les mainteneurs.

Cadres rejetés

  • La profondeur comme rapport entre le nombre de lignes d'implémentation et le nombre de lignes d'interface (Ousterhout) : récompense le gonflement de l'implémentation. Nous utilisons plutôt la profondeur comme effet de levier.
  • « Interface » comme le mot-clé interface de TypeScript ou les méthodes publiques d'une classe : trop étroit : l'interface ici inclut tous les faits qu'un appelant doit connaître.
  • « Frontière » : surchargé avec le contexte délimité du DDD. Dites jointure ou interface.

Aller plus loin

  • Approfondir un cluster étant donné ses dépendances, voir DEEPENING.md : catégories de dépendances, discipline de jointure et test de substitution plutôt que de couche.
  • Explorer des interfaces alternatives, voir DESIGN-IT-TWICE.md : lancer des sous-agents parallèles pour concevoir l'interface de plusieurs manières radicalement différentes, puis comparer sur la profondeur, la localité et le placement de la jointure.

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

Skills associés