Nous avons choisi React pour apprendre. L’IA a écrit le code à notre place.

staffpuzzle v2
Author : Arthur Bréant
Categories : développement, shiny
Tags : shiny, staffpuzzle
Date :

Journal d’une refonte, épisode 1 sur 4.

En février 2026, nous avons choisi React pour refondre un outil interne, en sachant que ce serait deux fois plus long que de le faire en R. C’était un arbitrage assumé : nous n’achetions pas une application, nous achetions une montée en compétence.

En juin, à mi-parcours, deux jalons déjà livrés, nous avons tout arrêté et sommes revenus à R.

Voici les trois raisons. Une seule est « nous nous étions trompés ». Celle qui a emporté la décision est plus dérangeante que ça : l’apprentissage que nous payions au prix fort n’avait tout simplement pas eu lieu, parce que le code arrivait plus vite que nous ne pouvions le lire.

StaffPuzzle est notre outil interne de staffing : qui travaille sur quoi, quelle demi-journée. La v1 est une application Shiny en lecture seule, mille deux cents lignes, qui tourne depuis des années.

staffpuzzle v1

La v2 devait ajouter l’écriture dans Google Calendar, un mode simulation et des indicateurs historisés. Nous avons pris deux décisions, à quatre mois d’intervalle, et la seconde a défait la première. Les deux sont écrites, avec leurs critères. C’est ce qui rend l’histoire racontable, et c’est aussi, on va le voir, ce qui l’a rendue possible.

Cette série raconte les quatre mois qui ont suivi, en quatre épisodes. Elle se termine sur le compte réel : ce que la refonte a coûté, ce que l’hébergement coûte, et ce que la licence évitée valait.

19 février : le choix de React

Trois options sur la table : tout en JavaScript (React, Supabase), tout en R (HTMX brut sur plumber2 brut), ou un hybride. Le tableau de critères était explicite, et il vaut la peine d’être lu tel qu’il a été écrit :

Critère Option A : React Option B : R Quatre mois après
Apprentissage frontend moderne Fort Faible n’a pas eu lieu
Time-to-market ~19-24 demi-journées ~12 demi-journées tenait
Réutilisation de l’existant R Aucune Totale tenait
Transférabilité vers d’autres projets web Forte Faible mauvais critère
Interactivité riche Naturelle Difficile factuellement faux

Regardez la deuxième ligne. Nous savions que React coûterait deux fois plus cher, et nous l’avons choisi quand même. Ce n’est pas de la naïveté : c’est un arbitrage assumé. L’équipe fait six personnes, elle maîtrise R et n’a presque aucune exposition aux stacks web modernes ; la v1 fonctionne, il n’y a pas d’urgence. L’Architecture Decision Record* (ADR) le dit sans détour : le confort de l’équipe en React est « junior », et c’est « un investissement délibéré, pas un acquis ».

Autrement dit, la décision de février n’achetait pas une application. Elle achetait une montée en compétence, et acceptait de la payer en délai.

Architecture Decision Record : un document destiné à entériner un choix d’architecture logicielle significatif.

L’option qui n’était pas dans le tableau

Vous l’avez peut-être remarqué : « continuer en Shiny » n’y figure pas. Les trois options examinées étaient du tout-JavaScript, du tout-R en HTMX, et un hybride. Aucune ne consistait à garder l’outil tel qu’il était et à lui ajouter ce qui lui manquait.

Ce n’est pas un oubli. La question avait été tranchée en amont, dans le cahier de cadrage rédigé un mois plus tôt, sous la rubrique « Contraintes techniques » :

Stack cible : Exit Shiny → architecture REST, site web classique.

Inscrite là, au même rang que les quotas de l’API Google et la gestion des jetons OAuth. Or les quotas de Google sont une contrainte : ils s’imposent à nous. « Exit Shiny » est une décision. La placer parmi les contraintes, c’est la soustraire au débat. On ne discute pas une contrainte, on fait avec.

Rien n’obligeait à quitter Shiny. Écrire dans un agenda, simuler, historiser des indicateurs : Shiny sait faire tout ça. L’ADR de février a donc comparé consciencieusement trois options à l’intérieur d’un périmètre que personne n’avait justifié. Et en juin, quand nous avons rouvert le dossier, nous avons réexaminé chaque critère un par un sans contester le périmètre.

Ce qui compte pour la suite. Une décision justifiée par l’apprentissage n’est valide que si l’apprentissage a lieu. C’est une hypothèse, pas un acquis, et elle se vérifie.

3 juin : le demi-tour

Quatre mois plus tard, deux jalons livrés en React, l’équipe rouvre le dossier. Il en sort une seconde décision qui annule la première. Trois choses avaient bougé, et elles ne sont pas de même nature.

Un : l’option refusée n’existait plus

En février, l’option R évaluée était HTMX brut sur plumber2 brut. Écrire du HTML à la main, poser les attributs à la main. C’était une critique juste, et elle avait pesé.

Entre-temps, deux paquets R ont été publiés, {htmxr} et {alpiner}, qui donnent à cette approche les abstractions R de haut niveau qui lui manquaient. L’option B de juin n’est pas l’option B de février. Ce n’est pas la même chose que d’avoir eu tort : le monde avait changé, et la décision n’avait pas de mécanisme pour s’en apercevoir toute seule.

Deux : deux arguments étaient simplement faux

Le tableau de février donnait l’interactivité riche comme « difficile » côté HTMX, avec le glisser-déposer en exemple. En juin, quelqu’un est allé lire le code React effectivement écrit : il n’y a pas de glisser-déposer. C’est une sélection au lasso, précisément ce qu’{alpiner} sait faire nativement. L’argument protégeait une difficulté qui n’existait pas.

Le second demande plus de nuance. Le critère disait : compétences React « fortement transférables vers d’autres projets web », compétences R peu. C’est exact, et c’est le mauvais critère. Il mesure ce qu’un développeur emporte sur son CV, pas ce qu’une entreprise capitalise. Pour une maison dont le métier est R, approfondir R n’est pas une transférabilité faible : c’est l’actif principal qui s’épaissit. Maintenir des paquets candidats au CRAN nous rend plus visibles et plus compétents que ne l’aurait fait une connaissance de surface de React.

Trois : l’apprentissage n’avait pas eu lieu

C’est celui qui fait mal, et c’est celui qui a emporté la décision. L’ADR de juin l’écrit en une phrase :

L’apprentissage React, motivation centrale de la décision de février, ne se réalise pas en pratique : le développeur n’a pas le temps de relire le code généré par l’IA, donc l’objectif de montée en compétence n’est pas atteint.

ADR-003, 3 juin 2026

Il faut mesurer ce que ça dit. La justification unique du surcoût de quatre mois était la montée en compétence. Le code a été écrit, l’application fonctionnait, les jalons étaient livrés. Et le bénéfice qui payait le surcoût, lui, ne s’était pas produit.

Assistée par une IA, une décision de stack justifiée par l’apprentissage peut se saborder elle-même. Le code arrive plus vite qu’on ne le comprend, la livraison avance, et l’illusion tient jusqu’à ce que quelqu’un demande à voir ce qui a été appris. Le sujet dépasse largement cet article et nous y reviendrons ; il suffit ici de constater qu’il a changé une décision technique à mi-parcours.

Ce que ça a coûté : le prix du demi-tour

Un revirement n’est pas gratuit, et l’ADR de juin liste ses propres conséquences défavorables avant de conclure.

La première n’est pas technique :

  • Refaire la communication avec les utilisatrices internes. Changer de cap technique en cours de projet, devant des gens qui attendent un outil, demande une explication qu’on préférerait ne pas avoir à donner.
  • Stabiliser des paquets encore expérimentaux pour les mettre en production.
  • Le facteur bus : l’écosystème retenu a un mainteneur principal, et c’est la même personne que le développeur du projet.
  • Sauver le capital documentaire du code React avant de le supprimer : les types et les tests décrivaient un modèle de données qui, lui, restait valable.

Le calcul, lui, était favorable : six à huit demi-journées pour tout reprendre, contre huit à dix pour finir en React. Une preuve de concept écrite dans la foulée, deux cent quatre-vingts lignes de R reproduisant l’écran principal à soixante-dix millisecondes par requête, a transformé cette estimation en engagement.

Ce sont les chiffres qui ont convaincu. Ce n’est pas eux qui ont décidé.

Trois mois après : où on en est

L’application tourne en production, écrit dans les agendas, historise ses indicateurs. Le ratio de tests sur code est passé de 0,26 à 0,77 : cinq cent quarante-quatre tests, aucun navigateur, un seul langage du calendrier jusqu’à l’écran.

C’est le gain qu’on n’avait pas anticipé. L’interface étant devenue une fonction pure de données vers du HTML, elle se teste comme le reste du code, sans navigateur ni capture d’écran. Nous avions justifié le retour par la cohérence de langage ; ce que nous avons obtenu de plus précieux, c’est la testabilité.

Et un bénéfice qui n’était dans aucun tableau : l’application est devenue le premier cas de production réel de nos propres paquets, ceux de l’hyperverse, ce qu’aucun exemple de démonstration n’aurait donné.

staffpuzzle v2

Ce qu’on retient

Écrire ses décisions permet de les défaire. C’est la leçon principale, et elle est presque banale jusqu’à ce qu’on en ait besoin. En juin, personne n’a eu à reconstituer de mémoire pourquoi React avait été choisi : les critères étaient posés, datés, pondérés. Il a suffi de les repasser un par un et de constater lesquels ne tenaient plus. Sans ce document, la discussion aurait porté sur les personnes, sur qui avait eu raison, au lieu de porter sur les critères.

Distinguer « le monde a changé » de « nous nous sommes trompés ». Sur trois raisons, une seule est une erreur de jugement : l’argument du glisser-déposer, qu’une lecture du code aurait démenti dès février. Les deux autres sont d’une autre nature : un outil est apparu, et une hypothèse sur nous-mêmes s’est révélée fausse. Confondre les trois produit soit de la culpabilité inutile, soit l’incapacité de revenir en arrière.

Une décision justifiée par un bénéfice non technique doit programmer sa vérification. Février achetait de l’apprentissage. Personne n’avait prévu de vérifier qu’il arrivait. Nous l’avons découvert par accident, quatre mois plus tard, en rouvrant le dossier pour une autre raison. C’est la seule chose que nous ferions différemment : si un critère décide, il doit avoir une date de contrôle.

Et si c’était à refaire. Nous choisirions probablement React à nouveau, en février 2026, avec ce que nous savions alors. C’est ce qui rend l’histoire intéressante : le demi-tour n’est pas la correction d’une bêtise, c’est le fonctionnement normal d’une décision qu’on a pris la peine d’écrire.


Votre application Shiny a grandi, et vous vous demandez quoi en faire ? Nous la lisons et nous vous rendons une note qui le dit, y compris quand la réponse est « n’y touchez pas ». Écrivez-nous en deux lignes : la taille de votre application, et depuis combien de temps elle tourne.


Journal d’une refonte, quatre épisodes :

  1. Nous avons choisi React pour apprendre. L’IA a écrit le code à notre place. (vous êtes ici)
  2. Ma première tentative de portage Shiny a échoué, et pas pour une raison technique (29 septembre 2026)
  3. On a quitté la plateforme managée. Voilà la facture réelle. (6 octobre 2026)
  4. Cinq signes que votre app Shiny a dépassé Shiny (13 octobre 2026)

Les deux décisions citées sont des Architecture Decision Records du dépôt StaffPuzzle, datées du 19 février et du 3 juin 2026. Les chiffres sont mesurés sur les branches du dépôt, pas estimés. Divulgation d’intérêt : htmxr et alpiner sont écrits par Arthur Bréant, qui a mené ce projet et maintient ces paquets.


Comments


Also read