La plupart des développeurs qui utilisent Claude Code en font un autocomplétion très coûteux. Ils posent une question, obtiennent une réponse, reformulent, obtiennent une meilleure réponse, et recommencent le lendemain depuis zéro. Tout le contexte — vos conventions de nommage, vos invariants architecturaux, vos erreurs passées — disparaît entre les sessions.

Le vrai levier est ailleurs : dans les skills personnalisés. Ce sont des fichiers SKILL.md qui transforment Claude Code d'un chatbot en un collaborateur qui connaît votre équipe, vos règles, et vos erreurs précédentes. Chaque skill est un actif capitalisé : il ne s'oublie pas, il est cohérent, et il s'améliore avec votre feedback.

Ce qu'est vraiment un skill

Un skill n'est pas un prompt sauvegardé. C'est un programme reproductible : il injecte le contexte nécessaire avant de raisonner, force une chaîne de pensée étape par étape, produit une sortie structurée, et documente explicitement ce qu'il doit faire quand il ne sait pas. La différence avec un prompt : un prompt s'adapte à la conversation courante, un skill est idempotent par construction.

Voici le skeleton que j'utilise pour tout nouveau skill :

# SKILL.md — [nom du skill]

## Rôle
[Une phrase. Quel expert ce skill incarne-t-il ?]

## Déclenchement
Invoqué par : `claude -p "Read [chemin]/SKILL.md et [instruction]"`
Données d'entrée attendues : [ce que l'utilisateur doit fournir]

## Contexte à injecter (AVANT de raisonner)
- Lire [fichier 1] — [pourquoi]
- Lire [fichier 2] — [pourquoi]
- Vérifier [état du dépôt / fichiers modifiés / etc.]

## Processus (chain-of-thought obligatoire)
Étape 1 — [nom] : [instruction précise, sortie attendue]
Étape 2 — [nom] : [instruction précise, sortie attendue]
Étape 3 — [nom] : [instruction précise, sortie attendue]
[...]

## Format de sortie
[Format exact, avec exemple si nécessaire]
Jamais de prose vague — toujours [structure précise].

## Critères de qualité
- [ ] [Vérification 1]
- [ ] [Vérification 2]
- [ ] [Vérification N]

## Modes d'échec
- Si [condition] → [comportement attendu, pas une tentative de deviner]
- Si le contexte est insuffisant → lister explicitement ce qui manque, s'arrêter

La section "Modes d'échec" est la plus importante et la plus souvent omise. Sans elle, le skill hallucine quand l'entrée est ambiguë. Avec elle, il s'arrête proprement et vous dit ce qu'il lui faut.

Les 5 skills que chaque équipe .NET devrait avoir

1. review — 50 vérifications, aucune oubliée

Le skill review est le plus utile à court terme. Il encode 50 vérifications réparties en six catégories. Voici l'extrait réel de mon SKILL.md :

## Catégories de vérification (50 checks)

### Architecture & SOLID (12 checks)
- SRP : une classe = une raison de changer
- OCP : extension via interface, pas via modification
- DIP : dépendances sur abstractions, jamais sur implémentations concrètes
- Violation de couche : logique métier dans un Controller/Handler direct
- Aggregate boundary : modification d'un agrégat depuis un autre agrégat
- Value Object manquant : primitives qui portent des règles métier (ex: Email, Money)
- Anemic domain model : entités sans comportement, logique dans des services
- Feature envy : une méthode qui utilise plus l'état d'une autre classe que le sien
- God class : plus de 200 lignes ou plus de 5 dépendances injectées
- Circular dependency : A → B → A (y compris via interfaces)
- Missing abstraction : switch/if sur un type en lieu et place d'un polymorphisme
- Primitive obsession : paramètres string/int là où un VO apporterait des invariants

### OWASP Top 10 (10 checks)
- A01 Broken Access Control : vérification tenant/ownership manquante (IDOR)
- A02 Cryptographic Failures : secrets hardcodés, connexions non chiffrées
- A03 Injection : entrées non validées transmises à SQL/OS/LDAP
- A04 Insecure Design : validation côté client uniquement
- A05 Security Misconfiguration : CORS wildcard, debug en production
- A06 Vulnerable Components : dépendances avec CVE connues
- A07 Auth Failures : JWT non validé, session non expirée
- A08 Integrity Failures : désérialisation non contrôlée
- A09 Logging Failures : exception swallowée sans log, PII dans les logs
- A10 SSRF : URLs construites depuis des entrées utilisateur

### Performance (10 checks)
- N+1 query : boucle sur IQueryable non matérialisé
- ToList() abuse : matérialisation prématurée avant filtrage
- async void : event handlers mis à part, jamais async void
- Task.Result / .Wait() : deadlock potentiel en ASP.NET
- ConfigureAwait(false) absent en bibliothèque
- Large object graph sans pagination
- Missing index : filtre EF Core sur colonne non indexée
- Eager loading excessif : Include().ThenInclude() sur 4+ niveaux
- String concatenation en boucle : StringBuilder attendu
- Allocation excessive : new dans une boucle chaude

### Tests (8 checks)
- Test sans assertion (arrange + act sans assert)
- Assert multiple sans message d'échec distinct
- Test du comportement d'un mock, pas du système réel
- Absence de test sur le chemin d'erreur
- Boundary condition manquante (valeur nulle, liste vide, limite exacte)
- Test de convention NetArchTest absent pour règle architecturale
- Fixture partagée mutable entre tests (isolation rompue)
- Nom de test qui ne décrit pas le comportement attendu

### Conventions & nommage (6 checks)
- Langage ubiquitaire non respecté (UserCapService au lieu de PlafondBénéficiaireService)
- Préfixe I systématique sur les interfaces non-abstractions
- Async non suffixé par Async
- CancellationToken absent sur méthode async publique
- Record vs class : Value Objects doivent être des records
- Visibility trop large : internal suffisant là où public est utilisé

### Observabilité (4 checks)
- Exception catchée sans log structuré
- Span/Activity absent sur opération I/O critique
- Log sans corrélation ID (TraceId/SpanId)
- Health check absent pour dépendance externe ajoutée

Ce qui rend cette liste utile, ce n'est pas sa longueur — c'est qu'elle est exhaustive par catégorie. Claude Code ne "passe" pas des checks selon son humeur : il les exécute tous et rend un rapport structuré avec sévérité (BLOQUANT / NON-BLOQUANT) et recommandation actionnable.

2. test-design — la suite de tests avant le code

Le skill test-design reçoit un use case ou un agrégat et produit la suite de tests complète avant qu'une ligne de production soit écrite. Sa valeur est dans son processus obligatoire :

## Processus test-design

Étape 1 — Cartographie des happy paths
  Pour chaque scénario nominal de la spec :
  - Identifier le Given/When/Then minimal
  - Nommer le test : [Contexte]_[Action]_[Résultat attendu]

Étape 2 — Détection automatique des boundary conditions
  Pour chaque paramètre d'entrée numérique : tester 0, négatif, max-1, max, max+1
  Pour chaque collection : tester vide, un élément, N éléments, N+1 si limite
  Pour chaque référence : tester null, entité inexistante, entité d'un autre tenant

Étape 3 — Chemins d'erreur
  Pour chaque règle métier explicite dans la spec :
  - Cas de violation de la règle
  - Cas de violation simultanée de plusieurs règles (ordre de validation)

Étape 4 — Invariants d'agrégat
  Pour chaque invariant identifié dans le domaine :
  - Test qui prouve que l'invariant tient après chaque command
  - Test qui prouve que la violation est rejetée avec le bon code d'erreur

Ce que j'aurais systématiquement oublié sans ce skill : les boundary conditions sur les collections. "Liste vide" et "exactement à la limite" sont les deux cas qui causent le plus de bugs en production et les deux cas les plus souvent absents des specs informelles.

3. po-agent — d'un ticket Jira vague à du Gherkin exhaustif

Le skill po-agent transforme une description de ticket en spec technique avec edge cases. Son format de sortie est fixe :

## Format de sortie po-agent

### Contexte métier
[Reformulation du problème en langage domaine — pas en termes techniques]

### Critères d'acceptation
Feature: [nom]
  Scenario: [happy path principal]
    Given ...
    When ...
    Then ...

  Scenario: [edge case 1 — identifié par le skill, pas dans le ticket]
    ...

### Décisions laissées au développeur
- [Question 1] — impact : [conséquence architecturale si mal décidée]
- [Question 2] — ...

### Hors-périmètre explicite
- [Ce qui ne doit PAS être implémenté dans ce ticket]

La section "Hors-périmètre explicite" est ce qui m'a le plus économisé de temps de review. Sans elle, l'implémentation déborde systématiquement sur des fonctionnalités adjacentes "logiques" — l'agent code ce qui lui semble naturel, pas ce qui était demandé.

4. kaizen — capturer chaque erreur avant qu'elle se répète

Le skill kaizen est invoqué automatiquement à la fin de chaque feature. Il lit le git log depuis le début du cycle, identifie les patterns problématiques, et met à jour deux fichiers : learned-errors.md et dotnet-kill-list.md.

Invocation :

claude -p "Read ~/.claude/skills/kaizen-agent/SKILL.md et analyse le cycle depuis le commit red: jusqu'au dernier fix: — identifie les patterns à capturer"

Sortie réelle après une feature récente :

## Analyse kaizen — cycle LCT-42423

### Patterns capturés

| Date       | Pattern                          | Erreur commise                        | Attendu                              | Statut  |
|------------|----------------------------------|---------------------------------------|--------------------------------------|---------|
| 2026-04-18 | handler-domain-logic             | Calcul métier dans le Handler         | Déléguer au Domain Service           | L1      |
| 2026-04-18 | missing-cancellation-token       | CancellationToken absent sur query EF | Toujours propager le token           | L2 (x4) |
| 2026-04-18 | record-vs-class-vo               | Value Object déclaré en class         | Records pour les Value Objects       | L1      |

### Escalades
- `missing-cancellation-token` passe L1 → L2 (4ème occurrence en 3 semaines)
  → Ajouté au SKILL.md review, catégorie Conventions, check obligatoire

### Kill-list update
Ajouté : "Handler ne doit pas contenir de logique métier — déléguer au Domain Service"
Pattern : logique dans le handler, tests verts, violation DDD silencieuse

Le mécanisme d'escalade est ce qui fait que la bibliothèque s'améliore seule. Un pattern L1 est noté. Un pattern L2 (3 occurrences) est ajouté au skill review. Un pattern L3 (5 occurrences) devient un test de convention automatique dans la suite d'intégration. L'erreur devient une contrainte structurelle.

5. security-agent — OWASP avec vos règles spécifiques

Le skill security-agent va au-delà des 10 checks OWASP génériques. Sa valeur est dans l'injection de vos règles spécifiques au contexte. Extrait de mon SKILL.md :

## Règles spécifiques au projet (à injecter avant l'analyse générale)

### Multi-tenancy (critique)
- Tout accès à une ressource doit filtrer par TenantId
- Toute query EF Core sur une entité tenant-aware doit inclure `.Where(x => x.TenantId == currentTenantId)`
- Les IDs exposés en URL ne sont jamais de confiance — toujours vérifier l'ownership

### JWT
- Claims attendus : sub, tenant_id, roles — si absent, rejeter 401 (pas ignorer)
- Ne jamais extraire le tenant depuis le token sans valider la signature
- exp doit être vérifié côté serveur — ne pas déléguer au middleware seul

### Validation d'entrée
- FluentValidation sur chaque Command et Query — pas de validation inline
- Les types primitifs string en paramètre de route sont suspects : préférer les types forts
- MaxLength sur tout champ texte persisté — pas de champ varchar(MAX) sans justification

Ces règles ne viennent pas d'un template — elles ont été écrites après des findings réels sur le projet. Chaque ligne représente une vulnérabilité qui aurait pu passer en production.

Écrire un skill qui fonctionne vraiment

Il y a quatre éléments qui distinguent un skill fiable d'un skill fragile :

1. Format de sortie concret. "Rends un rapport de review" produit n'importe quoi. "Rends un tableau Markdown avec colonnes : Sévérité | Fichier:Ligne | Description | Recommandation" produit quelque chose de parseable, comparable d'une session à l'autre, et intégrable dans un ticket.

2. Injection de contexte avant le raisonnement. Un skill qui raisonne sans lire les fichiers pertinents hallucine les conventions. La section "Contexte à injecter" force la lecture de votre CLAUDE.md, de votre learned-errors.md, et des fichiers impactés avant toute analyse. L'ordre compte : contexte d'abord, raisonnement ensuite.

3. Chain-of-thought forcée. "Analyse ce code" produit une réponse directe, souvent superficielle. "Étape 1 : liste tous les endroits où une entité est accédée sans vérification de tenant. Étape 2 : pour chacun, détermine si c'est intentionnel ou une omission. Étape 3 : classe par criticité." produit une analyse structurée avec un raisonnement traçable.

4. Documentation des modes d'échec. "Si le contexte est insuffisant pour déterminer si cette omission est intentionnelle → lister explicitement ce qui manque et s'arrêter" est plus utile que de laisser le skill improviser une réponse plausible mais fausse. Un skill qui s'arrête proprement vous fait gagner du temps. Un skill qui hallucine en silence vous en fait perdre.

L'effet de capitalisation

Voici comment la bibliothèque a évolué sur ma mission actuelle :

Semaine 1  : 3 skills (review, kaizen, po-agent) — génériques, pas encore calibrés
Semaine 4  : 7 skills — security-agent, test-design, create-api ajoutés
Semaine 8  : 12 skills — skills projet spécifiques (hris-conventions, fc-roles-mapper)
Semaine 16 : 19 skills — skills de debugging, observabilité, contrats d'API
Semaine 24 : 27 skills — couverture complète du cycle feature
  

Mais la croissance en nombre n'est pas l'indicateur pertinent. L'indicateur pertinent est le nombre de lignes dans dotnet-kill-list.md — chaque ligne est une classe entière d'erreurs qui ne peut plus se produire parce qu'elle est maintenant vérifiée automatiquement. Après six mois, la kill-list contient 43 entrées. Chacune représente une erreur que j'ai faite une fois et que ni moi ni les agents ne referont.

C'est l'effet de capitalisation : contrairement à l'expérience humaine qui s'étiole et s'oublie, un skill encodé est relu intégralement à chaque invocation.

Partager les skills entre projets et clients

J'utilise une convention de deux niveaux :

# Niveau global — s'applique à tous les projets
~/.claude/skills/
  review/SKILL.md          # 50 checks universels
  kaizen-agent/SKILL.md    # cycle kaizen
  security-agent/SKILL.md  # OWASP + règles transverses
  test-design/SKILL.md     # design de suite de tests

# Niveau projet — versionné avec le code
.claude/skills/
  create-api/SKILL.md      # conventions du projet (namespaces, structure)
  po-agent/SKILL.md        # domaine métier du client
  [client]-conventions/    # règles spécifiques au client

Les skills globaux sont dans un repo Git privé, versionné séparément. Je les forke par client quand les conventions divergent significativement — pas au niveau fichier, au niveau dossier. Un client bancaire avec des règles RGPD strictes aura un security-agent plus restrictif que le skills global, mais il hérite de la structure.

Le versionnement des skills suit les mêmes préfixes de commit que le code : kaizen: pour les améliorations de skills, fix: pour les corrections de comportement erroné, feat: pour les nouveaux skills.

Le skill qui économise 2 heures par semaine : create-api

Le skill create-api prend une user story et génère la slice verticale complète : Command, Handler, Validator FluentValidation, endpoint minimal API, et suite de tests xUnit. Voici un exemple réel.

Entrée :

LCT-42500 — En tant que gestionnaire RH, je veux pouvoir désactiver
un bénéficiaire afin que ses commandes futures soient rejetées
automatiquement.

Sortie générée par le skill (extrait) :

// Application/Commands/DeactivateBeneficiary/DeactivateBeneficiaryCommand.cs
namespace Edenred.Smarter.Client.Bff.Application.Commands.DeactivateBeneficiary;

public sealed record DeactivateBeneficiaryCommand(Guid BeneficiaryId) : IRequest<Result>;

// Application/Commands/DeactivateBeneficiary/DeactivateBeneficiaryCommandValidator.cs
public sealed class DeactivateBeneficiaryCommandValidator
    : AbstractValidator<DeactivateBeneficiaryCommand>
{
    public DeactivateBeneficiaryCommandValidator()
    {
        RuleFor(x => x.BeneficiaryId)
            .NotEmpty()
            .WithMessage("L'identifiant du bénéficiaire est obligatoire.");
    }
}

// Application/Commands/DeactivateBeneficiary/DeactivateBeneficiaryCommandHandler.cs
public sealed class DeactivateBeneficiaryCommandHandler(
    IBeneficiaryRepository repository,
    ICurrentTenantProvider tenantProvider)
    : IRequestHandler<DeactivateBeneficiaryCommand, Result>
{
    public async Task<Result> Handle(
        DeactivateBeneficiaryCommand command,
        CancellationToken cancellationToken)
    {
        var tenantId = tenantProvider.GetCurrentTenantId();
        var beneficiary = await repository
            .GetByIdAsync(command.BeneficiaryId, tenantId, cancellationToken);

        if(beneficiary is null)
        {
            return Result.Failure("BENEFICIARY_NOT_FOUND");
        }

        beneficiary.Deactivate();
        await repository.SaveAsync(beneficiary, cancellationToken);

        return Result.Success();
    }
}

// Tests/Commands/DeactivateBeneficiary/DeactivateBeneficiaryCommandHandlerTests.cs
public sealed class DeactivateBeneficiaryCommandHandlerTests
{
    [Fact]
    public async Task Handle_WhenBeneficiaryExists_DeactivatesAndReturnsSuccess()
    { ... }

    [Fact]
    public async Task Handle_WhenBeneficiaryNotFound_ReturnsFailure()
    { ... }

    [Fact]
    public async Task Handle_WhenBeneficiaryBelongsToAnotherTenant_ReturnsFailure()
    { // IDOR guardrail — généré automatiquement par le skill
      ...
    }

    [Fact]
    public async Task Handle_WhenBeneficiaryAlreadyDeactivated_ReturnsFailure()
    { // Idempotence — boundary condition détectée par le skill
      ...
    }
}

Ce qui me frappe à chaque utilisation : le test WhenBeneficiaryBelongsToAnotherTenant_ReturnsFailure est généré systématiquement, même si la user story n'en parle pas. Ce n'est pas de la chance — c'est parce que mon skill encode explicitement la règle "tout accès à une ressource génère un test IDOR". La user story ne spécifie jamais les invariants de sécurité transversaux. Le skill les applique toujours.

Sur ma semaine de travail actuelle, le skill create-api intervient sur 3 à 5 nouveaux endpoints. Le scaffold complet prend 4 minutes contre 45 minutes à la main. Sur une semaine de 5 jours, c'est 2 heures récupérées — sans compter les reviews qui ne trouvent plus les oublis habituels.

Par où commencer

Ne commencez pas par écrire 10 skills. Commencez par le skill review avec vos 10 checks les plus douloureux — ceux que vous retrouvez systématiquement dans vos PR. Utilisez-le pendant deux semaines. Notez ce qu'il rate. Corrigez. Ajoutez les checks manquants. Après deux semaines, ce skill connaît votre équipe mieux que n'importe quel guide de contribution.

Ensuite seulement, ajoutez kaizen pour capturer les patterns. Puis create-api pour le gain de temps. La bibliothèque croît par expérience réelle, pas par anticipation de besoins imaginés.

"Un skill n'est pas une documentation qu'on écrit une fois. C'est un expert qu'on forme en continu — avec chaque erreur comme leçon."

Vous voulez mettre en place une bibliothèque de skills dans votre équipe .NET ?

Je peux auditer vos patterns récurrents, configurer les premiers skills
calibrés pour votre contexte, ou intervenir en mission .NET 9.

✉ Me contacter Voir mes services →