URL:     https://linuxfr.org/users/mattpiz/journaux/j-ai-vibe-code-une-appli-pendant-un-mois-puis-j-ai-tout-jete
Title:   J'ai vibe-codé une appli pendant un mois, puis j'ai tout jeté
Authors: mattpiz
Date:    2026-09-11T10:42:34+02:00
License: CC By-SA
Tags:    elm, programmation_fonctionnelle, chiffrement_bout_en_bout, local-first, dette_technique, grands_modèles_de_langage et logiciel_libre
Score:   5


Cher journal,

Le 6 février, j'ai poussé un dernier _commit_ et j'ai abandonné un mois de travail. L'application marchait. Je l’ai testée avec des copains un weekend entier, et ils étaient plutôt contents. Plus de 20 000 lignes de code produites en quelques semaines, à la poubelle. Trois semaines plus tard, je repartais de zéro, dans un dépôt vide, dans un autre langage. Voilà pourquoi.

# La conception

Ça fait neuf mois, on arrive enfin à terme. Ce n'était pas prévu au départ, mais comme je suis assez fier d'elle, et convaincu qu'elle pourra vous aider un jour, je vais vous raconter comment elle a été conçue.

Tout a commencé la semaine du nouvel an. On était plutôt nombreux, une bonne dizaine, et vous imaginez qu'à autant, la coordination peut vite devenir compliquée ! Alors pour simplifier, en général, c'est chacun son tour. Mais comme ça a duré toute une semaine, on finit par oublier qui a déjà pris, cher ou pas. Dans cette situation, le plus pratique, c'est encore d'utiliser une appli : on enregistre la participation de chacun, afin de s'en souvenir plus tard. On peut même ajouter des émojis dans les descriptions. Par exemple, moi je m'occupe des 🍑 aujourd'hui, et hier, c'est Alice qui a géré les 🍆.

Bref, vous voyez le genre.

Le principe de ce type d'appli est assez simple. Pierre s'occupe de louer la maison, on prend les voitures de Camille et de Marie pour le trajet, Thomas fait les courses, et Jean, son truc, c'est de payer les tournées au bar. Chacun saisit ses dépenses et qui était concerné, et à la fin du séjour l'appli fait le bilan. Elle propose un plan de remboursement optimisé pour minimiser le nombre de virements à faire.

Pour ce groupe, on utilisait Tricount, l’appli la plus connue dans ce domaine. Sauf que les noms de tout le monde, qui a payé quoi et quand, en clair sur le serveur de quelqu'un d'autre, ça me plait pas des masses. J'ai pas très envie qu'à la prochaine fuite de données, tout ça se retrouve dans la nature.

# Réinventer la roue, c’est tout un art

Vous allez me dire, "mais ça existe déjà ton truc" et depuis longtemps. Alors oui, et non. Il y a effectivement pas mal d’applis de ce genre, libres, et que tu peux héberger toi même ou pas. D’ailleurs, j’ai vu passer ici des tribunes sur IHateMoney, Cospend, et d’autres. Ça marche plutôt bien, et clairement, les communautés derrière sont nettement plus développées que mes 10 copains et cousins XD. Mais la grosse différence, c’est l’archi. D’un coté, un serveur fait autorité, de l’autre ce sont les clients.

Et mon cas d'usage est grand public. Un groupe de personnes lambda (pas trop geek), sur leurs téléphones, pendant une semaine. Personne ne va créer un compte ou héberger quoi que ce soit. Et moi je ne veux pas risquer de fuiter les données de mes potes. Donc il me fallait un truc qui fonctionne sans compte, sur mobile, hors ligne, avec un serveur incapable de lire quoi que ce soit. Une bonne raison pour réinventer la roue !

# Un mois de vibe coding

Comme tout programmeur qui se respecte, je me suis dit qu'en deux ou trois semaines ce serait plié. En plus, c'était l'occasion idéale de tester enfin cette technique de pointe, le _vibe coding_.

J'ai donc commencé par une grosse séance de psy avec un Claude, pour en sortir une spécification bien détaillée de mon appli parfaite. Le 4 janvier, le premier _commit_ part dans le nuage avec un fichier [`DESIGN.md`](https://github.com/mpizenberg/partage/blob/3e0fa7da5acd8cfc5538c95d4f78fbb57ba46d44/DESIGN.md) qui couvre la vision, l'architecture, la technique, les fonctionnalités. Tout y est, ou du moins tout ce à quoi j'avais pensé. Dans la foulée, un [plan d'implémentation](https://github.com/mpizenberg/partage/blob/5c36853fcc0e7dad3c350e48e96468518eca49d1/PLAN.md) sans faille.

Je pars sur du SolidJS et TypeScript pour le client, Loro pour les CRDT, PocketBase pour le serveur. Les premiers jours, j’avance à vitesse grand V. Le LLM termine chaque phase du plan plus ou moins en autonomie. J'interviens de temps en temps, je lance l'appli pour tester localement, je jette un œil au code ici et là. Mais comme l'objectif était justement d'essayer le _vibe coding_, je limite mes interventions au strict minimum.

Quelques jours plus tard, on teste l'appli avec un groupe de copains sur un week-end. Pas de gros bug visible. Juste un petit problème de devise. Franchement, c’est bluffant.

# Le bug qui m'a fait tout jeter

Le _happy path_ en même temps, ça se passe souvent bien. Mais quand on commence à taper dans les cas limites, c’est là que ca devient (plus) drôle. Que se passe-t-il si je supprime une dépense puis que je la rajoute ? Si je crée une dépense au nom d'un membre puis que je supprime ce membre ? Si deux personnes modifient la même dépense hors ligne, puis se resynchronisent ? Et surtout, celui qui m'a achevé : qu'est-ce qui se passe quand un nouveau venu se trompe et prend la place de quelqu'un d'autre ?

Quand on crée un groupe avant de l’envoyer aux autres, on ajoute les noms de tout le monde. Ce sont des membres "virtuels". Puis chacun ouvre le lien d'invitation et revendique son identité : "moi, c'est Marie". À partir de là, le membre virtuel "Marie" et la vraie Marie qui vient d'arriver sont liés, et toutes les dépenses déjà saisies au nom de Marie virtuelle doivent suivre. Et on répète l’opération si Marie rouvre l’appli plus tard sur un nouvel appareil, son ordi par exemple. Autrement dit, une même personne peut avoir plusieurs identifiants au cours de la vie du groupe, et il faut les résoudre vers le bon, celui que je qualifie d’identifiant canonique.

Ce bug de lien canonique des dépenses aux membres, je l’ai corrigé je ne sais combien de fois. Mais comme l’affichage des membres est partout dans l’appli, (dans la vue des activités, l'onglet des soldes, le plan de remboursement, la création d’une dépense, la liste des membres, ..., j’ai du vérifier le bon lien des membres dans une dizaine de sections différentes de l’appli.

Avec mon expérience, j'aurais déjà dû comprendre. Un bug qui se corrige à autant d’endroits, c’est pas un bug, c'est une mauvaise représentation du modèle. Mais au lieu de prendre le problème à sa racine, quelques jours plus tard, je pousse litéralement un _commit_ avec le message "Introduce a new rigorous system for members", avec plus de 1800 lignes dont 600 de tests, et une tonne de doc qui documente les règles de résolution des identifiants de membres écran par écran. Ici j’utilise d’identifiant canonique, là l’identifiant le plus récent, etc. Mais le cauchemard était pas terminé.

- Le 15 janvier : "Fix join page bug resetting the member identity".
- Le 17 janvier : "Fix race condition when joining the group".
- Le 25 janvier : "Fix member metadata updates".
- Le 6 février (tout dernier commit) : "Better handling of canonical member ids in entry modals".

En gros, je me suis fait rattraper par ma dette technique. Avec le LLM, c’était facile de corriger un petit bug ici et là, mais sur l’architecture long terme, il n’y a pas de raccourcis. J’aurais dû maîtriser la modélisation métier, pour identifier la source du problème. Mais bon, plonger dans 23 000 lignes que j’ai pas écrites, c’est pire qu'un collègue qui t’en envoie une _pull request_ de 3 000 lignes à intégrer.

J'ai tenu un mois, puis j'ai craqué.

![Meme "big plans" refactor](https://jamesrwilliams.ca/_astro/big-plans.CyBgbExf_ecFGB.webp)

# Reset, on remet une pièce

Il n'y a pas grand-chose qui me frustre plus que d'utiliser une appli boguée. Donc cette fois, on pose des bases saines et on modélise avant d'écrire. Le 26 février, nouveau dépôt, en Elm. Je vais éviter le pavé mais en gros, avec des fonctions pures, des types immuables, les effets de bord repoussés aux limites, et une modélisation des données par types algébriques, on part en bonne compagnie.

Le cœur de l'appli tient dans cette signature :

```elm
applyEvents : List Envelope -> GroupState -> GroupState
```

C'est un _fold_ de tous les événements signés du groupe, ordonés de manière déterministe, qui fait évoluer l'état. Tout le reste, les soldes, le plan de remboursement, le fil d'activité, l'affichage, est calculé à partir de cet état, et uniquement de lui.

Ce qui règle le bug de résolution d'identité, c’est qu’elle se produit une seule fois, à l'intérieur de cette fonction. A l’intérieur, il y a des `Member.State` qui portent un champ `rootId` identifiant la personne. L'interface ne manipule jamais rien d’autre. Et une seule fonction qui à partir d’un ID et de l’état du groupe, retrouve le nom d’un membre. La _race condition_ du 17 mai disparait aussi parce que l’arbitrage se fait par une fonction pure, sur un ordre total. Aucun appareil n'a besoin de demander à un serveur la vérité. Tous les appareils appliquent la même règle aux mêmes données et arrivent au mêmes conclusions. C'est aussi ce qui permet au serveur d'être tout bête. Une base SQLite qui accumule et relaie des événements chiffrés sans pouvoir les déchiffrer.

Je continue d'utiliser des LLM pour écrire le code sur la V2. Mais l'usage n'a plus rien à voir : je modélise, je décide de l'architecture, je supervise (presque) tout. Je ne qualifierais pas cette V2 d'appli _vibe-codée_, mais je ne vais pas non plus prétendre d’avoir écrit le code à la main.

![Captures d’écran de l’application Partage](https://github.com/user-attachments/assets/c69aec83-395e-4ed5-bb07-68cdbfab6954)

# Un petit tour des questions potentielles

**Est-ce que je peux l'héberger moi-même ?** Oui. Il y a un conteneur préparé en intégration continue qui contient le serveur et le client, tout prêt à être déployé.

**C'est quoi le modèle économique ?** Rien d’autre que les dons pour l’instant, avec un lien dans l'appli. J'ai passé beaucoup de temps à m'assurer que l'instance publique puisse servir des milliers d'utilisateurs sans broncher sur un tout petit VPS, précisément pour que la question ne se pose pas dans l'urgence. Et je monitore un peu.

**Pourquoi Apache-2.0 sur le serveur et pas AGPL ?** Honêtement, j’y ai pas réfléchi des masses. Je voulais juste zéro friction pour l'auto-hébergement. Apache-2.0 est courant en Rust, que j’utilise régulièrement donc je suis resté là dessus.

**Le chiffrement de bout en bout, concrètement ?** La clé du groupe est dans le lien d'invitation, placée dans le fragment de l'URL, donc la partie après le `#` que le navigateur n'envoie pas au serveur. Le serveur voit donc passer des blocs chiffrés et ne peut pas les ouvrir. Le détail est expliqué dans les liens en fin de journal.

**Ça marche sur mon téléphone dégooglisé ?** C'est une PWA, donc ça tourne dans un navigateur normal, sans magasin d'applications ni compte. Je n'ai pas testé sur les systèmes dégooglisés, si quelqu'un s'y colle je suis preneur du retour.

**Pourquoi Elm ?** Parce que c'est un langage stable qui fait bien son taff. Je ne cherche pas la hype du dernier framework JS, je veux que ça tienne la route, sans bug, avec peu de maintenance.

# Pour aller plus loin

- [Pas besoin de compte](https://onpartage.eu/fr/partage-de-frais-sans-compte/) pour utiliser l’appli Partage
- [Tout est chiffré](https://onpartage.eu/fr/comment-partage-chiffre-vos-donnees/) de bout en bout entre les membres du groupe
- [Le client et le serveur sont libres](https://onpartage.eu/fr/application-libre-partage-de-frais/), MPL-2.0 pour le client, Apache-2.0 pour le serveur
- Et l’appli elle est où ? : <https://onpartage.eu/fr/>

# Le mot de la fin

Voilà. Neuf mois, et un bébé qui ne ressemble pas tellement à l'échographie du départ. Merci d'avoir lu jusqu'ici. Si vous essayez l’app, je veux bien des retours, et n'hésitez pas à _partager_.

