Articles

Défi Claude Code n°1 : Dicode

Une app iOS et Android, neuf dictionnaires et un site de 94 000 pages, sans écrire une ligne de code

Cyril Feraudet · 19 octobre 2026

À 15 ans, passionné d’électronique, d’informatique et de radiocommunications, je passe l’examen de radioamateur. Le ministère des Postes et Télécommunications me délivre l’indicatif radio FA1TMV, puis F1TMV à 16 ans. Ce milieu me fait rencontrer des passionnés bien plus âgés que moi, qui deviendront quelques années plus tard mes collègues de travail.

Leur vocabulaire n’est pas celui d’un ado de 16 ans. Beaucoup de mots courants chez les adultes me sont alors inconnus. Par frustration, je décide de lire le dictionnaire en entier.

On me prend pour un dingue quand je raconte ça. En réalité, ça va vite : on saute les mots qu’on connaît déjà, et ceux dont le début de la définition montre qu’ils n’ont pas d’intérêt immédiat. Pour être honnête, du fait de ce filtrage, je ne l’ai pas réellement lu en entier.

Aujourd’hui, mes enfants ont 15 ans et bientôt 17. Début janvier, je leur ai suggéré de faire la même chose. Autant pisser dans un violon, vous imaginez.

D’où l’idée (parce que sans idée, pas de projet) : reprendre le scroll infini des réseaux sociaux pour en faire un dictionnaire. Un mot par écran, du spaced repetition (algorithme SM-2) qui fait revenir chaque mot au bon moment, et des quiz. C’est Dicode.

Le défi, car c’est bien de cela qu’il s’agit : ne pas écrire une seule ligne de code. Tout a été écrit par Claude Code, uniquement en lui dictant mes specs et en travaillant sur son harness, plus particulièrement ses tools et ses MCP. Moi, je relis, je teste sur mon smartphone et je tranche.

Cet article raconte les huit mois et demi qui séparent le premier commit, le 1er février 2026, de la version 1.30.4 publiée sur les stores le 18 octobre. Avec ce qui a marché, ce qui a foiré, et ce que Claude ne fait pas.

La même définition au niveau CP-CE2 et au niveau Doctorat

Ce que fait Dicode

Dicode est une app iOS et Android de vocabulaire. On fait défiler les mots comme un feed de réseau social. Pour chaque mot, on choisit son niveau de lecture : la même entrée « éphémère » se lit « comme un papillon qui vit juste un jour » au niveau CP, et devient une « propriété ontologique de ce qui est constitutivement voué à la finitude temporelle » au niveau doctorat. Un swipe à droite range le mot dans « trop facile », un swipe à gauche le fait revenir en révision.

Elle contient neuf dictionnaires, tous hors ligne, embarqués dans l’app :

DictionnaireContenu
Français général476 315 mots, 7 niveaux de définition du CP au doctorat
Droit14 228 termes
Latin11 647 définitions, 249 978 formes fléchies
Grec ancien10 229 définitions
Intelligence artificielle2 704 termes
Géographie2 495 définitions, avec drapeaux
Histoire2 417 définitions, quatre frises chronologiques
Orthographe1 934 pièges
Philosophie532 définitions

Le tout fait 240 592 questions de quiz. À côté, le site https://dicode.fr publie 94 069 définitions, et un widget permet à n’importe quel site d’afficher la définition d’un mot quand on clique dessus.

Le feed des mots

1. Tout commence par un CLAUDE.md

Claude Code lit au démarrage un fichier CLAUDE.md à la racine du repo. C’est là qu’on écrit les règles du projet. Le repo en a un par sous-projet : l’app, les dictionnaires, le site, le marketing. Chacun dit comment builder, tester et déployer, et surtout ce qu’il ne faut jamais refaire.

La partie la plus utile, c’est la section « Known issues ». Chaque ligne est une erreur qui a coûté au moins un build. Un extrait :

PiègeConséquenceParade
LIMIT sans ORDER BY sur WikidataDeux runs ne renvoient pas le même sous-ensembleFusionner avec l’existant, ne jamais remplacer
Une requête qui fail sans erreurLe script écrit un fichier tronqué par-dessus le bonGuardrail : refuser d’écrire si le corpus maigrit
Deux builds iOS en parallèleexpo prebuild --clean sur le même ios/, les deux échouentUn seul build à la fois
LOWER() de SQLite sur un mot à capitale accentuéeLOWER('Écoute') reste Écoute : 6 149 questions de quiz invisiblesComparer des colonnes déjà normalisées (casefold) en Python

Quand une erreur arrive, on la corrige, puis on l’écrit. La session suivante ne la refait pas. C’est le vrai mécanisme d’apprentissage du projet : pas le modèle, le fichier.

À côté, Claude garde une memory d’une session à l’autre : une note par fait non évident (« FTS5 n’existe pas dans le SQLite d’Android », « le track internal du Play Store ne vérifie pas l’alignement 16 Ko »), avec le pourquoi et la façon de l’appliquer.

2. Les données : neuf dictionnaires

C’est la plus grosse partie du code : environ 49 600 lignes de Python, contre 33 000 pour l’app et 14 400 pour le site.

Les sources sont publiques : Wiktionnaire, Wikidata, la base Lexique pour les fréquences d’usage. Claude écrit les scripts d’extraction, puis rédige lui-même les définitions, en batch, avec la Batch API d’Anthropic (moitié prix, résultat dans la journée). Pour le français général, chaque mot reçoit sept définitions, une par niveau.

La Batch API mérite qu’on s’y arrête : c’est elle qui rend le projet abordable. On envoie des milliers de requêtes d’un coup, Anthropic les traite dans les 24 heures (souvent bien moins), et la facture est divisée par deux. Pour un dictionnaire, l’attente ne gêne personne : on lance un batch le soir, on fusionne le résultat le lendemain.

Les chiffres du dictionnaire d’intelligence artificielle, tirés du changelog du projet :

  • 2 628 termes, quatre niveaux de lecture chacun et 7 293 QCM : 10 batches, environ 75 $ au total ;
  • avant la production, deux batches de test de 10 termes, pour 0,40 $. Ils ont révélé trois défauts du prompt, dont un niveau expert 35 % trop court ;
  • la réécriture des niveaux avancé et expert de 2 727 définitions : 98,46 $.

Deux réglages ont compté autant que la remise :

  • réécrire plutôt que régénérer : on repasse le texte existant en input, et un token d’input coûte cinq fois moins cher qu’un token d’output ;
  • un effort medium plutôt que high : le thinking pesait 58 % de l’output facturé. Sur trois batches de test, l’effort high coûtait 157 $, pour un résultat moins bon.

Les faits, eux, ne sont jamais générés. La capitale d’un pays, sa monnaie, sa population viennent de Wikidata. Et Wikidata a ses pièges, qui sont tous dans le CLAUDE.md aujourd’hui :

  • la France avait pour capitale « Alger, Bayeux, Bordeaux » : la requête remontait aussi les valeurs historiques. Il faut exclure celles qui portent une date de fin (propriété P582) ;
  • l’euro s’affichait Q4916 : Wikidata a déplacé une partie de ses labels vers un code de langue mul. Les requêtes demandent maintenant fr,mul,en, et un label de la forme Q suivi de chiffres est rejeté.

Les quiz sortent d’une pipeline en cinq étapes, et chaque étape repart de là où elle s’est arrêtée en cas d’interruption : filtrer les mots, faire juger les candidats par Claude, planifier les types de questions, générer, fusionner. Chaque question a un ID stable : on la retrouve d’une génération à l’autre.

Un quiz de géographie : les drapeaux viennent de Wikidata, pas du modèle

3. L’app

L’app est en React Native avec Expo. Le dictionnaire général pèse 138 Mo en SQLite. Le copier au premier lancement prenait une minute : un module natif, en Swift et en Kotlin, l’ouvre directement en lecture seule depuis l’app. Le premier lancement est instantané.

Ce module a sa propre histoire. La recherche full-text utilise FTS5, une extension de SQLite. iOS l’a. Android non : le SQLite du système est compilé sans. Claude a donc compilé SQLite lui-même, avec FTS5, dans le module Android.

Puis, en août, le Play Store a refusé la release en production : « Artifact does not support 16KB page size ». Sur les 24 libs natives de l’app, une seule n’était pas conforme : notre SQLite compilé maison, aligné sur 4 Ko. Le fix tient en une ligne de CMake :

target_link_options(dicode_sqlite PRIVATE "-Wl,-z,max-page-size=16384")

Le piège : le track internal du Play Store ne vérifie pas cette règle. L’upload passait sans erreur, et seule la release en production échouait.

L’app a un mode clair et un mode sombre. Pour qu’aucune couleur en dur ne pose problème dans l’un des deux, une règle ast-grep refuse les couleurs littérales dans les composants. La règle a ses angles morts (une couleur construite par concaténation lui échappe), et le CLAUDE.md les liste.

4. Le site

Le site https://dicode.fr est construit avec Astro : 94 069 pages de mots pré-générées (SSG), hébergées sur S3 derrière CloudFront, avec toute l’infrastructure décrite en OpenTofu. Quand un visiteur cherche un mot qui n’a pas encore de définition, une Lambda la fait rédiger par Claude à la volée, la stocke sur S3, et elle devient une page statique au déploiement suivant.

Le SEO nous a appris une chose. Pour choisir les pages à mettre dans le sitemap, la première idée était un seuil de fréquence d’usage. Mesuré sur 16 mois de données Search Console, ce seuil aurait retiré du sitemap des pages qui portaient 59 % des impressions du site. On ne cherche pas la définition des mots courants : on cherche celle des mots qu’on ne connaît pas. Le sitemap retient maintenant les mots que les gens cherchent vraiment.

5. Les tests

Environ 1 500 tests automatiques : 681 tests unitaires et 466 tests d’intégration dans l’app, 151 sur le site, environ 200 en Python sur les données, et 7 flows Maestro qui pilotent l’app sur simulateur.

Les flows Maestro ont donné la leçon la plus utile. Trois d’entre eux passaient en validant un geste que personne n’avait fait. tapOn: "mémoire" touchait le champ de saisie, pas le résultat de recherche : le flow ne prouvait rien, et un bug est resté des mois sans être vu. La règle depuis : après chaque tap, vérifier quelque chose que seul l’écran visé affiche.

6. Les stores

Tout est buildé en local sur le Mac, sans service de build payant : un script pour l’IPA iOS, un pour l’AAB Android, puis fastlane pour l’upload vers TestFlight, l’App Store et le Play Store. La version 1.30.4 porte le build 104, le même numéro sur iOS et sur Android.

Une règle vient d’un testeur qui ne savait pas quoi tester : chaque build TestFlight doit dire quoi tester. Sans release notes, fastlane envoyait « New build from Fastlane », et le testeur ouvrait l’app sans savoir quoi regarder. La lane refuse maintenant de publier sans le fichier de release notes. Elle refuse aussi le grec polytonique dans ces notes (ἀ, ῳ) : App Store Connect le rejette, mais seulement après l’upload et le traitement du build.

7. Ce qui a foiré

Tout est dans l’historique Git :

  • le feed ne passait pas au mot suivant. Chaque définition avait son propre scroll, imbriqué dans le scroll du feed. Une fois en bas d’une définition, iOS ne transmettait pas le geste : pendant 1,25 seconde, le doigt bougeait et l’écran non (mesuré sur vidéo). J’avais dit dès le début que chaque définition défilait « comme dans une iframe ». Plusieurs fixes ont traité les symptômes avant qu’on supprime la cause : il n’y a plus qu’un seul scroll ;
  • la liste des dictionnaires ne scrollait presque pas sur Android. Elle débordait de son bandeau, et Android ne transmet un geste à un élément que s’il est à l’intérieur de son parent. Les taps passaient, pas le scroll. Elle s’ouvre maintenant dans une modale ;
  • 242 mots avaient une mauvaise URL sur le site : « cœur » avait l’URL /mot/c-ur/, et « œil » /mot/il/, la même que le pronom. La lettre œ disparaissait au lieu de devenir « oe ». Corrigé, avec 244 redirections depuis les anciennes URL ;
  • la release Android en production bloquée par l’alignement 16 Ko, décrite plus haut ;
  • deux builds iOS lancés en parallèle ont planté tous les deux, alors que Claude les annonçait OK ;
  • une commande de déploiement terminée par | tail pouvait fail sans erreur : l’exit code était celui de tail. Les commandes qui comptent écrivent maintenant leurs logs dans un fichier.

8. Ce que Claude ne fait pas

  • tester sur un vrai téléphone : c’est moi qui fais défiler, qui sens qu’un geste accroche, qui vois qu’un texte est illisible en mode nuit ;
  • relire le contenu : une source publique donnait Alger comme capitale de la France. L’erreur a été repérée et corrigée avant d’arriver dans l’app, mais il faut quelqu’un pour la voir ;
  • trancher : le prix des dictionnaires, ce qui entre dans l’app, ce qu’on publie. Claude propose, je décide ;
  • les comptes : Apple, Google, le domaine, le paiement de l’API. Claude ne crée pas de compte et ne saisit pas de mot de passe.

Les chiffres

Duréedu 1er février au 19 octobre 2026
Commits649
Codeenviron 99 000 lignes : 49 600 de Python, 38 700 de TypeScript, 2 800 d’Astro, 1 800 d’OpenTofu, un peu de Swift et de Kotlin
Documentationenviron 36 000 lignes de Markdown
Tests681 unitaires et 466 d’intégration (app), 151 (site), environ 200 en Python, 7 flows Maestro
Contenu9 dictionnaires, 240 592 questions de quiz, 94 069 pages sur le site
Versions1.30.4, build 104, sur l’App Store et le Play Store

Ce que l’on peut en retenir

Le CLAUDE.md compte plus que le prompt. Un prompt brillant sert une fois. Une ligne dans les known issues sert à chaque session suivante. Chaque erreur ne se fait qu’une fois, à condition de l’écrire tout de suite, pendant qu’on sait encore pourquoi elle est arrivée.

Claude écrit le code vite, mais il faut tester sur un vrai téléphone. Le feed qui accrochait pendant 1,25 seconde, aucun test ne l’a vu. Je l’ai senti en faisant défiler l’app sur mon téléphone. Sur ce projet, ce qui ralentissait n’a jamais été l’écriture du code, mais le temps passé à tester l’app à la main.

Un test qui passe ne prouve que ce qu’il vérifie. Trois flows Maestro passaient sans rien prouver. Depuis, je ne demande plus « les tests passent ? », mais « qu’est-ce que ce test aurait vu en cas de régression ? ».

Les faits viennent de sources, jamais du modèle. Claude rédige très bien une définition pour un élève de CP. Ce n’est pas à lui de décider de la capitale de la France. Tout ce qui peut être vrai ou faux vient de Wikidata ou du Wiktionnaire, et des guardrails refusent d’écrire un fichier plus petit que le précédent.

Enfin, ne pas écrire de code ne veut pas dire ne pas travailler. Je n’ai jamais autant spécifié, relu, testé et tranché. Le métier change, il ne disparaît pas.

Liens