Image: AI generated
Si vous voulez refactoriser du code legacy avec l’IA mais qu’il n’y a pas de tests, si votre LLM ecrit des tests mais s’arrete a mi-chemin, si vous voulez suivre mecaniquement la couverture tout en controlant l’agent – tsma construit cette ligne de defense.
Comment refactoriser du code sans tests ?
Vous heritez d’un projet legacy de 100 000 lignes. Pas de tests. Vous voulez refactoriser, mais impossible de savoir ce qui va casser. Pour ecrire des tests, il faut comprendre le code. Pour comprendre le code, il faut de la documentation – qui n’existe pas non plus.
Personne n’y touche. Le code continue de pourrir.
Tous les systemes legacy du monde sont pris dans cette impasse. Les entreprises du Fortune 500 consacrent 60 a 80 % de leur budget IT a la maintenance du legacy. 42 % du temps des developpeurs est absorbe par la dette technique.
Et si un LLM pouvait ecrire les tests a votre place ?
Les problemes quand un LLM ecrit les tests
Demandez a un LLM “ecris un test pour cette fonction” et il produira quelque chose. Mais trois problemes se posent.
Premier probleme : on ne sait pas par ou commencer. Quand il y a 527 fonctions, on commence par la premiere ? Par la plus importante ? Il n’y a aucun critere.
Deuxieme probleme : impossible de verifier la qualite des tests. Le test ecrit par le LLM passe. Mais verifie-t-il reellement le comportement de la fonction, ou n’est-ce qu’une coquille vide qui appelle la fonction sans aucun assert ? Il faut relire chaque test manuellement pour le savoir.
Troisieme probleme : sans feedback, les tests du LLM plafonnent a 60-70 %. Selon l’etude empirique de Schafer et al. (2023), la mediane des tests generes par LLM est de 70.2% en statement coverage et 52.8% en branch coverage. L’instruction “teste cette fonction” ne suffit pas pour atteindre 100 % de branch coverage. Il faut indiquer quelles branches manquent pour que le LLM complete le reste.
Le LLM n’est pas incapable d’ecrire des tests. Ce qui manque, c’est une structure qui lui indique quoi tester et a quel point il a bien teste.
tsma : un rail de test pilote par une seule commande
tsma est un outil CLI qui indexe toutes les fonctions du projet, detecte la presence de tests, mesure le coverage et fournit un feedback precis a l’agent LLM.
L’agent n’a besoin de connaitre qu’une seule commande :
$ tsma next
Cette unique commande pilote l’integralite de la boucle :
$ tsma next # Affiche la prochaine fonction sans test
→ On ecrit le test
$ tsma next # Detecte le nouveau test, l'execute, mesure le coverage
→ 100 % ? PASS, on passe a la suivante
→ < 100 % ? Affiche les branches non couvertes avec les numeros de ligne
$ tsma next # Re-mesure le test corrige
→ Ameliore ou non, marque DONE et passe a la suite
On repete jusqu’a ce que “All functions complete!” apparaisse.
Valide sur 527 fonctions
tsma a ete applique a un vrai projet Go (527 fonctions).
| Resultat | Nombre | Proportion |
|---|---|---|
| PASS (100 % branch coverage) | 246 | 46.7 % |
| DONE (best-effort) | 281 | 53.3 % |
| TODO (non traite) | 0 | 0 % |
246 fonctions ont atteint 100 % de branch coverage. Les 281 restantes n’y sont pas parvenues, mais ont ete testees autant que possible.
Pourquoi certaines fonctions n’atteignent-elles pas 100 % ?
Fonctions qui atteignent 100 % et celles qui n’y arrivent pas
La capacite d’une fonction a atteindre 100 % de branch coverage depend de la maniere dont elle recoit ses dependances.
Interface (mockable) – 100 % atteignable :
type Handler struct {
svc AuthSvc // interface -- remplacable par un mock
}
En test, l’injection d’un mock permet de controler tous les chemins :
svc := mocks.NewMockAuthSvc(ctrl)
svc.EXPECT().Login(...).Return(result, nil) // chemin de succes
svc.EXPECT().Login(...).Return(nil, err) // chemin d'erreur
Type concret (not mockable) – 100 % impossible :
type Handler struct {
svc *service.SMSImportService // pointeur de struct -- non remplacable
}
L’implementation reelle embarque des dependances internes (BDD, API externes, etc.). Impossible de provoquer une erreur specifique ou de forcer un resultat particulier. Les branches qui en dependent sont inaccessibles en test unitaire.
Reponse de tsma : apres le feedback sur les branches non couvertes, une seconde tentative est accordee. Si la branche reste inaccessible, la fonction est marquee DONE. Ce n’est pas une limite de l’outil, mais le reflet de la testabilite du code. Le dilemme du code legacy systematise par Feathers (2004) – “pour changer le code, il faut des tests ; pour ajouter des tests, il faut changer le code” – se resout en cassant les dependances et en introduisant des interfaces (DI). L’introduction d’interfaces rendrait les 100 % possibles, mais cela implique de modifier le code source.
Le feedback transforme radicalement les tests du LLM
La valeur centrale de tsma n’est ni l’indexation ni la mesure du coverage. C’est l’indication precise des branches non couvertes avec leurs numeros de ligne.
Sans feedback :
"Ecris un test pour la fonction ListContracts"
→ Le LLM ne teste que le happy path
→ Coverage 60-70 %
Avec feedback :
"Ecris un test pour la fonction ListContracts"
→ Coverage 65 % (11/17)
→ UNCOVERED:
line 41 -- if params.Status != nil
line 44 -- if params.BuildingId != nil
line 70 -- if err != nil (CountSummary)
→ Le LLM ajoute des tests couvrant exactement ces branches
→ Coverage 100 %
C’est le meme LLM. La seule difference est la presence du feedback. Trois lignes avec des numeros de ligne separent 60 % de 100 %. CoverUp (Pizzorno & Berger, 2024) a demontre empiriquement le meme principe. En inserant de facon repetee les resultats d’analyse de coverage dans les prompts et en focalisant l’attention du LLM sur les lignes non couvertes, ils ont atteint une mediane de line coverage par module de 81 % – une amelioration de 19pp par rapport au baseline sans feedback.
L’agent tombe – la progression est preservee
Les agents LLM plantent. Limite de tokens, erreur reseau, deconnexion de session. Impossible de traiter 527 fonctions en une seule session.
tsma persiste la progression dans .tsma/session.json.
$ tsma status
527 functions
PASS: 246 (46.7%)
DONE: 281 (53.3%)
TODO: 0 (0.0%)
L’agent plante a la 200e fonction ? Un nouvel agent lance tsma next et reprend a la 201e. session.json est le checkpoint.
Plusieurs agents peuvent travailler en relais sans conflit. L’atomicite est au niveau de la fonction.
La session est un cache, le code source est la verite
Un des principes de conception de tsma : la session est un cache, le source of truth est le code source.
Si l’on supprime un fichier de test, la fonction revient a TODO meme si session.json indique PASS. La session ne diverge pas de la realite.
Principe :
Session.json dit "PASS"
Mais le fichier de test n'existe plus → TODO
Le fichier source a change → a re-mesurer
Instructions pour l’agent LLM
L’agent n’a besoin que de 6 lignes d’instructions :
1. Executer tsma next
2. TODO -- lire la fonction et ecrire le test
3. Echec du test -- lire l'erreur et corriger le test
4. Branches non couvertes affichees -- ajouter des tests pour ces branches
5. PASS/DONE -- la fonction suivante s'affiche automatiquement
6. Repeter jusqu'a "All functions complete!"
L’agent n’a besoin de connaitre qu’une commande : tsma next. Le reste est contraint par le CLI.
Le train et les rails
Le vibe coding, c’est un train. Rapide. Mais sans rails, il deraille.
Tous les outils d’AI coding se concentrent sur rendre le train plus rapide. Un modele plus gros, un agent plus intelligent, un meilleur prompt. Mais plus le train accelere, plus le deraillement est devastateur.
tsma, ce sont les rails. Le LLM genere les tests (Neural), le CLI definit les limites (Symbolic Constraint). La creativite du LLM reste intacte, mais la qualite du resultat est imposee par la machine.
| Avant | tsma | |
|---|---|---|
| Ecriture des tests | Humain (lent) ou LLM (chaotique) | LLM ecrit, CLI verifie |
| Par ou commencer ? | Decision humaine | CLI determine l’ordre |
| Verification qualite | Revue humaine | CLI mesure le coverage |
| Feedback | Aucun | Numeros de ligne des branches non couvertes |
| Suivi de progression | Aucun | session.json automatique |
Le LLM genere librement. Mais uniquement sur les rails de tsma next.
Langages supportes
| Langage | Indexeur | Test Runner | Coverage |
|---|---|---|---|
| Go | go/ast | go test | go test -coverprofile |
| TypeScript | regex | npx vitest / npx jest | c8 / istanbul |
| Python | regex | pytest | coverage.py |
Go utilise un parseur AST pour une extraction precise des fonctions. TypeScript et Python reposent sur des expressions regulieres.
Les fichiers generes (*_gen.go, *.pb.go), les fichiers de test et les repertoires vendor/node_modules sont automatiquement exclus de l’indexation.
Installation et execution
make install
cd your-legacy-project
tsma next
C’est tout.
MIT License. github.com/park-jun-woo/tsma
References
- Schafer, M., Nadi, S., Eghbali, A., & Tip, F. (2023). An Empirical Evaluation of Using Large Language Models for Automated Unit Test Generation. IEEE Transactions on Software Engineering, 50(1), 85–105. arXiv:2302.06527
- Pizzorno, J. A., & Berger, E. D. (2024). CoverUp: Coverage-Guided LLM-Based Test Generation. arXiv preprint arXiv:2403.16218. arXiv:2403.16218
- Ryan, G., Jain, S., Shang, M., Wang, S., Ma, X., Ramanathan, M. K., & Ray, B. (2024). Code-Aware Prompting: A Study of Coverage-Guided Test Generation in Regression Setting using LLM. Proceedings of the ACM on Software Engineering (FSE 2024), 1(FSE), 951–971. ACM DL
- Lemieux, C., Inala, J. P., Lahiri, S. K., & Sen, S. (2023). CodaMOSA: Escaping Coverage Plateaus in Test Generation with Pre-trained Large Language Models. ICSE 2023, 951–963. ACM DL
- Feathers, M. C. (2004). Working Effectively with Legacy Code. Prentice Hall. ACM DL
- Besker, T., Martini, A., & Bosch, J. (2018). Technical Debt Cripples Software Developer Productivity. TechDebt 2018, 105–114. ACM DL
- Stripe. (2018). The Developer Coefficient. PDF
- U.S. Government Accountability Office. (2019). Information Technology: Agencies Need to Develop Modernization Plans for Critical Legacy Systems. GAO-19-471. GAO
- Tornhill, A., & Borg, M. (2022). Code Red: The Business Impact of Code Quality. TechDebt 2022, 11–20. arXiv:2203.04374
- Peng, S., Kalliamvakou, E., Cihon, P., & Demirer, M. (2023). The Impact of AI on Developer Productivity: Evidence from GitHub Copilot. arXiv:2302.06590
Lie : Ratchet Pattern – Comment faire qu’un agent finisse le travail – Le pattern derriere tsma. Pourquoi la verification mecanique l’emporte sur le jugement du LLM.
Lie : Le QI du modele compte moins que la topologie du feedback – Pourquoi la structure du feedback determine les resultats plus que la performance du modele.
Journal des modifications
- 2026-05-14: Version initiale