NATO Alphabet Coach

Replit Agent a construit une app fonctionnelle en une heure. J’ai passé les neuf jours suivants à la réparer.
L’idée
L’alphabet phonétique OTAN. Alpha, Bravo, Charlie. On croit le connaître jusqu’au moment où quelqu’un vous demande d’épeler « Zurich » par radio et que votre cerveau se fige sur le Z. Je voulais une petite app d’entraînement avec répétition espacée, saisie vocale, plusieurs langues, des thèmes, un convertisseur. Pas une startup. Juste un outil utile. Vous pouvez l’essayer vous-même. J’ai ouvert Replit, décrit ce que je voulais, et laissé l’Agent construire.
L’heure magique
Replit Agent a pris le cahier des charges complet et a échafaudé l’application entière en une seule session. 77 fichiers. Plus de 16 000 lignes. React, TypeScript, Tailwind, composants shadcn. Tout ce que j’avais demandé, câblé et fonctionnel. Premier déploiement : le jour même.

La pièce la plus impressionnante : l’algorithme de répétition espacée.
export function selectLetterForReview(
userProgress: UserProgress[]
): string {
const now = new Date();
// Priority 1: letters past their review date
const needReview = userProgress.filter(
(p) => new Date(p.nextReview) <= now
);
if (needReview.length > 0) {
// Higher difficulty, older reviews first
needReview.sort((a, b) => {
const diff = b.difficulty - a.difficulty;
if (Math.abs(diff) > 0.5) return diff;
return new Date(a.lastReviewed).getTime()
- new Date(b.lastReviewed).getTime();
});
return needReview[0].letter;
}
// Priority 2: letters below 70% accuracy
const poor = userProgress.filter((p) => {
const total = p.correctCount + p.incorrectCount;
return total > 0 && p.correctCount / total < 0.7;
});
if (poor.length > 0) { /* worst accuracy first */ }
// Priority 3: unpracticed letters (random)
// Fallback: least-attempted letter
}
L’Agent a fait mouche. Décroissance exponentielle, seuil de précision à 70 %, difficulté qui grimpe à chaque succès et recule à chaque échec. Mieux que ce que j’aurais écrit à la main.
Et puis la première heure s’est terminée.
Ce qu’il a fallu corriger
La spirale du système de thèmes
J’avais demandé plusieurs thèmes ; l’Agent en a livré cinq. Les construire était la partie facile. Les rendre lisibles, c’était autre chose.
Le texte était illisible en mode sombre. Invisible sur le dégradé arc-en-ciel. Contraste faux sur le thème NATO. L’Agent a produit huit commits consécutifs pour tenter de corriger la visibilité. Huit commits. Même problème. Chaque correctif cassait un thème différent.
Puis une tentative de migration vers Tailwind 4 a touché 49 fichiers et tout cassé. Le retour arrière a été immédiat : 1 377 lignes restaurées. Construire une fonctionnalité et la maintenir à travers le changement sont deux compétences distinctes. L’Agent ne possédait que la première.
Reconnaissance vocale : « fix mike hopefully »
La saisie vocale faisait partie du cahier des charges initial. L’Agent l’a livrée le deuxième jour. Elle a cassé immédiatement.
Ce qui avait commencé comme un bouton de saisie vocale touchant quatre fichiers s’est transformé en une odyssée de 19 fichiers. Détection de plateforme, gestionnaires d’erreurs par navigateur, surveillance audio, fabriques d’événements de reconnaissance. Six réécritures majeures sur six jours. Les messages de commit racontent l’histoire :
“Try to fix speech recognition”
“attempting to fix microphone”
“fix mike hopefully”
“Try to fix speech on android”
“try to fix ios speech”
Relisez-les. Regardez les majuscules disparaître. Regardez « fix » passer d’un verbe assuré à une supplique.
// What voice input started as: 4 files, one hook
export function useSpeechRecognition(config) {
// ~80 lines of straightforward Web Speech API
}
// What it became: 19 files across two directories
import { getAndroidInfo, getBrowserInfo, getIOSInfo,
isSpeechRecognitionSupported } from './speech/platform-detection';
import { handleAndroidError,
handleIOSError } from './speech/platform-error-handlers';
import { createRecognitionHandlers } from './speech/recognition-handlers';
import { initializeSpeechRecognition,
testMicrophoneAccess } from './speech/speech-recognition-utils';
import { stopAudioMonitoring } from './speech/audio-monitoring';
Frictions de déploiement
Replit génère des URL de développement dynamiques. Vite refuse les hôtes inconnus. Cinq tentatives de configuration plus tard, l’Agent a atterri sur allowedHosts: 'all'. Le système de notifications appelait une API backend qui n’existait pas ; il a fallu le supprimer entièrement. Une douzaine de commits, zéro fonctionnalité visible pour l’utilisateur.
Ce qui m’a surpris
Le support RTL. J’avais demandé plusieurs langues dont l’arabe, et l’Agent a déduit que l’arabe nécessite une mise en page droite-à-gauche. Le wrapper détecte la langue, inverse la direction du document, l’interface se miroir correctement. Livré proprement dès le premier passage. Quand le problème est bien défini et que les API du navigateur coopèrent, l’Agent excelle.

L’historique git est devenu un artefact en soi. Les commits de l’Agent sont verbeux : « Enhance user experience with voice control, hints, and compact display. » Les commits du développeur humain sont laconiques : « fix mike hopefully. » On voit le moment exact où l’humain a dû reprendre la main.
La leçon
Il y a un piège dans le vibe coding, et il a la forme du principe de Pareto. Les premiers 80 % sont éblouissants. Les 20 % restants, ce sont les API cross-navigateurs, la configuration de déploiement, la cohérence visuelle à travers des états qu’on n’a pas testés.
L’Agent construit la maison. C’est vous qui faites marcher la plomberie.
La Web Speech API sur iOS Safari, Android Chrome et les navigateurs de bureau n’a pas de solution propre et reproductible. On ne trouve pas un bon design et une implémentation solide en cherchant sur Google. Il faut les construire, par essais et erreurs, sur de vrais appareils, avec de vrais micros. Ce genre de travail, c’est précisément ce dont l’écart 80/20 est fait. Le NATO Alphabet Coach fonctionne. Les gens l’utilisent pour s’entraîner sur leurs Alpha et leurs Bravo. Il a fallu un développeur pour en arriver là.