Playwright et TypeScript sans connaître TypeScript : un ingénieur, une couche d’IA et quatre étapes de validation.
Un ingénieur QA venant de Selenium et C# crée un framework Playwright en TypeScript avec l’aide de l’IA : structure, règles d’architecture, hooks et quatre points de contrôle.
Sources: I Built a Playwright Framework in TypeScript Without Knowing TypeScript — Using AI
Un dépôt qui peut être cloné, contenant des compétences et des règles, ainsi qu’un Playwright piloté par MCP capable d’explorer l’application sans aide, est facile à promettre mais difficile à mettre en œuvre. Celui-ci existe déjà en tant que projet fonctionnel : clonez-le, exécutez une seule commande et les tests seront lancés sur un site en ligne. L’histoire qui se cache derrière cela commence par un ensemble de tests hérités et une question liée aux langages de programmation.
La migration qui n’a jamais été approuvée
Une équipe de contrôle qualité avait passé quatre ans à faire évoluer un ensemble de tests Selenium hérité, et certains collègues qui sont ensuite passés au développement le connaissaient encore depuis plus longtemps. Son noyau était constitué de milliers de tests UI en C#, accumulés au fil des ans par des auteurs qui avaient pour la plupart quitté l’entreprise. Le passage à Playwright figurait depuis longtemps sur la liste des souhaits : il est plus rapide, plus fiable et plus agréable à utiliser, mais ce changement n’a jamais eu lieu.
La raison était banale. Personne ne disposait de cent heures supplémentaires d’ingénierie à consacrer à une réécriture dont la valeur commerciale était nulle, et un manager ne peut pas approuver le fait que « le nouvel outil est meilleur ». Des arguments techniques sont toujours disponibles, mais plus l’ensemble de logiciels est vaste, plus la migration est coûteuse et plus les avantages semblent minimes en comparaison. La réécriture doit être financée à l’avance et en totalité. Son retour sur investissement n’apparaît jamais sous forme de chiffre dans un rapport : il se manifeste par moins de tests échoués et des corrections plus rapides sur une période de quelques années. L’idée est donc restée en suspens. Playwright et TypeScript apparaissaient de plus en plus souvent dans les offres d’emploi, et ne rien faire devenait de plus en plus coûteux, tant pour le projet que pour la personne concernée.
Puis une autre équipe a commencé quelque chose de nouveau, une page blanche : pas de héritage, pas de mur de tests à ne pas briser, et pas de migration à justifier. Une proposition de développer des outils d’automatisation d’UI et d’API a finalement introduit Playwright et TypeScript dans le travail quotidien. Cela a également mis en évidence un fait dérangeant : Java et C# étaient familiers, avec dix ans de code de tests derrière eux, mais il s’agissait de tests et non de développement, ce qui implique une profondeur différente. JavaScript, TypeScript et Playwright eux-mêmes étaient nouveaux. Une question est alors apparue, qui aurait semblé rhétorique il y a deux ans : dans quel mesure le manque de connaissance d’un langage peut-il freiner quelqu’un en 2026, avec l’IA autour de nous ?
D’où vient cette expérience
Aucun jour en particulier n’a déclenché ce changement. Il s’est progressivement imposé face à la prise de conscience que se contenter de tester et d’écrire des tests ne menait nulle part. Le contrôle qualité trouve sa justification non pas dans le nombre de bogues ou le nombre de lignes de code, mais en améliorant la qualité du produit grâce à des processus, une documentation, de l’automatisation et une communication efficaces ; au fil des années, une approche systématique a prouvé son efficacité par rapport aux efforts individuels isolés.
Deux facteurs ont poussé dans la même direction.
Une tentative provint d’un bot de trading cryptographique écrit en Python avec Claude Code par quelqu’un qui ne connaissait pas Python. La mise en œuvre commença avec l’excitation d’un premier succès, mais se termina par l’échec une fois que le code devint incompréhensible pour tous, sans que personne ne puisse expliquer pourquoi les tests échouaient ou comment les corriger. La leçon réside dans l’écart entre la création de code et la construction d’un système : le bot fonctionnait, mais il était impossible de le maintenir. Aucune carte du projet n’existait, les correctifs engendraient de nouveaux problèmes, et les tokens disparaissaient au profit de descriptions de symptômes plutôt que de recherches de causes.
Une autre pression provenait du travail quotidien, où l’IA s’infiltrait progressivement dans les routines de contrôle qualité. Les essais portaient sur un processus allant de la tâche à la fin du test, sur l’élaboration de règles précises que le modèle devait suivre à la lettre, ainsi que sur la mise en place d’un flux reproductible et contrôlable. Les collègues utilisaient l’IA comme une boîte de recherche plus performante ou une machine à code. Très peu d’entre eux l’incorporaient dans un système permettant de la rendre prévisible, ce qui semblait une opportunité gaspillée, car c’est la partie systématique qui apporte la valeur réelle.
Ces deux approches ont donné naissance au projet Playwright, formulé sous forme d’hypothèse : jusqu’où peut aller un ingénieur doté de solides connaissances de base avec l’IA qui s’occupe de la saisie, et est-il possible de créer un cadre de test complet sans écrire une seule ligne dans un langage que l’ingénieur ne maîtrise pas couramment ?
Pourquoi TypeScript et non C# ou Java
La première vérification était évidente : Playwright existe également pour C# et Java, il était donc peut-être inutile de changer de langage. La différence ne résidait pas dans la gestion du navigateur. Cette partie est identique partout, avec la même API, les mêmes sélecteurs et les mêmes navigateurs. La différence réside dans la manière dont les tests sont organisés et exécutés.
En JavaScript et TypeScript, Playwright dispose d’un exécuteur de tests natif qui prend en charge l’exécution parallèle, les fixtures, les rapports et le suivi automatique. Pour C# et Java, il n’y en a pas. Les équipes recourent à ce que l’écosystème utilise déjà, que ce soit xUnit, NUnit, JUnit ou TestNG, et Playwright ne fournit que des classes de base, ce qui oblige à développer soi-même l’organisation et le parallélisme. C’est là la véritable contrainte : non pas une syntaxe étrange ou l’absence d’une méthode, mais la nécessité de réinventer la roue, y compris le parallélisme, l’isolation des données par thread, les mécanismes de base et les rapports, des problèmes qui ont déjà été résolus une fois et qu’il vaut mieux éviter. À côté de cela se trouvait une option déjà entièrement prête, dans un langage que personne ne connaissait. Playwright a évolué aux côtés de JavaScript, il s’agit donc d’une solution native et non d’une solution externe.
Que signifie vraiment « construit sans connaissances »
Dire que le projet a été développé sans aucune connaissance serait non seulement inexact, mais aussi un mensonge éhonté. Dix ans d’expérience en contrôle qualité sont à l’origine de ce travail : création de processus de zéro et test de systèmes de tous types, des applications web et des API aux applications mobiles. C’est cette expérience qui a permis d’éviter que le projet ne soit submergé par une mer de code généré. La syntaxe de TypeScript et Playwright est restée un atout discret tout au long du projet.
Quelques cours ont été utiles. Entre les expériences précédentes et cette mise en œuvre, plusieurs cours d’Anthropic portaient sur la conception d’agents, la chaînage de compétences et la formulation de tâches pour l’IA. Ils ont complété l’expérience sans la remplacer. Ce qui comptait, c’était l’approche adoptée. Le projet n’est pas né d’un développeur cherchant simplement des tests ; il est né d’un ingénieur qui a indiqué au modèle les règles, les limites architecturales et ce qui devait être vérifié, puis l’a laissé agir dans ces cadres, ce qui a transformé un échange chaotique en un processus bien géré.
Un squelette, avec deux réserves
Deux points doivent être précisés avant de décrire la structure et ses décisions.
Tout d’abord, le projet n’est qu’un squelette et non un produit prêt à l’emploi, destiné à être modifié, adapté ou même déconstruit. Il est partagé afin de présenter une approche, et non pour prétendre à la perfection. Ceux qui cherchent un outil déjà prêt seront déçus, tandis que ceux qui cherchent un modèle pour développer un système basé sur l’IA en trouveront un. Les équipes ont leurs propres contraintes, exigences en matière de sécurité et intégrations, et un design adapté à une démonstration publique peut nécessiter de sérieuses modifications lorsqu’il s’agit de l’intégrer dans des systèmes d’authentification unique, de double authentification, de règles spécifiques pour les données de test et d’un système de gestion des tests. Ainsi, la logique sous-jacente au code constitue la partie portable.
Deuxièmement, l’ensemble d’outils était minimal. Il se composait de Claude Code avec l’abonnement de base de vingt dollars par mois, de Visual Studio Code en tant qu’éditeur, d’un serveur MCP configuré pour Playwright qui permettait au modèle d’explorer l’application par lui-même, et, probablement le plus important, d’une esprit frais et de temps libre. Aucune solution coûteuse destinée aux entreprises, aucune infrastructure complexe et aucun exécuteur de tests distribué n’étaient impliqués. Tout fonctionne sur un ordinateur portable ordinaire, ce qui fait partie du message : pour commencer, il n’est pas nécessaire de recourir à quoi que ce soit de surnaturel, seulement une compréhension de ce qui est construit et le désir de le créer.
Ce que couvre le projet et sa structure
La cible est Toolshop, une boutique en ligne de démonstration publique conçue pour s’entraîner à l’automatisation. Le choix est important : il s’agit d’un système ouvert sans contexte commercial, ce qui permet à n’importe qui de cloner le répertoire, d’installer les dépendances avec une seule commande et d’exécuter des tests sur un site en ligne, sans avoir à créer de comptes ni à parcourir de documentation.
La suite comprend trente-sept tests, dont trente-quatre réussis et trois échoués délibérément afin que le rapport montre à quoi ressemble un échec. Les tests API couvrent la logique serveur, les tests UI couvrent l’interface, et les tests hybrides créent un état via l’API avant de le vérifier dans le navigateur. Cela suffit amplement pour démontrer l’architecture, mais c’est bien trop peu pour évaluer la couverture du produit, qui dépend d’un produit spécifique et de ses risques commerciaux, et non du nombre de tests contenus dans une démonstration. La structure du répertoire :
config/
env.ts // The only place where environment variables are read
timeouts.ts // Timeouts, also in one place
core/
run-user.ts // The user registered by this specific run
global-setup.ts // Register → UI login → data snapshot → download OpenAPI spec
global-teardown.ts // Cleanup after the run: remove favorites and the user
allure-labels.ts // Parses [Section / Subsection] from the test name into report structure
allure-categories.ts // Buckets for triaging failures in the report
api/
generated/schema.d.ts // Types generated from OpenAPI, never touched by hand
types.ts // Domain types derived from the schema
client.ts // openapi-fetch, paths and params checked by the compiler
contract.ts // Runtime validation of API responses against the schema
auth.api.ts // Typed clients on top of api/client.ts
pages/
base.page.ts // Base page class
components/ // Elements present on every page (site header)
auth/ // Page Objects for authorization pages
account/ // Account area: account, profile, favorites
shop/ // Catalog, product page, checkout
fixtures/
base.fixture.ts // All dependency injection, test/expect only imported from here
data/ // Strings and routes, so they don’t live inside tests
utils/ // Logger, reference data snapshot
tests/
auth/ // Everything auth-related in one folder
auth.api.spec.ts // API: login, token lifetime
registration.api.spec.ts
login.ui.spec.ts // UI: login form, errors, logout
catalog/
products.api.spec.ts
catalog.ui.spec.ts
cart/
cart.ui.spec.ts
favorites/
favorites.api.spec.ts
favorites.hybrid.spec.ts // Hybrid: state via API, verification via UI
profile/
profile.hybrid.spec.ts
contract/
contract.api.spec.ts // Matching live API responses against the spec
demo/ // Tests tagged @demo that fail on purpose
docs/ // Feature maps, checklists, test cases, plans
.claude/ // AI layer: rules, skills, agents, hooks```
Structurer les décisions de manière évolutive
- Les fonctionnalités d’abord, les couches ensuite. Un choix qui peut sembler étrange au premier abord influence en réalité l’évolutivité globale : les tests sont regroupés par fonctionnalité, et la couche correspond à un suffixe de nom de fichier, comme dans
login.ui.spec.ts,auth.api.spec.tsetcheckout.hybrid.spec.ts. Lorsqu’un développeur modifie la logique d’autorisation, il trouve tous les tests associés soustests/auth/sans avoir à fouiller dans trois dossiers différents. Avec trente tests, cela passe inaperçu, et une organisation par couches donne un aspect ordonné. Avec trois cents tests,tests/ui/se transforme en un dossier encombré de soixante fichiers, car toute modification d’une fonctionnalité affecte tous les tests qui lui sont rattachés, quel que soit le niveau de couche. Des tests dispersés transforment l’analyse en une véritable quête. - Lorsque même les fonctionnalités deviennent surchargées. Avec des milliers de tests, un dossier de fonctionnalité peut contenir des centaines de fichiers. La solution suivante consiste à utiliser des sous-dossiers par sous-fonction ou par scénario, tels que
tests/auth/login/,tests/auth/registration/ettests/auth/password-reset/, ce qui prolonge l’idée de conserver le contexte à côté du code. De même, des centaines de tests ne devraient pas partager un seul fichier, car cela crée un document illisible et impossible à examiner. - L’hybride comme point de départ. La démonstration se divise en tests API, UI et hybrides, mais un projet réel n’est pas obligé de suivre ce schéma ni de créer trois couches pour chaque fonctionnalité. Il se peut qu’il n’y ait pas d’API indépendante digne d’être testée, ou que les tests UI soient déjà hybrides, avec des préconditions définies via l’API ou la base de données, et que seule la partie liée à l’interface soit vérifiée dans le navigateur. Cela est tout à fait légitime, car une séparation pour elle-même est une erreur. Écrire des tests UI hybrides dès le début permet d’éviter les tests UI purs qui transfèrent l’état dans le navigateur et consomment beaucoup de temps. La structure est un moyen, et chaque abstraction supplémentaire augmente les efforts de maintenance.
Quatre décisions qui expliquent le reste
Le dépôt consigne quatorze décisions architecturales dans .claude/rules/architecture.md, et chacune y répond à la question « pourquoi de cette manière » plutôt qu’à « qu’est-ce que c’est ». Quatre d’entre elles sont racontées ici, car il vaut mieux les laisser dans le dépôt plutôt que de les paraphraser.
Chaque exécution enregistre son propre utilisateur
La configuration habituelle consiste en un compte de test partagé par tous les tests. Cela reste valable tant que l’ensemble de tests ne devient pas trop volumineux et que les tests ne s’exécutent pas en parallèle. Dans ce cas, un test vide le panier au moment même où un autre vérifie s’un produit s’y trouve, ce qui provoque une erreur qui apparaît une fois sur vingt exécutions et jamais sur la machine du débogueur lui-même.
La règle semble banale : chaque exécution dispose de son propre environnement isolé. Cela diffère de la promesse d’éviter les données partagées ou de tout nettoyer par la suite. Cela signifie qu’il n’y a physiquement personne avec qui entrer en conflit. Dans la démonstration publique, un utilisateur nouveau enregistré dans global-setup suffit à accomplir la tâche. Un environnement d’entreprise peut cependant bloquer cela en imposant des limites liées aux utilisateurs, des entités partagées ou un processus de recréation des données déjà en place. Le principe reste valable, tandis que la méthode de création de l’environnement isolé constitue une question distincte.
La même logique conduit à retries: 0. Une tentative de réessai sert de simple mesure temporaire et non de garantie : elle masque en réalité une condition de concurrence critique et reporte les conséquences au moment où elles deviennent les plus graves. Un test nécessitant une réessai n’est pas instable, mais il signale une erreur d’isolation, et ce rapport mérite d’être examiné. Le coût en est clairement indiqué : dans la démonstration, l’enregistrement d’un utilisateur nécessite une seule requête qui réussit toujours, tandis que dans d’autres contextes le mécanisme différera.
Les types proviennent de la spécification, et un test de contrat vérifie cette spécification
Il s’agit de deux vérifications distinctes qui sont constamment confondues.
- La première a lieu au moment de la compilation. L’API dispose d’un document OpenAPI à partir duquel les types sont générés ; ainsi, un champ mentionné dans le code mais absent de la spécification empêche la compilation du projet. Ce processus ne prend que deux secondes et ne nécessite aucune requête vers le serveur.
- Le deuxième teste si le serveur correspond réellement à sa propre spécification. Un type généré ne peut pas le prouver, car il ne démontre que la conformité avec le document. Les tests de contrat interagissent avec l’API en production et comparent les réponses réelles au schéma. Dès la première exécution, ils ont détecté une incohérence dans le chemin : le document indiquait
products/{productId}tandis que le code utilisaitproducts/{id}. Une autre fois, ils ont montré que le filtre de catalogue appelaitby_category_slug, un paramètre qui n’apparaît absolument pas dans la spécification. Le serveur l’accepte et les tests donnent un résultat positif, donc tout semble « fonctionner ». Mais un paramètre hors du contrat ne peut pas s’éloigner de ce dernier ; il ne peut disparaître que silencieusement un jour.
Un test de contrat n’est donc pas une vérification pour savoir si une extrémité de chemin fonctionne. Il vérifie plutôt si c’est bien l’extrémité de chemin sur laquelle tout le monde s’est mis d’accord.
Un jeton valable trois cents secondes
La conception initiale prévoyait de se connecter une fois avant le démarrage, de sauvegarder le jeton dans un fichier et de permettre à chaque travailleur de le lire. C’est presque ce qui s’est passé. Mais le jeton de cette API n’est valable que trois cents secondes exactement, ce que des tests spécifiques vérifient en comparant deux champs contenus dans le jeton, et la suite de tests prend plus de temps que cela. Le test le plus lent aboutit à un jeton obsolète, entraînant une erreur 401 qui ressemble à un problème de permissions plutôt qu’à une expiration, ce qui fait que le débogage commence au mauvais endroit.
Ainsi, le fichier contient des identifiants et non des jetons. Chaque travailleur conserve son propre jeton en mémoire et le renouvelle tôt. Une conséquence en découle : il faut toujours récupérer le jeton à l’endroit précis où il est utilisé, et jamais le stocker dans une variable en haut d’un bloc. Une seule telle ligne suffit à réapparaître le problème même que la conception visait à éliminer.
Les données de référence en snapshot une seule fois, jamais l’état mutable
Les catégories, les marques et la page du produit ne changent pas entre les exécutions. Elles peuvent être récupérées une fois avant le début et transmises aux tests, de sorte que les trente tests ne demandent pas chacun la même liste. Les favoris, le panier et le profil ne doivent jamais faire partie d’un tel snapshot, car ce sont les tests eux-mêmes qui les modifient. Créer des snapshots de l’état mutable n’est pas une optimisation : cela génère des données dépassées, car un test lit ce qui était vrai il y a cinq secondes, échoue, et quelqu’un cherche alors une erreur dans une application qui n’en possède pas.
Le nettoyage suit la même logique. Il s’exécute dans l’ordre, d’abord les entités puis leurs propriétaires, et il échoue jamais ; seules les permissions insuffisantes génèrent un avertissement. Une opération de nettoyage qui échoue transforme un seul test rouge en une exécution entière marquée en rouge avec une explication erronée, le pire type de résultat négatif.
La couche d’IA
Imaginez ce qui se passe lorsque Claude Code s’ouvre dans un dossier de projet et reçoit la demande « écrire des tests pour le panier ». Le modèle dispose uniquement de cette demande. Il ne sait pas que les comptes partagés sont interdits, que les identifiants doivent provenir d’une carte vérifiée, ou que faire des assertions sur un compteur global dans une suite de tests en parallèle est un piège. Il devine, et le fait de manière assez fiable, de sorte que le code semble correct et fonctionne probablement. La plupart des gens s’arrêtent là et considèrent cela comme du travail avec l’IA.
La couche d’IA reformule le problème. La question cesse d’être ce que le modèle va deviner pour devenir ce qu’il doit savoir et ce qu’il ne pourra jamais faire. Elle se compose de cinq composants, chacun ayant sa propre tâche.
- CLAUDE.md, toujours chargé. Un fichier situé dans la racine du projet qui s’affiche au début de chaque session. Il sert de présentation pour un nouveau collaborateur, dans ce cas le modèle, présenté en deux écrans : ce que représente le projet, l’organisation des éléments, les cinq points les plus importants et ce qui est formellement interdit. Le mot « toujours » définit les limites, car chaque conversation paie pour le chargement de ce fichier dans le contexte. Seuls les éléments nécessaires à chaque fois y sont inclus. Quarante conventions de style figurent dans un fichier séparé auquel il fait référence.
- Règles, raisons et non des commandes. Ces fichiers contiennent les décisions architecturales, les conventions de codage et les procédures relatives aux navigateurs. Une instruction indique de ne pas placer le token dans une variable. Une règle explique que le token dure trois cents secondes, de sorte qu’un token placé en haut meurt au milieu de l’exécution et provoque une erreur 401 dans les tests les plus lents. Avec la raison en main, le modèle n’a pas besoin d’interdiction spécifique : il comprend la catégorie de problème et le repère dans des environnements inconnus.
- Compétences, procédures avec un mécanisme de contrôle. Une compétence est un guide pour une tâche donnée : des étapes, une description, des phrases déclencheuses, un modèle désigné et une liste d’outils autorisés. La liste des outils constitue une véritable limite et non de la documentation. La compétence chargée de rédiger des cas de test ne peut pas exécuter de commandes, donc elle ne peut pas commencer à exécuter des tests par précaution, aussi motivée soit-elle.
- agents : une entité distincte dotée de droits limités. Deux raisons pratiques justifient cette approche. D’une part, le contenu d’une page contenant tous ses attributs représente des milliers de tokens inutiles, et s’il est intégré à la conversation principale, le code masque les discussions relatives à la liste de contrôle. Un agent fonctionne dans son propre contexte et fournit un résultat, et non des données brutes. D’autre part, il existe des limites claires : l’agent de recherche n’a pas accès au code de test, de sorte que sa reconnaissance produit de la documentation plutôt que du code en tant que fait concret. L’agent chargé d’écrire des tests ne peut pas nécessairement exécuter l’ensemble de ces tests, ce qui explique pourquoi l’écriture peut se faire sans exécution réelle.
- hooks/ : appliqués de force sans être mémorisés. Une règle en prose reste en vigueur tant que quelqu’un s’en souvient, c’est pourquoi les bannissements les plus coûteux deviennent des hooks : de petits scripts qui s’exécutent avant une action et peuvent la bloquer. Les types générés ainsi que les fichiers
.envne peuvent pas être modifiés. Les commandesgit push --force,--no-verifyet--amendsont rejetées automatiquement. Un hook ne persuade ni ne donne d’explications par la suite. L’action ne a pas lieu, et la réponse indique la raison ainsi que la procédure appropriée.
En une phrase chacun : CLAUDE.md est la lecture obligatoire dès le premier jour pour un nouveau membre, les règles constituent les normes de l’équipe, les compétences sont les guides de procédure, les agents sont des stagiaires ayant un accès limité, et les hooks sont les systèmes de contrôle qui restent fermés.
Le même principe sur un ticket en cours
Les abstractions n’ont de sens que lorsque une tâche réelle passe par elles. Prenons PROJ-431, « payer une commande par carte ». L’ordre normal des opérations n’a rien à voir avec l’IA : quelqu’un teste la fonction manuellement, quelqu’un rédige une liste de contrôle, quelqu’un la transforme en cas d’usage, et quelqu’un les automatise. Une seule personne peut effectuer les quatre tâches ou trois personnes peuvent se les répartir. Pour le pipeline, cela n’a pas d’importance, c’est justement le but, car le pipeline s’insère dans le processus existant sans le remplacer. Le numéro de la tâche est le fil conducteur de tout, préféré aux noms de fonctionnalités ou de branches, car il survit aux renommages et aux réaffectations.
Étape un : la liste de contrôle
L’ingénieur QA démarre la compétence et lui transmet le numéro. Avec MCP connecté à Jira, la compétence lit directement le ticket, y compris la description, les critères d’acceptation et, souvent ce qui est le plus utile, les commentaires dans lesquels un développeur a expliqué la moitié du fonctionnement sans mettre à jour la description. En l’absence de MCP ou avec un ticket vide, l’ingénieur décrit la fonctionnalité dans la demande. Le pipeline a besoin d’une entrée et se fiche de son origine.
Alors commence l’étape pour laquelle tout a été conçu. La compétence en question ne tire pas une liste de contrôle à partir du texte du ticket ; elle fait appel à l’agent de recherche, qui se connecte à l’application, que ce soit dans un environnement local ou de test, visite les pages concernées, clique sur les éléments, lit le DOM en temps réel, appelle l’API par lui-même et compare les réponses aux spécifications. Ce n’est qu’après cela que la liste de contrôle est rédigée, en se basant sur ce qui existe réellement et non sur ce que promettait le ticket. Les critères d’acceptation ne sont que des indications, pas la vérité, et la moitié des écarts entre eux et la réalité constituent les premiers bugs de la tâche, découverts avant même que du code ne soit écrit.
Deux fichiers sont générés, et le fait de les garder séparés est la leçon la plus importante ici. Le premier est la liste de contrôle : des scénarios métier priorisés dans le format de l’équipe, et non des vérifications d’éléments. Un bord rouge autour d’un champ ne constitue pas un scénario ; en revanche, un utilisateur dont la carte appartient à quelqu’un d’autre et qui est arrêté, avec indication des raisons, en constitue un. Le second fichier est la carte des fonctionnalités, qui contient des localisateurs vérifiés, des champs API, des règles de validation ainsi que les problèmes rencontrés au cours du processus. La liste de contrôle représente le résultat direct de la tâche, tandis que la carte sert de référence pour les étapes ultérieures. L’ingénieur QA lit la liste de contrôle, supprime ce qui est inutile, ajoute ce que seul une personne connaît, et cette revue constitue une étape de contrôle : rien ne peut avancer sans approbation.
Étape deux : cas de test
Quelle que soit la personne qui a effectué l’étape un, elle peut continuer, ou une autre personne si les tests manuels et l’automatisation sont séparés. Une deuxième compétence commence avec le même numéro de tâche et localise elle-même la liste de contrôle et la carte. L’ingénieur choisit ce qu’il faut couvrir, tout ou seulement ce qui a été convenu. Cette compétence transforme ces éléments en cas d’essai et règle une seule chose par cas : le niveau auquel appartient la vérification.
- Les règles appliquées par le serveur deviennent des tests API.
- Les éléments visibles par l’utilisateur deviennent des tests UI.
- Une vérification nécessitant quinze clics de configuration pour une seule assertion devient un test hybride, avec un état construit via l’API et seule la partie visible étant vérifiée visuellement.
Les erreurs ici coûtent plus cher que des cas mal rédigés, car elles génèrent une couverture lente et chevauchante que personne n’ose supprimer par la suite. Cette compétence ajoute également une section de service à chaque cas : les données à utiliser, les localisateurs, ce qui doit être unique, ce qui est nettoyé et sous quel compte. Pour un humain, cela ressemble à de la paperasse. Pour l’étape suivante, c’est une spécification prête à l’emploi. L’ingénieur la revue et l’approuve, ce qui constitue la deuxième étape de validation. Les équipes sur Qase ou TestRail peuvent y ajouter une compétence qui télécharge les cas approuvés via API et les marque ensuite comme automatisés.
Étape trois : automatisation
La compétence d’automatisation prend également le numéro de la tâche et récupère la liste de contrôle, les cas ainsi que la carte elle-même. Son premier geste est inattendu : elle vérifie à quel point la carte est à jour. Chaque section porte une date de vérification, et tout ce qui est plus ancien d’un mois cesse d’être considéré comme un fait pour devenir une affirmation. Cette compétence ne refuse ni ne fait confiance aveuglément. Elle déplace les éléments obsolètes dans une section dédiée aux risques du plan et laisse à un humain le soin de décider s’il faut relancer la reconnaissance ou accepter le risque. Un localisateur obsolète apparaîtra malgré tout, mais trois étapes plus tard, en tant que délai d’expiration dont la cause reste inconnue.
Vient ensuite le plan : quels fichiers apparaissent, quels données utilise chaque test, ce qui est créé avant l’exécution et supprimé après, quels méthodes de page et de client sont ajoutés et quelles questions restent en suspens. Le plan constitue le troisième et plus important obstacle, car il détermine si le lot peut être exécuté en parallèle sans risque, et le modifier ultérieurement est coûteux.
Une fois approuvé, le plan est transmis à l’agent chargé de l’écriture du code, dont la caractéristique principale est de ne pas inventer de locuteurs. S’il manque un locuteur nécessaire dans la carte, il ne suppose pas en un similaire ni ne cache cette lacune en sautant le test. Il laisse une note dans le code indiquant que personne n’a vérifié le locuteur et en signale l’absence. Le test est néanmoins écrit et échoue, ce qui est normal, car le problème reste visible et n’est pas résolu.
Ce qui était auparavant des commandes distinctes se trouve désormais à l’intérieur de cette étape : exécuter, lire l’erreur, corriger. Chaque test dispose de deux tentatives, puis l’agent s’arrête et admet sa défaite. Une exécution séquentielle vérifie la logique, tandis que deux exécutions parallèles vérifient l’isolation. Aucun bouton supplémentaire n’est nécessaire, car rien de tout cela ne requiert une décision humaine. Il s’agit d’une procédure inscrite dans les règles.
Les interdictions ici sont spécifiques et ne font l’objet d’aucun débat :
- Ne désactivez pas un test qui échoue pour faire apparaître le résultat en vert.
- Ne augmentez pas le nombre de tentatives.
- Ne affaiblissez pas discrètement une vérification pour qu’elle corresponde au comportement observé.
Si l’application se comporte de manière incorrecte, un résultat rouge pour le test est le bon résultat.
Étape quatre : revue et validation
La revue et l’envoi en commit relèvent de deux compétences distinctes et non d’une seule, une séparation délibérée, car une étape entamée avec l’intention d’envoyer en commit a tendance à trouver plus facilement des raisons de ne pas le faire. La revue intègre des connaissances spécifiques au projet que n’a pas un revueur généraliste : ce qu’une entité partagée signifie dans ce contexte, ce qu’un token en haut d’un bloc met en péril, pourquoi s’appuyer sur un compteur global est trompeur. Chaque entrée de cette liste provient d’une défaillance antérieure de ce processus. Afin d’empêcher que cette séparation ne devienne une simple formalité, la revue appose une empreinte digitale du diff qu’elle a examiné, et l’envoi en commit refuse de se poursuivre si le diff a changé depuis. Ce n’est pas un verrouillage. C’est une question à réponse précise : ce code a-t-il été revu, ou s’agit-il d’un code différent ? La différence entre un processus et une habitude réside précisément là.
Lorsque toute la chaîne de traitement est terminée, PROJ-431 contient l’ensemble des informations : ce qui a été étudié, ce qui a été choisi pour vérification, ce qui est géré automatiquement, ce qui reste manuel et ce qui ne sera pas automatisé ainsi que les raisons de cela. La raison n’est pas un amour de la documentation. Six mois plus tard, la question de savoir pourquoi un scénario n’a pas été pris en compte sera posée par une personne présente lors d’une rétrospective, et une réponse doit donc exister.
Ce qui survit à la tâche
Le principal risque lié à la configuration n’est pas un code mal généré. C’est plutôt le fait que le contexte se dégrade en un ensemble chaotique après six mois, sans que personne ne sache quel fichier faire confiance. Ainsi, les artefacts ont trois durées de vie distinctes, et il est interdit de les mélanger.
- Les règles restent en vigueur aussi longtemps que le projet et ne changent que parallèlement au code.
- La carte des fonctionnalités reste valable pendant des années et appartient à la fonctionnalité elle-même et non au ticket. Elle est enrichie de sections datées, de nouvelles pages ainsi que de nouveaux problèmes potentiels, sans jamais être reconstruite pour chaque tâche.
- Les listes de contrôle, les cas d’usage et les plans appartiennent à une tâche donnée et deviennent de l’histoire une fois celle-ci clôturée.
Sans cette séparation, un an plus tard, la carte de localisation existe en dix exemplaires dispersés parmi les numéros de tâches, la moitié étant incorrecte sans moyen de savoir laquelle. Lorsque la carte d’une fonctionnalité s’agrandit, comme c’est le cas pour les grandes fonctionnalités, la solution est similaire à celle utilisée pour le code : il faut la diviser en parties le long de séparations naturelles telles que la page, la sous-fonctionnalité ou le scénario, et conserver un index succinct indiquant ce qui se trouve où. Une règle stricte s’applique également : seuls les faits vérifiés entrent dans la carte, c’est-à-dire ce que l’agent a réellement vu dans l’application en temps réel, accompagné d’une date. Les suppositions et les récits basés sur des tickets ne sont pas autorisés. Une carte qui les accepte est inutilisable au cours d’un sprint, et il n’est pas possible par la suite de distinguer les faits des suppositions.
Aucune de ces méthodes n’est une recette universelle. Quatre étapes du pipeline, cinq composants de la couche d’IA, des portes de contrôle et des durées de vie des artefacts constituent un arrangement possible. Les équipes diffèrent en nombre de membres, en produits et en outils, ce qui fait que la moitié des détails s’adapteront différemment. Certaines approches fonctionneront, d’autres nécessiteront des ajustements et d’autres encore ne fonctionneront pas, et c’est acceptable.
Le point central reste inchangé : il faut construire correctement la couche d’IA afin que le modèle suive les règles de l’équipe, reste dans les limites définies par celle-ci et utilise ses artefacts. Les tâches routinières doivent être transférées à d’autres : lire les demandes, rédiger des cas similaires, sélectionner des couches, rechercher des emplacements, relancer les processus en cas d’échec et les corriger, examiner à nouveau les mêmes erreurs pour la centième fois. Les ingénieurs en QA ne sont pas remplacés et le processus existant n’est pas annulé. Le temps consacré aux tâches qui peuvent être transférées diminue simplement.
Les seniors sont-ils encore nécessaires ?
Les ingénieurs seniors sont plus nécessaires que jamais, mais avec un rôle modifié. David Heinemeier Hansson a observé que la meilleure accélération grâce aux agents qu’il ait vue provenait des personnes les plus expérimentées, celles capables de juger si les résultats fournis par les agents étaient aptes à être utilisés.
Auparavant, les ingénieurs étaient appréciés pour leur capacité à écrire du code, à maîtriser la syntaxe et à exécuter les tâches rapidement, ce qui était logique lorsque le code était le principal produit. Avec l’IA, cette valeur a changé d’un ordre de grandeur, et non de quelques pourcents à peine. La machine écrit le code, tandis que l’être humain décide de ce qui doit être écrit, pourquoi, dans quels limites et comment vérifier que le résultat n’est pas erroné.
Imaginez deux spécialistes. Le premier ouvre Claude Code et demande la création d’un test de connexion. Ce dernier est écrit et réussit, ce qui donne au spécialiste l’impression d’être productif, ce qui est vrai aujourd’hui. Un mois plus tard, le test cesse de fonctionner, la cause reste inconnue, des correctifs sont appliqués au hasard tandis que les tokens s’épuisent, et finalement tout est réécrit ou le test est supprimé et oublié.
Le second spécialiste conçoit d’abord : comment les tests sont isolés, quel modèle suit le client API, comment les tokens sont gérés et ce qu’il advient de l’état qui change entre les tests. Ce n’est qu’ensuite que l’instruction est transmise au modèle : écrire un test de connexion selon ces règles.
Le premier contient un test qui fonctionne actuellement. Le second contient un système qui continue de fonctionner, car il repose sur une architecture et non sur le hasard. Sur un petit ensemble de tests, l’écart est imperceptible, à ceci près que le premier termine dix ou quinze tests en dix minutes, tandis que le second nécessite deux heures ou plus pour les mêmes tests. Après cent, deux cents ou mille tests, ainsi que cinquante à cent exécutions du pipeline, l’écart devient évident.
Cela signifie-t-il qu’il faut apprendre un langage de programmation ?
Oui, mais pas pour écrire du code. Une langue est nécessaire pour comprendre ce que l’IA a produit, pour détecter les erreurs dans le code généré, pour examiner les demandes de fusion et pour décider ce qui atteint la branche principale. La syntaxe est un outil. Une structure complexe issue de la mémoire est hors de portée pour un testeur dans un domaine inconnu, mais l’architecture constitue un terrain familier : à quoi doit ressembler le système, comment les tests restent isolés, comment les pipelines sont organisés et quels éléments doivent figurer dans un bon rapport. C’est cette base qui a rendu possible le projet Playwright.
L’architecture ne naît pas d’une instruction. L’IA peut la proposer, parfois bien, mais l’architecture est un choix parmi des solutions et non simplement une solution. Un modèle propose cinq options qui semblent toutes raisonnables. Celle qui pourra être mise en œuvre en six mois, avec dix fois plus de tests et des exigences modifiées, n’est pas quelque chose qui se génère automatiquement. Elle provient de l’expérience, des leçons apprises en corrigeant des tests défaillants, en comprenant pourquoi un test passe localement mais échoue en environnement de CI, et en découvrant qu’un compte partagé représente un couplage caché prêt à exploser.
Les entreprises n’ont pas besoin de personnes qui se contentent d’appuyer sur des boutons et d’écrire des instructions dans Claude Code ou Cursor. De telles personnes sont nombreuses et leur nombre ne fera qu’augmenter ; le marché est en outre rempli de développeurs qui croient avoir trouvé la pépite, mais dont cette illusion ne dure que peu de temps. Ce qui est recherché, c’est des personnes qui comprennent pourquoi l’IA a agi d’une certaine manière, qui détectent ses erreurs, qui formulent des tâches de façon à ce que le modèle fournisse une solution fonctionnelle plutôt qu’une erreur soi-disant « corrigée », et qui construisent des systèmes plutôt que de continuer à fournir des tokens dans des cycles interminables de « réessayez, ce n’est pas ça ».
L’expérience en matière de traitement, d’automatisation et de pipelines n’est pas un simple souvenir. C’est le fondement d’un travail efficace en intelligence artificielle et ce qui distingue celui qui crée le chaos de celui qui construit un système. Autrefois, les connaissances servaient à effectuer des tâches manuellement. Aujourd’hui, elles servent à définir des tâches pour l’IA et à vérifier les résultats. Le rôle a changé, mais la valeur de l’expertise reste la même. Il est en effet plus facile de le voir, car à côté de l’IA, il devient clair qui se contente de générer et qui comprend ce qui a été généré et pourquoi.
Ce que montre l’expérience
Un cadre a été développé dans un langage que son créateur ne maîtrise pas, et il ne s’agit pas d’une histoire de génie. C’est plutôt l’histoire de celui qui refuse de deviner ce que ferait l’IA et préfère consacrer du temps à expliquer les règles du jeu : ce qui est autorisé, ce qui ne l’est pas, et pourquoi de cette manière et non d’une autre. C’est ennuyeux, cela n’a rien à voir avec l’ambiance, et il faut du temps avant que le premier résultat n’apparaisse. C’est précisément cela qui fonctionne.
Ce dépôt ne prouve pas que les ingénieurs sont obsolètes ou que les langages ont perdu de leur importance. C’est un test en temps réel de la manière dont le rôle de l’ingénieur évolue lorsque la valeur se déplace de l’écriture de code à la définition des tâches, à la conception des systèmes et à la vérification des résultats. Écrire du code à la main reste une compétence. Une deuxième compétence s’y est ajoutée et s’est avérée tout aussi importante : comprendre ce qui a été généré, pourquoi, et ce qui viendra ensuite.
L’IA ne remplace pas l’ingénieur. Elle devient le junior le plus rapide et le plus obéissant qui soit, inlassable, ne discutant jamais et toujours prêt à tenter à nouveau. Elle a un défaut : elle propose une solution correcte ainsi que du pur non-sens avec la même assurance, et seuls ceux qui comprennent le travail peuvent les distinguer. Par conséquent, l’ingénieur doit rester aux commandes en tant que personne connaissant la destination et le chemin, et non simplement en tant que celui qui tient le volant.