01
le défi
IAgency, une académie colombienne de formation à l'IA, faisait tourner sa promotion sur trois outils empruntés : le cours vivait chez Hotmart, les échanges avec les élèves sur WhatsApp, et l'agenda des séances individuelles dans un tableur. Chacun marchait seul ; ensemble, ils ne se parlaient pas. Personne ne pouvait dire, à un instant donné, où en était une élève : ce qu'elle avait vu du cours, ce qu'elle avait demandé, quand elle avait rendez-vous.
La commande initiale tenait en une phrase : un écran de connexion avec un lien vers le cours hébergé sur Hotmart. Elle a grandi à mesure que chaque outil emprunté montrait sa limite : un contenu qu'on ne peut pas ouvrir cohorte par cohorte, une conversation professionnelle mêlée aux messages personnels, un rendez-vous encaissé à la main.
Deux contraintes cadraient le reste, et elles n'ont pas bougé : les élèves entrent depuis leur téléphone, et l'hébergement est un mutualisé Hostinger, sans conteneur, pas de build sur le serveur, pas de node_modules dans le paquet.
02
la solution
Une application web privée, conçue et développée de zéro, où le contenu, la messagerie, l'agenda avec encaissement et les avis vivent au même endroit. L'élève y voit son cours, ses classes en direct, les enregistrements de ses séances individuelles, ses objectifs et son certificat ; l'équipe gère tout cela depuis le même portail, sans jamais toucher à la base de données.
La décision structurelle est un contrat : un seul fichier OpenAPI décrit les 116 opérations de l'API, et Orval en génère deux paquets que personne n'édite à la main : les schémas Zod dont le serveur se sert pour valider l'entrée et façonner la sortie, et les hooks TanStack Query que consomme le front. L'API et l'écran ne peuvent plus diverger en silence : le typecheck tombe avant l'utilisateur.
La deuxième règle, écrite dans le code, est que le portail appartient à la cliente : aucune intégration ne doit pouvoir le faire tomber. Google, l'assistant, le courrier, le push et le paiement éteignent leur fonction, l'écrivent à l'écran, et le reste continue. C'est vrai des sept.
03
le résultat
En production, avec de vraies élèves. Hotmart, WhatsApp et le tableur de l'agenda ont été retirés : le contenu propre s'ouvre cohorte par cohorte à date fixe ou « X jours après le début de ta promotion », la messagerie porte les pièces jointes derrière la session, et une séance avec un coach se réserve et se paie sans quitter la page.
À ce jour : ~34 000 lignes de TypeScript, 37 tables, 116 opérations d'API, 123 handlers Express répartis sur 18 routeurs, 107 commits en quatre mois.
Ce que j'en retiens tient surtout dans les pannes. Un hébergeur qui suspend la boîte mail pour « activité suspecte », sans intrus : c'était le portail lui-même, une annonce partant en trente envois simultanés. Une vérification de marque Google échouée deux fois parce que le robot téléchargeait une SPA vide derrière un écran de connexion. Un `drizzle-kit push --force` qui a proposé de vider la table des élèves. Chacune a laissé une règle écrite quelque part : dans le code, dans le package.json, dans le document de contrôle qu'on lit avant de toucher à quoi que ce soit.