Cet article est une histoire qui vous fait vivre notre première expérience de vibe coding. Nous allons partager avec vous les étapes du parcours. La question qu’on voudrait répondre est : est-ce que l’application comme nous l’avons à l’état final aurait pu être créée par un non développeur sans grande maîtrise des technologies sous-jacentes? Nous vous laissons y répondre après avoir vu le parcours par lequel nous sommes passés.
En juillet 2025 j’avais une fête de famille et j’ai proposé d’apporter la pizza. Étant montréalais, mon idée était d’aller chez Amelias qui a repris le restaurant du défunt Amelios, une pizzeria phare dans le ghetto McGill que je visitais au moins une fois par semaine lorsque je faisais mes études graduées à l’université McGill. Amelios a fermé ses portes à l’hiver 2015 à cause de divers problèmes légaux, mais le phoenix est rené de ses cendres en juillet 2015, sous le nouveau nom d’Amelias. Je n’y étais pas allé depuis ce tout ce temps.
J’arrive donc là-bas et je vois un menu similaire à ce que je connaissais. Mes pizzas fétiches : la white pizza, la pizza au bacon, la pizza végétarienne sont toutes là sur le menu. Il y a quelques ajouts, comme la Mike’s Hot Honey Spicey Pepperoni Special. C’était un mardi. Il y avait un spécial : achetez deux pizzas, obtenez-en une gratuite. Je prends donc notre commande classique : white pizza et une pizza moitié végétarienne, moitié bacon (!). Comme il y a un spécial, je décide d’essayer aussi la pizza spéciale de Mike.
Et là c’est le temps de payer. Je sors ma carte de crédit et on me dit que seulement les paiements comptant sont acceptés. Tout d’un coup je me sens reculer dans le temps de 30 ans. Je reconnais les pratiques d’Amelio’s qui n’ont pas changées avec Amelias, la nouvelle génération de cette fabuleuse pizzéria. Heureusement, j’avais de l’argent liquide sur moi, ce qui est très rare en 2025. Et là, la jeune serveuse fait ma facture, à la main sur un bloc d’additions de restaurant. Elle sort sa calculatrice et calcule d’abord la TPS, puis la TVQ et finalement le total. C’est ce qui a été l’étincelle du sujet de cet article : pourquoi ne pas construire une application web progressive, qui pourra fonctionner sans accès à Internet et calculera les taxes de vente du Québec?
Quiconque a suivi mes activités de formation au cours des 30 dernières années, sait que j’utilise ce modèle pour mes cours de développement web. Pour les débutants en JavaScript : pour apprendre à créer des fonctions, manipuler le DOM, séparer la logique, de la présentation, de la structure. J’utilise l’application dans un cours de PHP avec la programmation objet et la mise en place de tests unitaires. Puis, dans le cours de JavaScript avancé, où, sous forme d’une application React, nous ajoutons le concept de taxe inverse, nous pratiquons la séparation en modules et l’API d’internationalisation. Dans le cours sur les API du HTML5, nous revisitons l’application pour la création d’une application web progressive avec les service workers.
Mais nous sommes en 2025, alors l’idée ici fut de plutôt faire développer l’application par l’intelligence articielle (IA) et de la déployer en ligne. J’explique ici comment nous avons procédé.
L’application en question est un exemple parfait d’un développement web qui peut être délégué à l’intelligence artificielle : une application qui peut être 100% client sans besoin de persistance. Nous n’avions jamais expérimenté avec ces technologies avant, mais avons assisté à quelques présentations qui utilisaient l’IA pour des applications intéressantes et de plus grande envergure que celle que nous avons en tête. Nous sommes donc ici dans le royaume du vibe coding. Selon Gemini en août 2025
Le "vibe coding" est une méthode de développement logiciel où l’on utilise des grands modèles de langage (LLM) et l’intelligence artificielle pour générer du code à partir de simples descriptions en langage naturel, souvent sans être un expert en programmation. L’IA prend en charge la génération et l’écriture du code, permettant aux utilisateurs, même novices, de se concentrer sur les idées et l’expérience utilisateur, transformant le développement en un processus plus intuitif et rapide.
Mes lectures de l’été m’avaient fait découvrir ces différents systèmes de création d’applications :
Nous cherchions un système qui permette de faire générer l’application et d’avoir accès au code source pour après la faire évoluer au besoin. Après avoir essayé les trois premières (Bolt, V0 et Lovable), notre choix s’est arrêté sur Bolt. Nous ne voulons pas dire que Bolt est le meilleur des systèmes, nous voulons juste présenter le chemin par lequel nous sommes passés pour finaliser notre application et la rendre disponible pour des gens comme le personnel d’Amelias. Dans notre équipe nous avons un formateur avec une bonne connaissance de la gestion de projet et le développement Web (l’auteur de cet article), un développeur full stack et devops aguerri, un intégrateur/concepteur graphique avec une grande expérience avec CSS et HTML, mais allergique au développement JavaScript.
Comme j’avais déjà multiples versions d’applications qui calculent les taxes de vente, lors de mon premier prompt je me suis dit, poussons un peu plus loin et demandons à ce que l’application puisse permettre optionnellement d’ajouter le pourboire.
J’étais en rencontre Zoom avec mon développeur full stack/devops et lui ai dit : « Tiens, on va essayer ça pour le fun ». J’ouvre donc Bolt pour lui montrer, et je réponds très succinctement à la question What should we build today? sur son écran d’accueil :
Benoit
Tax calculator for Quebec with the possibility of including tip.
Bolt
[Réception de l’application avec possibilité de sauver dans GitHub.]
Quelques minutes après, j’avais mon application. Mais là, nous sommes au Québec, alors je me suis dit : il faut que l’application puisse être utilisée en français aussi. Rapidement, j’ai donc ajouté un deuxième prompt pour faire modifier l’application :
Benoit
I would like to add the possibility to have it either in French or English.
Bolt
[Réception de l’application modifiée.]
Quelques minutes plus tard j’avais une application, ma foi, très acceptable (voir Figure 3). La conversation avec mon développeur s’est terminée ainsi et là j’ai réfléchi sur ce qui venait de se passer.
Quelques jours après, nous nous rencontrons à nouveau mon développeur et moi pour faire le suivi des travaux réguliers. Bolt offre la possibilité d’amener l’application dans GitHub et aussi de la déployer sur Internet. Je décide donc de demander à mon développeur de refaire le processus de création de l’application sur plusieurs plateformes de vibe coding, de voir si on peu récupérer le code et de voir comment le déploiement proposé fonctionne. Je lui demande de voir aussi comment nous, de notre côté,nous pouvons reprendre l’application et l’amener dans nos flux de déploiements habituels.
Je lui donne cependant un nouveau défi : prendre mes prompts originaux, mais aussi de voir pour que l’application créée soit une application web progressive ( progressive web app ou PWA). Pour ceux qui ne savent pas, avec une PWA, une fois que j’ai accédé à l’application avec accès réseau, elle est sauvegardée en cache pour être utilisée même si nous n’avons pas d’accès réseau. Je me doute bien que la serveuse d’Amelias a un téléphone intelligent, mais il n’est pas évident qu’elle aura un accès Internet en tout temps et je veux qu’elle puisse accéder à l’application même si elle n’a pas d’accès réseau.
Le développeur fait donc reconstruire l’application à partir des prompts originaux et rajoute ce prompt qu’il a copié de ce que Bolt proposait dans sa librairie de prompts :
Stéphane
Create a complete web app manifest file to make my website installable as a Progressive Web App. Include appropriate icons, theme colors, display preferences, and orientation settings. Generate the necessary HTML code for linking the manifest and verifying proper implementation.
Bolt
[Réception de l’application avec possibilité de sauver dans GitHub.]
Au niveau des plateformes de vibe coding, en bout de ligne, elles sont pas mal toutes pareilles pour ce genre d’application. Elles utilisent les mêmes cadres applicatifs (React, TypeScript, Tailwind, Vite) à part V0 qui base son code aussi sur Next.js. Finalement nous avons gardé le code de Bolt.
Nous nous ramassons donc avec une application faite en React avec TypeScript. Le système
de packaging est Vite (et non Webpack). Pour les CSS et la grille adaptative, c’est
configuré avec Tailwind. Le code comporte principalement deux composantes :
TaxCalculator.tsx
et LanguageToggle.tsx
.
Pour la publication, la plupart des plateformes proposent d’héberger le site, soit sous leur sous-domaine (.bolt.host par exemple) ou bien d’amener son propre domaine.
Les plateformes proposent toutes de déployer dans GitHub. Dans notre cas, nous avons intégré le code à notre compte GitHub et puis là, nous avons rajouté une action pipeline pour déployer ça sur notre site Web.
Pour l’application web progressive, ça a été moins bien. La plateforme a généré le
manifeste, le code et tout. Ça semblait beau, mais la partie cache, le cœur pour être
capable d’accéder lorsqu’on est hors ligne ne marchait pas. Notre développeur a essayé de
comprendre pourquoi, puis il a décidé d’arrêter de passer du temps là-dessus. Il a tout
supprimé ce qui était lié à cet aspect et a repris la configuration lui-même. Ça lui a
pris cinq minutes et ça a marché tout de suite. En bout de ligne, pour rajouter ce genre
de fonctionnalité avec laquelle il était déjà très à l’aise, il a perdu beaucoup de temps
à comprendre pourquoi ça ne marchait pas. Tout était là, rajouté par dessus, mais pas bien
intégré. Pour que ça marche il faut faire un service
worker et passer par une librairie comme workbox. Ce n’était pas bien intégré
avec Vite, la confusion était là. Toute la configuration était là, c’était une bonne base.
Il a fallu chercher comment intégrer une PWA avec Vite. Nous avons repéré le plugin
vite-plugin-pwa
, et c’est ce qu’on a intégré et c’est là que ça a
marché. Le plugin sert à lister les resssources à mettre en cache et configurer le prompt
d’installation. Nous n’avions pas le choix d’avoir un plugin parce qu’il y a des fichiers
générés et ce plugin sert à bâtir la liste de ces fichiers.
Durant l’été 2025 nous avons accueilli un stagiaire. Il travaillait sur un autre volet de débroussaillage avec l’IA. Comme il n’était pas développeur, je lui ai parlé de notre expérience et lui ai montré l’application. Je me suis rendu compte d’une chose et lui d’une autre :
Ce sont donc ces deux raffinements que nous ferons dans notre troisième itération.
Pour créer les icônes, j’ai donné un défi à notre concepteur graphique. Il avait une contrainte : il fallait que les icônes soient créés par l’IA. Ceci sera le sujet d’un autre article, mais à terme il a choisi celui créé par Looka. Normalement il aurait dû falloir payer pour l’avoir et surtout avoir toutes les variations possibles (pour l’écran, pour des produits physiques comme des t-shirts, etc), mais il a été en mesure d’extraire le logo.
Les logos ont été donnés au développeur et il les a intégrés dans l’application. À ce niveau, pas vraiment d’intérêt de passer par l’IA, mais c’est une possibilité qu’on pourrait explorer. La capture de la Figure 4 montre ce que ça donne.
La raison pourquoi l’application ne mettait pas le pavé numérique par défaut sur un téléphone est très simple, les champs de formulaire (pour le sous-total et le montant du pourboire) avaient un type texte au lieu d’un type nombre. Dans la composante React, il fallait changer ceci
<input type="text" value={amount} onChange={handleAmountChange} placeholder={t.subtotalPlaceholder} className="..." />
par celà
<input type="number" value={amount} onChange={handleAmountChange} placeholder={t.subtotalPlaceholder} className="..." />
C’est une opération triviale pour un développeur qui sait comment faire le travail et comme nous avons accès au code, c’est ce que nous avons fait. Si l’utilisation de l’IA aurait été pour du faire du no-code, alors là il aurait fallu procéder différemment et avec un autre système demander de changer l’application ou sinon il aurait fallu le spécifier dans le prompt original.
Après le quatrième test en conditions réelles, je me suis rendu compte des lacunes de l’application. Le logo de l’application n’était pas dans l’écran, il manquait les metadonnées Open Graph, il manquait la fonctionnalité de taxes inverses et les captures d’écrans de l’application pour les places de marché d’applications n’étaient pas disponibles.
L’application fut construite avec un caractère de police icône sous forme de calculatrice en guise de logo. Or, nous avons fait fait un logo par IA générative pour mettre dans le manifeste et voir lorsqu’on sauvegarde sur l’écran d’accueil. Pourquoi ne pas pousser plus loin l’affichage de l’image de marque de notre application en intégrant le logo dans le rendu de l’écran principal? Le gabarit de l’application a donc été changé pour mettre le logo. C’est l’intégrateur qui l’a fait.
Le changement de logo a amené une deuxième étape qui était l’ajustement des couleurs pour s’harmoniser avec le bandeau qui avait été ajouté.
Si les gens mettent l’URL de l’app sur les réseaux sociaux, nous aimerions bien contrôler les cartes enrichies. Le manifeste contenait déjà pas mal toute l’information que nous avions besoin. Nous le changeons légèrement, puis dans Gemini, nous avons demandé
Benoit
Can I have the OpenGraph metadata based on this manifest? [contenu de manifest.json inclus]
Gemini
[Donne le code pour Open Graph mais les URLs ne sont pas corrects.]
Mais là en regardant le résultat proposé, nous nous sommes rendus compte que le URL de l’application n’avait pas été fourni, alors ce n’était pas 100% correct. Nous avons donc rajouté
Benoit
URL for the app is: https://apps.hubformation.ca/taxes/
Gemini
[Donne le code pour Open Graph avec les bons URLs.]
Après ces deux petits prompts, j’ai mes données OpenGraph. J’inclus ça dans le code, je teste. Tout est bien beau.
Le quatrième test en conditions réelles m’a convaincu que l’application avait besoin de permettre de mettre le total et non le sous-total. En premier lieu, je l’ai fait moi-même à partir du code existant. J’étais un peu rouillé, alors ça m’a pris une heure et demie. Mais ceci m’a permis de savoir quoi mettre dans le prompt pour l’IA générative. J’ai ensuite demandé à Gemini de me refactoriser la composante. Il a fallu quelques clarifications mais en moins de 10 minutes j’avais un code fonctionnel. Voici les prompts que j’ai mis:
Benoit
Can you add the concept of inverse tax on this Quebec tax calculation component? I need a toggle for an inverseTax boolean (with label "Taxes inverse" in French and "Inverse tax" in English). The label for the amount input box needs to be "Sous-total (avant taxes)" for the forward calculation and "Total (après taxes) for the inverse tax calculation. Here is the original code of the component: [contenu de TaxCalculator.tsx inclus]
Gemini
[Réponse reçue mais code incorrect car le pourboire était inclus dans le calcul inverse. De plus les traductions ne sont pas dans un fichier à part.]
Benoit
The total should not include the tip.
Gemini
[Réponse reçue. Je la regarde, mais pas avec assez d’attention, et je crois qu’il se trompe. La raison pourquoi je fais cette erreur est que le code pour le calcul du pourboire est dupliqué et ça m’induit en erreur.]
Benoit
No, the tip should always be calculated from the subtotal, but the amount is either the subtotal (forward calculation) or needs to be calculated from the amount which represent the total (inverse tax). The grand total is the sum of the total including taxes and the tip amount.
Gemini
[Là il se choque et au lieu de me donner le code, il me donne les étapes de ce qu’il ferait. Je vérifie (c’est quand même assez long) et ça me semble convenable. Alors je lui demande le code de la composante et j’en profite pour lui demander de séparer les traductions dans une composante à part.]
Benoit
Can you provide with the code implementing what is written. Note that the list of translations are in the language.ts file at the moment: [contenu de langage.ts inclus]
Gemini
[Et là il me donne un code qui a un sens avec les traductions mises à jour dans un fichier à part.]
J’essaie le code et c’est fonctionnel du premier coup. J’aime l’idée d’avoir ajouté une variable pour le total avec taxes et l’explicatif quand la taxe inverse est choisie, une initiative qu’il a prise sans que je lui demande. Par contre, le code contient des duplications et je n’aime pas l’approche.
Au lieu de lui demander de le faire à nouveau, je décide d’aller du côté de l’assistant IA de JetBrains (nous aurions pu aussi utiliser VSCode avec Copilot). Au moment de l’essai, le mode Code n’était pas disponible pour moi, alors j’ai du travailler avec le mode Chat seulement. Le premier essai avec le modèle openai-gpt-4o fut plutôt décevant car il a fait une grosse erreur au niveau du calcul inverse. Le code proposé ne s’intégrait pas directement et les consignes étaient mal suivies. Je perdais du temps finalement. Avec le modèle google-chat-gemini-pro-1.5, c’était beaucoup mieux. Le code était bien, sans duplication et les instructions étaient bien suivies. Il manquait l’explicatif quand la taxe inverse est choisie (Gemini me l’avait proposé d’emblée), alors je lui ai juste demandé de faire l’ajout. C’est le code que j’ai décidé de mettre dans notre application en bout de ligne.
Pour avoir une belle présentation lors du téléchargement dans les magasins d’applications, nous voulons avoir des captures d’écran de l’application. Nous faisons ces captures et nous les ajoutons à la base de code.
Tout excité d’essayer notre nouvelle création, je fais un premier test. Je vais chercher des pitas au restaurant grec du coin et en attendant ma commande je cherche l’URL de l’application (je ne l’ai pas encore mise du mon écran d’accueil, elle traîne dans un message Slack) et je constate que le total avec le pourboire ne correspond pas. Le plat était de 11,95 $ et j’ai mis un pourboire de 1,80 $ et j’avais un total de 15,54 $ et sur la facture du restaurateur j’avais un total 15,60 $. J’ai gardé la facture et je me suis dit que je validerais au retour au bureau. Mais en fait je me suis rendu compte que la serveuse n’avait pas mis 11,95 $ pour le sous-total mais bien 12 $, ce qui faussait les calculs faits avec l’application. En faisant tout ça, je me demande s’il ne serait pratique d’avoir aussi le calcul la taxe inversée. Ce test a été fait avant la deuxième itération.
Une deuxième occasion s’offre à moi. Nous allons à un restaurant italien tout près de chez moi. Mon app est bien en place sur le téléphone cette fois-ci : elle est sauvegardée sur mon écran d’accueil avec la belle icône générée par l’IA. La serveuse vient avec le terminal de paiement. J’ai mon application à côté et je veux tester le pourboire. J’ai l’habitude de ne pas prendre les boutons préprogrammés et de mettre moi-même le montant, mais maintenant que je veux tester l’app, je choisis plutôt de mettre le pourcentage de pourboire, comme je fais dans l’application. Les montants ne concordent toujours pas. Je questionne la serveuse pour comprendre comment le calcul se fait sur le terminal. Le sous-total était de 86,00 $, le total avec les taxes de 98,88 $. Je demande de mettre un pourboire de 15% (le service était très lent...) et le résultat fut de 14,83 $ pour le pourboire. L’application, elle, calculait un pourboire de 12,90 $, ce qui était le montant correct. La serveuse voyant mon désarroi, m’a expliqué que lorsqu’on entrait le pourcentage nous-mêmes, le terminal appliquait le pourboire sur le total et non le sous-total. Les boutons des suggestions de pourboire, eux, ne faisait pas cette erreur. Il y a raison de croire que le terminal est déviant suite à la politique des pourboires pour le Québec apportée le 7 mai 2025. La bonne nouvelle : l’application fonctionne et ... elle est utile pour ceux qui veulent les montants justes. Ce test a été fait après la troisième itération.
Une troisième occasion s’offre à moi : un samedi matin d’août, je vais à la boulangerie pour acheter des viennoiseries. Je prends un croissant et une danoise à la cannelle. Je mets le sous-total dans l’app pour voir le total avec les taxes. Les montants ne correspondent toujours pas à la facture qu’on me remet : celle-ci a un montant en deça de ce que l’app calcule. En fait, la raison est que le croissant est non taxable et la danoise l’est. Oups! Ça complexifie les choses. Premièrement, rien n’indique sur la facture ce qui est taxable et ce qui ne l’est pas. Mais même si ce l’était (comme j’ai sur ma facture d’épicerie), avoir à taper tous les montants d’une facture à plusieurs items demanderait une application beaucoup plus lourde sous la forme d’une calculatrice avec la possibilité de dire pour chaque montant s’il correspond à un item taxable ou non.
Une quatrième occasion de test se présente. Je vais dans une chaîne de restauration rapide. À l’écran je ne vois pas le sous-total, seulement le total. Dans cette situation, je me rends compte que clairement l’application doit avoir une fonctionnalité de taxe inverse. Le sous-total est sur ma facture, mais je ne l’obtiens qu’après avoir donné le montant de pourboire. Ce test a été fait avant la quatrième itération. Une fois la version avec la taxe inverse déployée, je suis retourné à ce restaurant pour un repas à emporter. Eux aussi ont un terminal de paiement qui n’est pas conforme à la politique des pourboires. Le total avec les taxes était de 34,58$, l’application donne alors que le sous-total devrait être 30,08$ alors le pourboire de 10% devrait être 3,01$ or le terminal a mis 3,46$.
Le résultat final de l’application est maintenant accessible sur apps.hubformation.ca/taxes.
Pour nous, la construction de l’application était notre premier essai avec les générateurs de code et les assistants. Ce qu’on peut conclure est qu’il faut s’assurer d’avoir un bon prompt originel. Après, si on veut sauver du temps, il faut aller se chercher des outils pour aider la refactorisation ou l’évolution de l’application. Nous avons travaillé avec l’assistant IA de JetBrains mais ça aurait très bien pu être VSCode avec Copilot.
Il reste qu’une maîtrise des technologies sous-jacentes à la création du code sont nécessaires pour arriver à se sortir du trou lorsque l’application produite est déviante. Même pour faire construire le bon prompt originel, ça demande une bonne connaissance de la technologie et du problème à résoudre. Quand nous avons cette connaissance, avec les bons outils, nous pouvons grandement améliorer notre productivité. C’est donc une bonne idée d’expérimenter, de se familiariser avec ce qui existe et de développer nos compétences dans l’utilisation de ces technologies. Au début, il faut prévoir de perdre du temps, mais éventuellement nous devrions monter en vélocité. Mais maintenant nous sommes en selle et je prévois que plus le temps avancera, plus notre équipe sera efficace, sutout pour les développeurs un peu moins expérimentés.
Pour terminer, juste mentionner que quelques minutes avant de publier cet article nous avons refait un prompt beaucoup plus élaboré qui reprenait toutes les leçons apprises dans notre parcours. L’application créée était presque parfaite. C’est un exercice que nous ferons faire à nos étudiants, alors nous ne mettons pas le prompt final dans cet article. C’est aussi un bel exercice à faire par le lecteur...
Les cours que nous donnons qui couvrent différents aspects sous-jacents à la technologie derrière cette application sont :
Nous n’avons pas de cours de TypeScript ou de Tailwind pour le moment, mais ces sujets sont couverts à un très haut niveau dans le cours Technologies Web : les essentiels.