Note : pitch en une phrase — PixelFlux est un petit canevas de pixels multijoueur, mais l'essentiel est le pipeline de production autour : builds reproductibles, tests en couches, analyse de la supply chain, delivery en GitOps et documentation vivante. Plan de l'exposé : montrer l'application en marche, puis expliquer comment elle est construite, testée, sécurisée, livrée et documentée.
Note : Les fonctionnalités sont volontairement réduites pour que rien ne détourne l'attention de l'ingénierie. Une grille 200×200, seize couleurs, une synchro en temps réel. En gardant le domaine trivial, ce qui est évalué, c'est le build system, les tests, les analyses de sécurité et le pipeline de delivery.
Note : Inviter le public à ouvrir ik-clicker.com sur leur téléphone pour peindre en même temps — la synchro à plusieurs est plus parlante en direct. Basculer sur le navigateur, l'application déjà ouverte. Déroulé : (1) deux onglets côte à côte ; (2) peindre dans l'un, le pixel apparaît instantanément dans l'autre ; (3) pointer le pied de page : la version vient du build, et l'identifiant du pod change à chaque rafraîchissement car Traefik load-balance sur trois réplicas. Plan B si le réseau flanche : une capture d'écran. Puis revenir aux slides.
Note : Les serveurs axum ne conservent aucun état, donc ils scalent horizontalement sous un autoscaler. Tout l'état du canevas et tous les événements transitent par Redis : une requête de peinture écrit le pixel et publie un événement, chaque réplica y est abonné et le pousse à ses propres navigateurs. C'est ainsi qu'une peinture sur le pod A atteint un spectateur sur le pod B.
Note : Le canevas est minuscule, donc un snapshot coûte peu : un nouveau client récupère /api/canvas une fois, puis suit /api/events pour les deltas. /info expose la version et l'identifiant du pod affichés dans le pied de page. Le front-end est volontairement un seul fichier HTML embarqué : cela garde le conteneur léger et l'application facile à auditer.
Note : L'application ne fait que pousser du serveur vers le client, donc SSE convient mieux que WebSockets — plus simple, compatible avec les proxys et capable de se reconnecter seul. Le pub/sub de Redis est ce qui rend le multi-instance correct : sans lui, une peinture n'atteindrait que les clients du même pod. Le choix est documenté dans l'ADR 0004.
Note : La même architecture Redis qui partage le canevas sert à diffuser la configuration : un changement dans l'admin est écrit dans Redis et publié sur config:events, et chaque réplica recharge ses réglages en direct. La page est sécurisée (pas de mot de passe par défaut, comparaison à temps constant, cookie HttpOnly/SameSite) et la palette peut être imposée côté serveur, pas seulement masquée dans l'UI.
Note : La reproductibilité est le socle sur lequel tout repose. Le flake Nix fige chaque outil à une version exacte, donc un portable et le runner de CI buildent de la même façon. `task` se place au-dessus comme une liste de commandes lisible. Rien dans le projet ne dépend d'un outil installé globalement.
Note : Le conteneur sort du même flake, il n'y a donc pas de Dockerfile distinct qui se périme. Distroless signifie qu'il n'y a littéralement ni shell ni gestionnaire de paquets à l'intérieur, ce qui retire l'essentiel de ce qu'un attaquant utiliserait, et il tourne en utilisateur non-root. Un check de CI échoue dès que l'image dépasse 20 Mo, ce qui nous oblige à rester sobres.
Note : Chaque niveau attrape une classe de bugs différente. Les tests unitaires couvrent la logique ; les tests d'intégration démarrent un vrai Redis dans un conteneur pour ne pas mocker la dépendance la plus risquée ; Hurl valide le contrat HTTP de bout en bout ; k6 donne un premier signal de performance. Les quatre s'exécutent via `task`, et les trois premiers tournent en CI.
Note : La CI réutilise l'environnement de développement exact, donc un pipeline vert signifie que le projet marche vraiment, pas qu'il marche dans une image de CI sur mesure. Les jobs reflètent le gate local. Le job container verrouille la qualité et, sur main, publie l'image que la production récupère. Docs et slides se déploient sur Pages depuis le même run.
Note : La supply chain est traitée comme une priorité. On produit un SBOM, on scanne l'image pour les CVE connues et on bloque les plus graves, et on cherche les secrets commités en local comme en CI. Comme les crates, les outils et même le digest de l'image en production sont pinnés, ce qu'on teste est exactement ce qu'on livre.
Note : La delivery est en mode pull. Je pousse sur main, la CI publie une nouvelle image, et Image Updater repère le nouveau digest et met à jour l'Application Argo, qui déroule le déploiement — je ne lance jamais kubectl apply à la main. Aucun identifiant n'est nécessaire car tout est public et le write-back utilise le RBAC interne au cluster. Les mêmes manifests montent le TLS et l'auto-scaling.
Note : Voici la topologie de déploiement. Le trafic entre par Traefik, qui termine le TLS et load-balance en round-robin sur le Service. Le Service vise les trois pods du Deployment ; le HorizontalPodAutoscaler ajoute des pods jusqu'à dix selon la charge CPU. Comme les pods sont stateless, ils dialoguent tous avec Redis, qui détient l'état du canevas et le bus pub/sub — c'est lui qui synchronise les instances.
Note : La documentation vit à côté du code et passe par le même pipeline, elle ne peut donc pas dériver. Les Architecture Decision Records expliquent les compromis, ce qu'un évaluateur veut généralement voir. Même ce deck est du Markdown dans le dépôt, rendu par Marp et publié sur Pages à côté du manuel — c'est l'idée du « slides as code ».
Note : Les garde-fous font du bon chemin le chemin par défaut. On ne peut pas commiter du code non formaté ni un message non conventionnel, et on ne peut pas pousser quelque chose qui ne build pas ou casse les tests unitaires. clippy bloque le build : une erreur, pas un simple conseil. Résultat : un code et un historique cohérents sans compter sur la mémoire.
Note : Annoncer franchement les limites est plus convaincant que de prétendre qu'il n'y en a pas. L'application n'a pas d'auth, c'est donc un canevas ouvert par conception ; la persistance se limite à Redis ; et il n'y a pas encore de stack de métriques. Aucun de ces points n'est difficile à ajouter vu le pipeline déjà en place — ce n'était simplement pas le but de l'exercice.
Note : Conclusion — l'idée à retenir est qu'une petite application peut tout de même démontrer un SDLC complet et de qualité production. Montrer le dépôt, la doc et les slides en ligne, puis ouvrir la discussion. Sujets probables : pourquoi SSE plutôt que WebSockets, pourquoi Nix, et comment la boucle GitOps évite les secrets — chacun a un ADR ou une slide derrière.