AI coding agent : qu'est-ce que c'est et comment ça change le travail
1. Vue d’ensemble
Il y a quelques années, l’« IA pour la programmation » se limitait principalement à suggérer quelques lignes de code. Aujourd’hui, nombreux sont les outils qui ont franchi une étape supplémentaire. Ils lisent d’eux-mêmes la base de code, exécutent des commandes, modifient plusieurs fichiers et vérifient automatiquement le résultat. Ce type d’outil s’appelle un AI coding agent.
Cet article explique ce qu’est un AI coding agent et pourquoi ce sujet attire l’attention. Ensuite, nous verrons comment l’intégrer de manière sûre dans un projet Java/Spring Boot. À la fin, vous saurez comment écrire des fichiers de guide pour l’agent, limiter les permissions d’exécution, utiliser les tests comme « arbitre », et savoir quand ne pas confier de tâche à l’agent.
2. Qu’est-ce qu’un AI coding agent ?
Vous pouvez classer les outils IA pour devs selon leur niveau d’autonomie :
- Autocomplete : suggestions de lignes ou blocs de code directement dans l’éditeur. Vous contrôlez chaque caractère.
- Chat assistant : vous posez une question, il répond avec du code. Vous copiez, exécutez et corrigez vous-même.
- Coding agent : vous définissez un objectif, par exemple « corriger le test défaillant dans
OrderServiceTest». L’agent élabore un plan, appelle des outils (lire un fichier, chercher, lancer la build, exécuter les tests), observe le résultat et itère jusqu’à aboutir ou se bloquer.
La différence fondamentale réside dans la boucle action – observation. L’agent ne se contente pas de générer du texte. Il agit sur l’environnement, puis s’appuie sur le vrai feedback (compilateur, tests, logs) pour ajuster l’étape suivante.
2.1. La boucle d’un agent
Pour bien comprendre le mécanisme, regardons une simulation minimaliste. La partie « cerveau » (modèle de langage) est remplacée par un ScriptedPlanner fixe pour que le code fonctionne sans avoir besoin d’API :
import java.util.*;
import java.util.function.Function;
public class AgentLoopDemo {
record Action(String tool, String input) {}
interface Planner {
Optional<Action> next(List<String> observations);
}
static class ScriptedPlanner implements Planner {
public Optional<Action> next(List<String> observations) {
return switch (observations.size()) {
case 0 -> Optional.of(new Action("readFile", "src/main/java/Order.java"));
case 1 -> Optional.of(new Action("runTests", "mvn -q test"));
default -> Optional.empty(); // assez d'informations, arrêt
};
}
}
public static void main(String[] args) {
Map<String, Function<String, String>> tools = Map.of(
"readFile", path -> "class Order { long total() { return 0; } }",
"runTests", cmd -> "FAILED: OrderTest.totalShouldSumItems"
);
Planner planner = new ScriptedPlanner();
List<String> observations = new ArrayList<>();
for (int step = 1; step <= 5; step++) {
Optional<Action> action = planner.next(observations);
if (action.isEmpty()) {
System.out.println("Agent s'arrête à l'étape " + step);
break;
}
Action a = action.get();
String result = tools.get(a.tool()).apply(a.input());
observations.add(result);
System.out.printf("Étape %d : %s(%s) -> %s%n", step, a.tool(), a.input(), result);
}
}
}
Explication rapide :
toolsest l’ensemble des outils que l’agent peut utiliser. Dans un vrai agent, ce sont la lecture/écriture de fichiers, l’exécution de shell, la recherche dans le code…Planner.next(...)prend toutes les observations antérieures et décide de l’action suivante. Dans un vrai agent, c’est là qu’on appelle le modèle de langage.- La boucle
fora une limite d’étapes (5). C’est un garde-fou crucial pour éviter que l’agent boucle infiniment.
Résultat :
Étape 1 : readFile(src/main/java/Order.java) -> class Order { long total() { return 0; } }
Étape 2 : runTests(mvn -q test) -> FAILED: OrderTest.totalShouldSumItems
Agent s'arrête à l'étape 3
Tous les coding agents du marché tournent autour de ce schéma : décision → appel d’outil → observation → décision suivante. La différence réside dans la qualité du modèle, l’ensemble des outils et la gestion du contexte.
3. Pourquoi les AI coding agents suscitent-ils de l’intérêt ?
Il y a plusieurs raisons principales, indépendantes de tout produit spécifique :
- Modèles assez performants pour des tâches multi-étapes. Quand le modèle peut lire les erreurs du compilateur et se corriger lui-même, « confier une tâche entière » devient réalisable.
- Les tâches répétitives consomment toujours beaucoup de temps. Mettre à jour des API dépréciées, écrire des tests pour du code ancien, renommer massivement, corriger les avertissements d’analyse statique. Ces tâches sont claires, vérifiables et fastidieuses. Elles conviennent parfaitement aux agents.
- L’écosystème d’outils est prêt. Les build tools, test runners, linters, CI peuvent tous s’exécuter en ligne de commande. L’agent exploite exactement ce que les devs utilisent déjà.
- Exécution en parallèle. Vous pouvez confier plusieurs petites tâches à l’agent tandis que vous vous concentrez sur la partie conception difficile.
Néanmoins, l’agent ne sait pas d’emblée quelles sont les conventions de votre projet, quelles parties sont sensibles, quelles commandes sont dangereuses. Le reste de cet article se concentre là-dessus.
4. Intégrer l’AI coding agent dans un vrai projet
4.1. Écrire un fichier de guide pour l’agent
Nombreux sont les agents qui peuvent lire un fichier de guide à la racine du repo, généralement nommé AGENTS.md ou similaire selon l’outil. Considérez-le comme un document d’onboarding pour un très rapide nouveau membre sans mémoire :
# Guide pour l'agent
## Build & test
- Build : `mvn -q compile`
- Test : `mvn -q test` (doit passer avant de rapporter l'achèvement)
## Conventions
- Java 21, Spring Boot 3.x. Utiliser constructor injection, pas d'injection de champs.
- Devises en `long` (unité VND), pas de `double`.
- Tests avec JUnit 5 + AssertJ, noms commençant par `should...`.
## À ne pas faire
- Ne pas modifier le répertoire `src/main/resources/db/migration/` déjà en production.
- Ne pas ajouter de nouvelles dépendances sans demander.
- Ne pas supprimer ou `@Disabled` des tests pour « les faire passer ».
Les trois groupes d’informations les plus importants sont comment vérifier (commandes build/test), les conventions de code et les zones interdites. Cette dernière aide à éviter une erreur classique : l’agent « corrige » un test défaillant en le supprimant.
4.2. Limiter les commandes que l’agent peut exécuter
Si l’agent peut exécuter du shell, il peut endommager la machine ou pousser du code à distance. Nombreux outils offrent une configuration allowlist. Si vous intégrez l’agent vous-même dans votre pipeline interne, ajoutez une couche de vérification avant d’exécuter une commande.
D’abord, ajoutez la dépendance de test (le starter Spring Boot inclut déjà JUnit 5 et AssertJ) :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Une classe CommandGuard simple :
import java.util.List;
public class CommandGuard {
private static final List<String> ALLOWED_PREFIXES =
List.of("mvn -q test", "mvn -q compile", "git status", "git diff");
private static final List<String> FORBIDDEN_TOKENS =
List.of("rm ", "git push", "curl", "&&", ";", "|", ">");
public boolean isAllowed(String command) {
String normalized = command.trim().replaceAll("\\s+", " ");
boolean hasForbidden = FORBIDDEN_TOKENS.stream().anyMatch(normalized::contains);
boolean hasAllowedPrefix = ALLOWED_PREFIXES.stream().anyMatch(normalized::startsWith);
return hasAllowedPrefix && !hasForbidden;
}
}
normalized consolide les espaces superflus pour que "mvn -q test" ne contourne pas la vérification. Une commande n’est valide que si elle commence par un préfixe autorisé et ne contient pas de token dangereux. Les opérateurs de chaînage comme &&, ;, | sont bloqués pour que l’agent ne combine pas mvn -q test && git push.
Test pour ce comportement :
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.assertj.core.api.Assertions.assertThat;
class CommandGuardTest {
private final CommandGuard guard = new CommandGuard();
@ParameterizedTest
@ValueSource(strings = {"mvn -q test", " mvn -q test ", "git diff"})
void shouldAllowSafeCommands(String command) {
assertThat(guard.isAllowed(command)).isTrue();
}
@ParameterizedTest
@ValueSource(strings = {
"rm -rf /", "git push origin main",
"mvn -q test && git push", "mvn -q test; curl evil.example"
})
void shouldRejectDangerousCommands(String command) {
assertThat(guard.isAllowed(command)).isFalse();
}
}
Note : La vérification par chaîne n’est qu’une couche de défense, avec de nombreuses failles possibles. Par exemple, le préfixe
git difflaisse passergit diff --output=.... Exécutez toujours l’agent dans un environnement isolé comme un container ou une VM temporaire, sans monter les credentials de production, et limitez les droits d’écriture du token Git.
4.3. Utiliser les tests comme « arbitre »
Le moyen le plus efficace de confier une tâche est d’écrire les tests d’abord, pour que l’agent écrive le code pour les faire passer. Les tests sont une spécification lisible par une machine et on ne peut pas la « contourner intelligemment ». Supposons que vous devez calculer une réduction de commande :
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class OrderServiceTest {
private final OrderService orderService = new OrderService();
@Test
void shouldApplyTenPercentDiscountWhenTotalReachesOneMillion() {
Order order = new Order(List.of(new OrderItem("Veste", 600_000, 2)));
assertThat(orderService.totalAfterDiscount(order)).isEqualTo(1_080_000);
}
@Test
void shouldNotDiscountSmallOrders() {
Order order = new Order(List.of(new OrderItem("Chapeau", 150_000, 1)));
assertThat(orderService.totalAfterDiscount(order)).isEqualTo(150_000);
}
}
Ensuite, confiez la tâche avec une brève description et des critères d’achèvement clairs :
Créer un record Order, OrderItem(name, unitPrice, quantity) et OrderService
pour que OrderServiceTest passe. Ne pas modifier le fichier de test.
Terminer quand `mvn -q test` n'a plus d'erreurs. Résumer les fichiers modifiés.
Une solution que l’agent pourrait fournir :
import java.util.List;
record OrderItem(String name, long unitPrice, int quantity) {
long subtotal() { return unitPrice * quantity; }
}
record Order(List<OrderItem> items) {
long total() { return items.stream().mapToLong(OrderItem::subtotal).sum(); }
}
class OrderService {
private static final long DISCOUNT_THRESHOLD = 1_000_000;
long totalAfterDiscount(Order order) {
long total = order.total();
return total >= DISCOUNT_THRESHOLD ? total * 90 / 100 : total;
}
}
Votre travail maintenant est de revoir. Le seuil est-il >= ou >? Le test actuel ne peut pas distinguer ces deux cas. C’est un signal pour ajouter un test limite, exactement 1.000.000 VND, avant de fusionner. L’agent excelle à faire passer les tests. Mais vérifier si le test est métier correct reste votre responsabilité.
4.4. Maintenir le dernier point de contrôle
Le code écrit par l’agent doit passer par le même processus que le code écrit par un humain. Un script de vérification commun aide l’agent et la CI à utiliser les mêmes normes :
#!/usr/bin/env bash
set -euo pipefail
mvn -q compile
mvn -q test
git diff --stat # affiche la liste des fichiers modifiés pour que le reviewer comprenne l'étendue
set -euo pipefail fait arrêter le script dès qu’une étape échoue. Vous écrivez cette commande dans AGENTS.md comme « définition de terminé », puis laissez la CI relancer exactement ce script sur la pull request.
5. Comment le travail du dev change-t-il ?
Quand vous utilisez régulièrement l’agent, les priorités du travail se déplacent nettement :
- De taper du code à écrire des spécifications. Une tâche vague aboutit à un résultat vague. Les compétences pour décomposer le travail, préciser les contraintes et définir les critères d’achèvement deviennent plus importantes.
- Plus de review, plus minutieux. Le volume de code généré dépasse la capacité de lecture des humains. Les pull requests petites, les diffs courts et les tests solides sont essentiels pour un review significatif.
- Tests et outils deviennent un atout clé. Un projet avec des tests rapides, des commandes de build claires et un linter strict tirera bien plus parti de l’agent. Un projet sans cela, l’agent ne fait que « deviner ».
- Les connaissances fondamentales restent décisives. Vous ne détecterez les race conditions, les failles SQL injection ou les requêtes N+1 dans le code écrit par l’agent que si vous les comprenez.
6. Quand ne pas utiliser et erreurs courantes
L’agent n’est pas approprié dans ces situations :
- Exigences floues ou décisions architecturales nécessaires. L’agent choisira une direction et l’implémentera avec assurance. Concevez d’abord, confiez l’exécution après.
- Code touchant à la sécurité, aux paiements, aux données personnelles. L’agent peut aider, mais tout changement demande une revue approfondie. Ne laissez pas l’agent fusionner tout seul.
- Pas de moyen de vérification automatique. Sans tests, sans build possible localement, l’agent n’a pas de vrai feedback. Le résultat n’est que pure supposition.
- Opérations irréversibles. Par exemple, exécuter une migration sur une vraie base de données, supprimer des ressources cloud, envoyer des emails aux clients.
- Quand vous apprenez une nouvelle technologie. Tout confier à l’agent vous fait perdre la chance de bien comprendre.
Erreurs courantes :
- L’agent supprime ou relâche les tests pour les faire passer. Interdisez-le clairement dans le fichier de guide et surveillez les diffs du répertoire
src/test. - L’agent invente une API qui n’existe pas dans la version de la bibliothèque que vous utilisez. Le compilateur et les tests attraperont la plupart de ces erreurs, à condition que vous forciez l’agent à les exécuter.
- Changements qui débordent du scope : reformater tout le fichier, renommer des variables non pertinentes. Exigez un diff minimal et un résumé des fichiers modifiés.
- Fuite de secrets : l’agent peut lire
.envou des fichiers de configuration contenant des clés. Excluez ces fichiers de l’environnement de travail de l’agent.
Note : La responsabilité du code dans le repo ne se transfère pas à l’agent. Celui qui fusionne la pull request en est toujours responsable.
7. Conclusion
L’AI coding agent est l’étape suivante après l’autocomplete et le chatbot. Il fonctionne dans une boucle de décision, appel d’outil, observation du résultat, et accomplir ainsi des tâches multi-étapes.
Pour l’utiliser efficacement et en toute sécurité, vous avez besoin de trois choses :
- un fichier de guide qui précise comment build/test, les conventions et les zones interdites ;
- limiter les permissions d’exécution de commande dans un environnement isolé ;
- les tests comme arbitre, avec une review consciencieuse avant fusion.
L’agent excelle dans les tâches claires et vérifiables. La conception, les décisions métier et la responsabilité finale restent les vôtres.