Les agents IA aiment Gleam
Cet article présente notre exploration initiale de Gleam pour Curling IO v3. Nous avons finalement choisi Rust et TypeScript. Pourquoi nous avons choisi Rust pour la version 3 explique cette décision et ce que nous avons conservé de cette exploration.
Vous êtes prévenus : cet article contient des opinions qui seront controversées et qui pourraient mal vieillir. Terrain dangereux.
Les agents de programmation IA comme Claude Code, OpenAI Codex et Google Gemini peuvent écrire du code, l’exécuter, lire les erreurs et réessayer. Tout repose sur cette boucle. Plus elle est rapide et informative, plus l’agent devient utile. Après avoir développé Curling IO version 3 en Gleam avec des agents de programmation IA, je suis convaincu que Gleam est le meilleur langage pour cette méthode de travail. Les agents n’écrivent pas un meilleur Gleam, car les données d’entraînement sont moins nombreuses. Mais son compilateur leur permet de se corriger sans attendre un humain.
La boucle de rétroaction qui compte
Tous les agents de programmation IA fonctionnent de la même façon : écrire du code, vérifier qu’il fonctionne, corriger ce qui est brisé et recommencer. La qualité de l’étape de vérification détermine tout.
Dans un langage typé dynamiquement, vérifier que le code fonctionne signifie exécuter la suite de tests. Cela prend du temps. Les tests ne couvrent pas nécessairement ce qui est réellement brisé, et ils sont eux aussi du code : chaque test ajouté augmente la complexité du projet. Beaucoup de bogues ne se manifestent qu’à l’exécution, parfois bien plus tard en production. L’agent écrit du code qui semble correct. Vous le révisez, et il vous semble correct aussi. Puis un utilisateur rencontre une erreur nil à deux heures du matin.
Dans Gleam, vérifier signifie compiler. Cela prend quelques secondes. Quand la compilation échoue, les messages sont précis : voici le fichier, la ligne, ce que vous avez écrit et ce qui était attendu. L’agent les lit, corrige et compile à nouveau. Après quelques cycles, la structure du code est saine.
Ce que le compilateur détecte
Aucune valeur nulle
Gleam n’a pas de valeur null. Les valeurs facultatives sont représentées par Option(T), soit Some(value) ou None. Vous ne pouvez pas déréférencer une valeur nil par accident. Le compilateur oblige à traiter les deux cas. Les erreurs liées aux valeurs nulles sont l’exception la plus fréquente en production. Une étude portant sur plus d’un milliard d’événements les a trouvées dans 70 % des environnements de production.
Lorsqu’un agent écrit du JavaScript, il doit penser à vérifier les valeurs nulles partout. Il ne le fait pas toujours. Gleam élimine entièrement cette possibilité.
Les références nulles sont connues comme l’erreur à un milliard de dollars, et ce n’est pas qu’une préoccupation théorique. En juin 2025, une seule valeur nulle dans un champ de base de données s’est propagée dans le système Service Control de Google Cloud et a mis plusieurs produits GCP et Workspace hors service dans le monde pendant des heures. Cette valeur a atteint un parcours sans gestion d’erreur, s’est répliquée mondialement en quelques secondes et a causé une panne résolue complètement après près de trois heures. Le type Option de Gleam aurait obligé le développeur ou l’agent à traiter le cas manquant avant de compiler. Il n’empêche pas toutes les défaillances d’une telle chaîne, mais élimine la catégorie précise de bogue qui l’a déclenchée.
Il y a aussi un enjeu de sécurité. Des valeurs nulles non traitées peuvent laisser une application dans un état inattendu : vérifications d’authentification sautées parce qu’un nil s’est glissé dans le parcours, ou données exposées sur une page d’erreur qui n’aurait jamais dû être atteinte. Chaque catégorie d’erreur éliminée par le compilateur représente une surface d’attaque que l’agent ne peut pas introduire par accident.
Changements structurels
Voici un exemple réel de notre code. Supposons que l’agent ajoute un champ à un type :
pub type Listing {
Listing(
id: Int,
name: String,
capacity: Int,
waitlist_enabled: Bool, // new field
)
}
En JavaScript, ajouter une propriété à un objet ne change rien avant l’exécution. Si un modèle ou un point d’accès API n’inclut pas le champ, vous ne le saurez que lorsque ce parcours sera exécuté. Les tests peuvent le détecter, ou le manquer si la couverture est incomplète.
En Gleam, toutes les fonctions qui construisent ou déstructurent un Listing cessent alors de compiler. Le compilateur liste chaque endroit à mettre à jour. L’agent suit la liste, fait les changements et obtient une compilation réussie.
Cela vaut pour tous les changements structurels : renommer un champ, passer un type de String à Int, ajouter une variante à un type union. Les agents s’en sortent bien. Les humains oublient des éléments dans les listes.
Filtrage par motifs exhaustif
Le compilateur Gleam exige que le filtrage par motifs couvre tous les cas possibles. Pour un Result, il faut traiter Ok et Error. Pour un type union personnalisé à quatre variantes, il faut traiter les quatre. Le compilateur vous signale tout oubli.
Prenons un type d’état de paiement :
pub type PaymentStatus {
Pending
Completed
Refunded
Failed
}
Si quelqu’un ajoute une variante Disputed, toutes les expressions case qui filtrent un PaymentStatus dans le code cessent de compiler tant qu’elles ne traitent pas Disputed. Dans un langage typé dynamiquement, l’agent écrit un switch ou une chaîne de if pour les cas courants et oublie le cas limite. Une erreur d’exécution attend de se produire. En Gleam, le compilateur la détecte avant toute exécution.
Une syntaxe simple, moins d’erreurs possibles
Gleam est un petit langage. Une seule façon de définir une fonction et de traiter les erreurs et les valeurs facultatives : Result et Option. Aucune exception, conversion implicite ou macro.
C’est important pour les agents, car un espace de décision réduit signifie moins de mauvaises décisions. Quand il existe six façons de faire quelque chose, l’agent doit choisir, et il ne choisit pas nécessairement l’approche idiomatique. En Gleam, il n’y en a généralement qu’une. L’agent n’a pas à connaître les préférences stylistiques de la communauté ou les conventions de gestion d’erreurs du projet. Le langage a déjà tranché.
Le formatage suit la même logique. gleam format fait autorité. Aucune configuration, aucun débat de style. Après formatage, le code de l’agent est identique en apparence au code écrit à la main. Impossible de voir la différence, et ce n’est pas nécessaire.
Les espaces ne comptent pas
Je préfère en fait les langages où les espaces sont significatifs. Elm et Haskell sont parmi mes préférés. Une indentation nette me semble plus belle que des accolades partout. Mais les agents ne la maîtrisent pas encore. D’après mon expérience, ils trébuchent constamment sur les modèles Slim. Les erreurs d’indentation sont fréquentes dans le code produit par les LLM. Le modèle peut mélanger tabulations et espaces ou se tromper d’un niveau d’imbrication. Ces erreurs sont silencieuses et sémantiques : elles changent ce que fait le code.
Gleam utilise des accolades. Les espaces ne comptent pas pour le compilateur. gleam format les normalise. Une catégorie d’erreurs en moins pour les agents.
Des boucles de rétroaction rapides
Une compilation rapide
Le compilateur Gleam traite notre projet complet en quelques secondes. Une suite de tests typique dans un langage typé dynamiquement prend de 30 secondes à quelques minutes. Lorsque l’agent itère, l’écart entre quelques secondes de compilation et plusieurs minutes de tests s’accumule rapidement.
Des tests rapides
Les tests sont rapides aussi. Chacun a sa propre base SQLite en mémoire sans état partagé, ce qui nous permet de tous les exécuter en parallèle. Environ 800 tests se terminent en moins d’une seconde. Ce gain s’accumule pendant une session où l’agent compile et teste des dizaines de fois.
Moins de données d’entraînement, un vrai coût
Gleam a moins de données d’entraînement que JavaScript, TypeScript ou Python. Les agents produisent du code Gleam de moins bonne qualité au premier essai. Ils utilisent des approches inexistantes et inventent des fonctions absentes de la bibliothèque standard.
Le coût est réel : l’agent prend plus de temps à écrire le code initial en Gleam qu’en JavaScript.
Mais voici ce que j’ai constaté : le délai entre le début du travail et le déploiement d’un code correct est plus court en Gleam. L’agent écrit plus lentement, mais le compilateur détecte immédiatement les erreurs. En JavaScript, il écrit plus vite, mais les erreurs apparaissent plus tard, dans les tests ou en production.
La méthode en pratique
Voici le déroulement d’une fonctionnalité typique :
- Nous écrivons une spécification de ce que nous voulons. C’est itératif : nous l’affinons avec l’agent avant d’écrire du code. Une bonne spécification fait la différence entre un résultat utile et du rebut.
- L’agent écrit l’implémentation : types, requêtes de base de données, gestionnaires de requêtes et vues.
- Il lance
gleam build. La compilation échoue avec 5 à 10 erreurs. - L’agent lit chaque erreur, corrige et recompile. Il fait quelques cycles.
- La compilation réussit. L’agent lance les tests. Ils passent ou échouent sur la logique métier, pas sur des erreurs de valeurs nulles ou de types incompatibles.
- Nous révisons les différences, en regardant la logique, l’intention et la redondance.
Les agents ont tendance à dupliquer du code plutôt qu’à réutiliser les fonctions existantes, probablement à cause des limites de leur fenêtre de contexte. Repérer les occasions d’extraire une logique partagée est notre commentaire le plus fréquent.
Nous écrivons encore beaucoup de code à la main. Les agents n’ont pas toujours raison. Ils produisent parfois du code laid ou redondant qu’il faut repérer et réécrire. Nous intervenons lorsque l’agent tourne en rond et essaie de corriger la même erreur sans avancer. En Gleam, cela arrive beaucoup moins que ce que nous avons vu dans les langages dynamiques.
Les autres langages
D’autres langages à typage statique avec de bons compilateurs partagent certaines de ces propriétés. Rust, OCaml, Haskell et Elm ont tous des systèmes de types forts qui détectent les erreurs à la compilation.
Mais Gleam combine des caractéristiques qui conviennent particulièrement aux agents :
- Une syntaxe simple et facile à générer.
- Une compilation rapide pour des boucles de rétroaction courtes.
- Un formatage de référence qui donne au code de l’agent l’apparence du code écrit à la main.
- Aucune valeur nulle, ce qui élimine une catégorie d’erreurs très courante.
- Un filtrage par motifs exhaustif qui empêche d’oublier des cas.
- Des espaces non significatifs, pour que les erreurs de formatage ne changent pas le comportement.
Il fonctionne aussi sur BEAM, qui offre la tolérance aux pannes et la concurrence, mais c’est un autre sujet.
Le compromis est la maturité de l’écosystème et les données d’entraînement. Gleam est jeune. Les bibliothèques sont moins nombreuses. Le code des agents demande plus de corrections au premier passage. Cet écart se réduit à mesure que les données d’entraînement augmentent.
Le pari
Je crois que les agents IA écrivent déjà la majorité du code dans un nombre croissant de projets. Le choix de langage est évalué autrement. La vitesse à laquelle un humain peut écrire compte moins. Le temps de révision exigé du développeur compte davantage que tout.
Le développeur est le goulot d’étranglement. Nous sommes lents comparés aux ordinateurs. Si le compilateur a déjà vérifié la structure du code avant l’arrivée des différences à l’écran, la personne qui révise peut se concentrer sur la logique et l’intention plutôt que de chercher les vérifications nil manquantes.
Nous avons choisi Gleam pour d’autres raisons, mais sa compatibilité avec les agents de programmation IA a été un avantage énorme.
Addenda : où en est la programmation assistée par IA
Prenons du recul par rapport à l’argument technique. La programmation assistée par IA est là pour rester. Même si la technologie ne s’améliore pas sensiblement, elle est déjà assez utile pour que les développeurs l’adoptent en masse.
Les exigences augmentent
Les agents sont déjà assez bons pour beaucoup de tâches d’applications métier, mais cette affirmation vient avec une grosse réserve. Chaque modification demande un examen attentif. Le développeur doit être un expert du domaine, de la sécurité et de la programmation. La vitesse de production du code a augmenté le niveau d’expertise requis du développeur plutôt que de le réduire.
Attendez-vous à plus de vulnérabilités
Les agents produisent du code qui fonctionne, mais le fonctionnement et la sécurité sont des critères très différents. Ils détecteront probablement les problèmes évidents comme l’injection SQL. Les problèmes moins évidents sont dangereux : par exemple, ne pas réaliser qu’une décision prise trois demandes plus tôt rend indirectement les renseignements personnels de tous vos clients accessibles par une URL publique. Cela exige une compréhension de l’ensemble que les agents n’ont pas encore. Je pense que ce phénomène deviendra visible dans les prochaines années, à mesure que du code écrit par des agents arrivera en production sans révision suffisante.
Le secteur évolue
Le secteur cherche activement les bonnes abstractions pour le développement assisté par IA. Deux tentatives se démarquent :
Mog est un langage conçu pour être écrit et lu par des IA plutôt que des humains. Je suis sceptique. Si le développeur qui révise les différences est le goulot d’étranglement, privilégier la lisibilité machine au détriment de la lisibilité humaine aggrave la situation.
Codespeak propose une autre approche : des spécifications et du code comme représentations interchangeables, avec un passage dans les deux sens. Je pense que les deux sont importants et qu’aucun ne suffit actuellement seul. Une spécification aide à réfléchir à l’ensemble, mais manque des détails qui comptent lorsque les choses tournent mal. Le code contient tous les détails, mais sa lecture permet difficilement de prendre du recul sur tout le système. Ils se situent à des niveaux de détail différents. Les fusionner en une seule chose fait perdre l’utilité propre à chacun.
Les langages à typage statique ont un avantage : les types se documentent eux-mêmes. Un type bien défini en Gleam communique déjà une bonne partie de ce qu’une spécification dirait de la structure des données et des limites d’une fonction, sans document distinct.
La suite
Le rôle du développeur se déplace vers la supervision. Cela pourrait signifier beaucoup plus de logiciels créés, beaucoup moins de développeurs employés, ou les deux. Et ce rôle de supervision pourrait lui-même disparaître si les agents deviennent capables de boucler le travail seuls. C’est une possibilité réelle qu’il faut reconnaître honnêtement.
Après un an à développer des logiciels en production avec des agents de programmation IA, je constate que cela fonctionne si l’on fait l’effort de vérifier tout ce qui en sort. Gleam n’a pas été conçu pour les agents IA, mais une bonne conception du langage compte finalement plus que les outils créés expressément pour eux.