Fix Mike Hopefully

L'alphabet phonétique OTAN rencontre le code assisté par IA

Replit Agent a construit une application 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 monté 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é, déjà en place et fonctionnel. Premier déploiement : le jour même.

Le convertisseur transformant « Hello World » en badges alphabet OTAN

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 visé juste. Espacement exponentiel, 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, et le vrai travail a commencé.

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, avec un mauvais contraste 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é 44 fichiers et tout cassé. Retour arrière : 1 363 lignes annulées. L’Agent savait construire des fonctionnalités. Les maintenir face au changement, c’était une tout autre compétence.

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. Cas particuliers par téléphone et navigateur, gestionnaires d’erreurs dédiés, 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. Voyez les majuscules disparaître, « 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';

La reconnaissance vocale part dans tous les sens d’un navigateur à l’autre. Chromium offre un chemin à peu près praticable, Safari arrive avec ses propres contraintes, et Firefox devient un cas de repli plutôt qu’une implémentation sur laquelle on peut compter. Un développeur construirait une couche propre pour masquer ces différences et garder la logique spécifique au navigateur derrière un petit nombre d’adaptateurs ciblés. L’Agent n’a pas trouvé cette frontière assez tôt. Alors chaque correctif de plateforme est devenu son propre fichier, son propre gestionnaire, son propre cas particulier. Dix-neuf fichiers là où trois suffiraient.

Frictions de déploiement

Replit génère des URL de développement qui changent sans cesse. Vite refuse les hôtes qu’il ne connaît pas. Cinq tentatives de configuration plus tard, l’Agent a fini par bricoler un contournement qui ouvrait les vannes à tous les hôtes pour les aperçus. 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 : le genre de travail qui ne figure jamais dans une démo.

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 de droite à gauche. Le composant racine détecte la langue, inverse la direction du document, et l’interface se réorganise correctement. Et, cette fois, ça a marché proprement du premier coup. Quand le problème est bien défini et que les API du navigateur coopèrent, l’Agent excelle.

Paramètres montrant 7 langues, contrôles vocaux et sélection de thème

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

Le vibe coding cache un piège, et il a la forme du principe de Pareto. Les premiers 80 % sont éblouissants ; les 20 % restants, ce sont les API qui se comportent différemment selon les navigateurs, la configuration du déploiement, et 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.

Il n’existe pas de solution universelle, propre et recopiable pour la reconnaissance vocale sur iOS Safari, Android Chrome et les navigateurs de bureau. Aucune recherche Google ne mène à un bon design couplé à une implémentation solide. Il faut la construire soi-même, 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’exercer sur les Alpha et les Bravo, et pour en arriver là, il a fallu un développeur capable de rester face à des permissions micro cassées sur trois environnements jusqu’à ce que le code marche.

Le rôle avait changé : je ne construisais plus du logiciel, je le finissais.