Claude Code comme équipe QA du développeur solo
by Sylvain Artois on 27 févr. 2026
- #QA
- #Testing
- #AI
- #Claude Code
Dans une petite startup, les bugs coûtent cher. Pas seulement en temps d’ingénierie — en confiance. Quand vos dix premiers clients dépendent de votre produit et que quelque chose casse, le coût n’est pas un ticket Jira. C’est un coup de téléphone, des excuses, et une fonctionnalité qui glisse d’une semaine.
Wispra est une petite startup avec laquelle je travaille en tant qu’expert freelance en ingénierie IA. Nous optimisons le GEO (Generative Engine Optimization) pour nos clients — en aidant les commerces locaux à apparaître dans les résultats de recherche générés par l’IA. La stack technique est un backend FastAPI, PostgreSQL, et une bonne dose d’orchestration de LLMs. L’équipe est petite : je gère l’essentiel de la stack IA moi-même.
J’ai plus de 1 300 tests unitaires. Ils couvrent les services, les routes, les cas limites. Ils passent. Et pourtant, ils ne suffisent pas.
Les tests unitaires vérifient des fonctions isolées avec des dépendances mockées. Ils ne détectent pas le bug où une fonction de génération de prompts produit des doublons parce que la logique de déduplication dépend d’un état de base de données qu’aucune fixture ne reproduit. Ils ne détectent pas la corruption silencieuse de données quand une migration change la valeur par défaut d’une colonne et que trois services en aval supposent l’ancienne valeur.
Ce dont j’avais besoin, c’était quelque chose entre les tests unitaires et une passe QA complète. Quelque chose qui vérifie l’état réel de la base de données après l’exécution d’un pipeline réel.
Le détour par les tests E2E
J’ai essayé les tests end-to-end traditionnels. J’ai écrit un article sur la mise en place de miroirs de sites web locaux avec des alias réseau Docker et Caddy pour que le crawler tape sur du vrai HTML servi localement. Ça marche, mais les tests E2E dans une startup qui avance vite peuvent être un fardeau difficile à maintenir. On peut réécrire un module en quelques jours — et passer une journée de plus à corriger les tests E2E qui ont cassé. Même avec l’aide de Claude Code, le ROI est négatif quand on livre plusieurs fonctionnalités par semaine.
Comment ça a commencé : un plan de test et une prise de conscience
Récemment, j’ai travaillé sur un ensemble de fonctionnalités majeur : GEO Analysis V2. Ça couvrait 8 PRDs (Product Requirements Documents) — génération de prompts, crawling, scoring, suivi des concurrents, analyse d’annuaires et benchmarking.
Mon workflow avec Claude Code était assez classique à ce stade : des prompts XML stockés dans le dépôt, génération de PRD, puis implémentation. Une fois les 8 PRDs terminés, j’avais beaucoup de nouveaux comportements à vérifier avant la mise en production.
J’ai donc demandé à Claude de générer un plan de test — un document structuré listant chaque requête SQL à exécuter, chaque endpoint API à vérifier, chaque valeur attendue à contrôler. L’idée était de l’exécuter à la main. Le résultat : un fichier markdown rempli de blocs SQL et de cases à cocher :
## Phase 3 — Vérifications du Plan Starter
### 3.1 Distribution des prompts
```sql
SELECT prompt_type, COUNT(*) as nb
FROM generated_prompts
WHERE run_id = '<RUN_ID>' AND plan = 'starter'
GROUP BY prompt_type ORDER BY prompt_type;
```
Attendu (10 prompts) :
| type | nb |
|------|-----|
| brand-perception | 1 |
| transactional | 9 |
- [ ] Total = 10 prompts
- [ ] 0 informational, 0 comparative (budget starter)
Et puis je l’ai regardé en me disant : Claude peut déjà exécuter des requêtes SQL via docker exec. Il peut appeler des APIs avec curl. Il peut lancer pytest. Pourquoi c’est moi qui exécute tout ça ?
J’ai demandé à Claude d’exécuter le plan de test. Il l’a fait. La première exécution a détecté 3 vrais bugs avant le déploiement. La mise en production s’est bien passée.
Architecture : slash commands, sous-agents et état
Ce qui a commencé comme une expérience ponctuelle est devenu un workflow reproductible. Claude Code supporte les slash commands personnalisées — des fichiers markdown placés dans .claude/commands/ qu’on invoque avec /nom-de-la-commande. Chaque fichier a un frontmatter YAML et un corps en markdown avec les instructions.
J’ai construit deux commandes :
/qa— pour la QA au quotidien (exécuter les plans de test, les tests unitaires, générer un rapport)/release-qa {version}— pour la validation pré-release (ajoute l’analyse des commits, les notes de version, le tagging git)
---
name: qa
description: Exécution des tests QA — exécuter les plans de test, les tests unitaires et générer un rapport de résultats
---
# QA — Project AI
Vous êtes l'exécuteur des tests QA. Exécutez les plans de test QA sur l'état actuel du dépôt.
Votre rôle est de **coordonner des sous-agents** pour chaque étape majeure...
La décision de conception clé : la slash command est un orchestrateur, pas un exécuteur. Elle délègue le travail à des sous-agents (via l’outil Task de Claude) qui gèrent chacun une portion du plan de test. Ça garde le contexte de conversation principal léger — les sorties SQL verbeuses et les logs de pytest restent dans le contexte de chaque sous-agent. L’orchestrateur ne remonte que des résumés structurés.
L’orchestrateur maintient un objet d’état léger qui grandit au fil du workflow :
QA_STATE:
RUN_IDS: { STARTER: uuid, PLUS: uuid, PRO: uuid }
PHASE_RESULTS: { 0..8, logs }
UNIT_TEST_SUMMARY: { PASSED: 1332, FAILED: 0, SKIPPED: 0 }
REPORT_PATH: QA/results/TEST_RESULTS_v0.9.0_2026-02-27.md
VERDICT: READY TO SHIP
Chaque sous-agent ne reçoit que la portion d’état dont il a besoin. C’est comme passer des arguments de fonction — couplage minimal, contrats clairs.
/qa command (orchestrateur)
|-- Étape 0 : Vérif Docker, vidage logs, ping DB (direct)
|-- Étape 1 : Phase 0 prérequis --> sous-agent
|-- Étape 1 : Phases 1-2 exécution du pipeline (interactif)
|-- Étape 1 : Phases 3-6 vérifications par plan --> sous-agent
|-- Étape 1 : Phase 7 + Phase 8 + Logs --> 3 sous-agents parallèles
|-- Étape 2 : Test du pipeline de données --> sous-agent
|-- Étape 3 : Génération du rapport --> sous-agent
+-- Étape 4 : Analyse des écarts + correction --> sous-agent + utilisateur
Implémentation : ce qui se passe dans chaque étape
Étape 0 — La mise en place, ennuyeuse mais critique
Chaque exécution QA commence par trois vérifications de base. Si Docker ne tourne pas, rien ne fonctionne. Si le fichier de log n’est pas vidé, on ne peut pas distinguer les erreurs de cette exécution.
# 1. Vérifier que les services Docker sont up
cd /path/to/infrastructure && docker compose ps
# 2. Vider les logs pour un point de départ propre
> /path/to/api/logs/app.log
# 3. Vérifier la connectivité à la base de données
docker exec db_container psql -U db_user -d database -c "SELECT 1"
Ces commandes s’exécutent directement dans le contexte principal — pas besoin de sous-agent pour trois commandes rapides.
Déléguer le travail aux sous-agents
Le pattern central : l’orchestrateur envoie un prompt structuré à un sous-agent general-purpose. Le prompt spécifie quoi lire, comment exécuter, et quel format retourner.
**Déléguer à un sous-agent `general-purpose`** avec ce prompt :
> Vous exécutez la Phase 3 (Vérifications par Plan) du plan de test QA.
>
> **RUN IDs :**
>
> - STARTER : {RUN_ID_STARTER}
> - PLUS : {RUN_ID_PLUS}
> - PRO : {RUN_ID_PRO}
>
> 1. Lire les Phases 3, 4 et 5 de `QA/TEST_PLAN.md`
> 2. Remplacer chaque placeholder `<RUN_ID>` par les UUIDs réels
> 3. Exécuter chaque vérification SQL :
> `docker exec db_container psql -U user -d database -c "SQL"`
> 4. Évaluer chaque résultat par rapport aux valeurs attendues
>
> **Retourner les résultats sous forme de tableaux markdown, un par phase :**
>
> | Vérification | Résultat | Notes |
> | ------------ | -------------- | ------- |
> | description | PASS/FAIL/SKIP | détails |
Le sous-agent lit le fichier du plan de test, substitue les RUN_IDs, exécute chaque requête SQL et commande curl, compare les résultats aux valeurs attendues, et retourne un résumé structuré. L’orchestrateur collecte ces résumés dans son état.
Trois sous-agents, un seul message
Certaines phases sont indépendantes. Les tests unitaires, les vérifications de statut admin et la vérification des logs ne dépendent pas les uns des autres. La slash command demande à Claude de lancer les trois dans un seul message — ils s’exécutent en parallèle, ce qui réduit significativement la durée de la tâche.
### Phase 7 + Phase 8 + Vérification des Logs (Sous-agents Parallèles)
**Lancer les trois sous-agents dans un SEUL message.**
#### Phase 7 — Tests Unitaires
**Déléguer au sous-agent :** Exécuter pytest, retourner les compteurs pass/fail/skip...
#### Phase 8 — Vérifications Admin
**Déléguer au sous-agent :** Exécuter les vérifications SQL pour les vues admin...
#### Vérification des Logs
**Déléguer au sous-agent :** grep ERROR|CRITICAL dans les logs, catégoriser...
SQL : la vraie couche de vérification
Le cœur de chaque phase, ce sont les requêtes SQL contre la base de données réelle. Pas des mocks HTTP, pas des assertions sur des fixtures — de vraies requêtes contre le même schéma et les mêmes données qu’en production.
-- Vérifier que le nombre de prompts correspond à la configuration du plan
SELECT prompt_type, COUNT(*) as nb,
SUM(CASE WHEN brand_mentioned THEN 1 ELSE 0 END) as branded
FROM generated_prompts
WHERE run_id = '78e0fd5a-...'
GROUP BY prompt_type ORDER BY prompt_type;
-- Attendu : total=20, ...
Chaque vérification devient une ligne dans le tableau de résultats : PASS si la valeur correspond, FAIL sinon, SKIP si ça nécessite une vérification manuelle (comme des contrôles visuels de l’interface).
Le plan de test qui se corrige tout seul
C’est probablement la partie la plus utile du système. Après chaque exécution QA, un sous-agent compare les résultats réels avec les valeurs attendues du plan de test et propose des corrections.
Trois catégories d’écarts :
STALE_EXPECTATIONS — la valeur attendue est obsolète, ce n’est pas un bug. Par exemple, le plan de test dit « 1300 tests unitaires » mais vous en avez ajouté 47 nouveaux. Des niveaux de confiance aident au triage : HIGH signifie que le même écart est apparu sur 3 exécutions consécutives ou plus (mise à jour sans risque), MEDIUM signifie 2 exécutions, LOW signifie première occurrence (investiguer avant de mettre à jour).
MISSING_CHECKS — de nouvelles fonctionnalités observées dans les résultats sans couverture dans le plan de test. Une nouvelle colonne de base de données est remplie mais aucune vérification SQL ne la contrôle.
OBSOLETE_CHECKS — des vérifications pour des comportements qui n’existent plus. Une fonction legacy a été supprimée, mais le plan de test la cherche encore.
STALE_EXPECTATIONS:
| Phase | Vérification | Actuel | Proposé | Confiance |
|-------|------------------|-----------|-----------|------------|
| 7 | Nb tests unit. | 1300 | 1347 | HIGH |
MISSING_CHECKS:
| Phase | Vérification proposée | Raison |
|-------|------------------------|-------------------------------|
| 5 | Plage reputation_score | Nouvelle feature en v0.8.3 |
OBSOLETE_CHECKS:
| Phase | Vérification | Raison |
|-------|------------------------|-------------------------------|
| 7 | Grep fonction legacy | Fonction supprimée en v0.8.2 |
Le développeur passe en revue chaque proposition et approuve, sélectionne des corrections spécifiques, ou ignore. Ce n’est qu’après approbation qu’un autre sous-agent applique les modifications au fichier du plan de test. Une trace d’audit est ajoutée en haut du document :
> Dernière correction : 2026-02-26 (exécution QA)
> Appliqué : 3 valeurs attendues mises à jour, 7 vérifications ajoutées, 0 vérifications obsolètes supprimées
Avec le temps, le plan de test s’éloigne de moins en moins de la réalité. Il devient un document vivant qui suit le comportement réel du système.
Du QA à la release : /release-qa v0.8.3
La commande /release-qa étend /qa avec des étapes de gestion de release. Elle prend un numéro de version en argument :
---
name: release-qa
description: Workflow QA pré-release complet
argument-hint: <version ex. v0.9.0>
---
Trois étapes supplémentaires au début :
-
Analyse des commits — un sous-agent lit
git logdepuis le dernier tag, inspecte les fichiers modifiés et catégorise les commits en fonctionnalités, corrections de bugs, améliorations et migrations. -
Mise à jour du plan de test — en fonction des changements de code, un sous-agent décide si le plan de test a besoin de nouvelles vérifications, de valeurs attendues mises à jour ou d’éléments supprimés.
-
Génération des notes de release — un sous-agent parallèle rédige les notes de version en suivant le format des releases précédentes dans le dépôt.
Les étapes 2 et 3 s’exécutent en parallèle puisqu’elles dépendent toutes deux uniquement de la sortie de l’analyse des commits.
À la fin, si toutes les vérifications passent, la commande crée le tag git et le pousse :
git tag v0.9.0 && git push origin v0.9.0
Pourquoi ça marche : des données de production, pas des fixtures
La décision de conception la plus importante : tester contre une copie locale de la base de production, pas des fixtures.
Les fixtures sont une représentation de ce que vous imaginez être un jeu de données. Un dump de production, c’est les données réelles — avec les lignes legacy, les cas limites et les incohérences du monde réel qu’aucun auteur de fixture n’aurait l’idée d’inclure.
Ça veut dire que la QA détecte des bugs que les fixtures ne révéleraient jamais : des problèmes de migration de données, des cascades de clés étrangères, des formules de scoring qui cassent sur des valeurs réelles.
Un mot de prudence : si vos données de production contiennent des informations personnelles, il faut les anonymiser. En Europe, le RGPD rend ça non négociable. PostgreSQL Anonymizer gère ça très bien — il peut masquer les emails, noms et autres champs sensibles tout en préservant la structure des données. Dans mon cas, les données sont orientées business (noms d’entreprises, sites web, scores SEO), pas personnelles, donc le risque est moindre. Mais réfléchissez-y avant de copier votre base de données.
Je n’essaie pas de tout tester. Le système exécute les pipelines principaux pour quelques entreprises sur différents plans d’abonnement et vérifie l’état en sortie. Si le chemin nominal fonctionne sur des données réelles, la release est en bonne forme.
Résultats d’une exécution réelle :
## Résumé QA — v0.8.3
| Métrique | Précédent (v0.8.0) | Actuel (v0.8.3) | Delta |
| ------------------- | ------------------ | --------------- | ----- |
| Total vérifications | 26 | 72 | +46 |
| PASS | 26 | 66 | — |
| FAIL | 0 | 0 | — |
| SKIP | 0 | 6 | +6 |
Verdict : READY TO SHIP
Les 6 vérifications ignorées sont des éléments de vérification visuelle (badges dans l’interface admin) qui nécessitent un œil humain. Tout le reste est automatisé.
Limites et honnêteté
Ce n’est pas un remplacement pour une vraie équipe QA. Ça fonctionne pour un développeur solo ou une petite équipe qui livre vite. À l’échelle, il faut des humains qui font du test exploratoire, vérifient l’accessibilité et pensent à des scénarios qu’aucun plan de test ne couvre.
Les limites de la fenêtre de contexte. Les plans de test volumineux peuvent dépasser ce qu’un seul sous-agent peut garder en contexte. Le pattern de délégation phase par phase résout ça — chaque sous-agent gère une portion, pas le document entier.
Le coût. Chaque exécution QA consomme des tokens de LLM. Une exécution complète de /release-qa avec 70+ vérifications, plusieurs sous-agents et la génération de rapport n’est pas gratuite. Pour une startup, le coût est justifié par les bugs détectés. Mais mieux garder un oeil sur les tokens consommés.
Le non-déterminisme. L’orchestration basée sur un LLM fait que la même vérification peut parfois être interprétée légèrement différemment. Le format de prompt structuré et le format de retour explicite réduisent ça, mais ne l’éliminent pas. Je n’ai cependant jamais vu ça causer un faux PASS — les requêtes SQL sont déterministes et le sous-agent ne fait que rapporter la sortie.
Ce que j’ai réellement livré
Deux slash commands. Un plan de test en markdown. Une copie de la base de production. Des sous-agents qui exécutent du SQL, curl et pytest.
Le système n’est pas élégant. C’est une collection de fichiers markdown qui disent à une IA quoi vérifier et comment le vérifier. Mais après plusieurs releases, il a détecté de vrais bugs, maintenu le plan de test à jour grâce à sa propre boucle de correction, et automatisé la partie la plus fastidieuse du développement solo : vérifier que tout fonctionne encore après un gros changement.
Pour un CTO solo qui livre des fonctionnalités chaque semaine, ça a été le meilleur investissement en qualité en dehors des tests unitaires. Pas des tests E2E (trop coûteux à maintenir seul). Pas des passes QA manuelles (trop lentes et sujettes à erreur quand c’est vous qui avez écrit le code). Ça se situe entre les deux — suffisamment automatisé pour tourner en quelques minutes, suffisamment complet pour détecter de vrais problèmes, et suffisamment peu cher pour être exécuté avant chaque release.