Le 27 mars 2026, sh0 est passé d'une plateforme de déploiement avec un CLI basique à une plateforme de déploiement avec une expérience développeur complète. Seize nouvelles commandes. Deux nouveaux endpoints serveur. Un système de streaming WebSocket. De la documentation sur trois surfaces en cinq langues. Six sessions d'audit indépendantes. Zéro bug connu à la fin.
Cet article raconte l'histoire complète -- pas les détails d'implémentation (ceux-ci sont dans les articles précédents) mais la méthodologie, la chronologie, les décisions et les leçons apprises.
Le point de départ
sh0 avait déjà un CLI. Dix commandes construites dès le premier jour du projet, couvrant l'essentiel : serve, setup, status, deploy, logs, env, ssh, check, templates, compose, et d'autres. Ces commandes reproduisaient le tableau de bord et étaient immédiatement utiles pour le développement et les tests.
Mais elles supposaient toutes que l'application existait déjà sur le serveur. Le développeur devait créer les applications via le tableau de bord, configurer les dépôts Git et gérer les déploiements via l'interface web. Le CLI était une télécommande pour le serveur, pas un outil de développement autonome.
Le manque, c'était sh0 push -- la capacité de déployer depuis un répertoire local avec une seule commande.
La chronologie
| Session | Phase | Ce qui a été fait | Découvertes |
|---|---|---|---|
| 1 | Phase 1 : Build | push, login, whoami + endpoint serveur | -- |
| 2 | Phase 1 : Audit R1 | Revue de 2 600 lignes | 3 Critiques, 6 Importants, 5 Mineurs |
| 3 | Phase 1 : Audit R2 | Vérification des corrections, revue fraîche | 2 Importants, 4 Mineurs |
| 4 | Phase 2 : Build | init, link, open, config | -- |
| 5 | Phase 2 : Audit | Revue du code Phase 2 | 0 Critique, 1 Important, 2 Mineurs |
| 6 | Phase 3 : Build | restart, stop, start, delete, domains | -- |
| 7 | Phase 3 : Audit | Revue du code Phase 3 | 1 Critique, 0 Important, 4 Mineurs |
| 8 | Phase 4 : Build | watch + streaming WebSocket | -- |
| 9 | Phase 4 : Audit | Revue du code Phase 4 | (inclus dans l'audit global) |
| 10 | Audit global R1 | Revue transversale (3 200 lignes) | 2 Critiques, 5 Importants, 7 Mineurs |
| 11 | Audit global R2 | Vérification des corrections globales | 0 nouvelle découverte |
| 12 | Phase 5 : Docs | Marketing, tableau de bord, 4 pages de docs | -- |
| 13 | Phase 5 : Mise à jour | Synchronisation des docs avec les commandes Phase 2-4 | -- |
Treize sessions. Chaque session opère avec un contexte vierge -- pas de report des sessions précédentes, pas de biais du constructeur.
Ce qui a été construit
Nouvelles commandes CLI (16)
| Commande | Lignes | Phase | Objectif |
|---|---|---|---|
sh0 push | ~580 | 1 | Déploiement en une commande depuis un répertoire local |
sh0 login | ~90 | 1 | Authentification interactive |
sh0 whoami | ~30 | 1 | Afficher l'identité actuelle |
sh0 init | ~120 | 2 | Détection de stack + génération du .sh0ignore |
sh0 link | ~60 | 2 | Lier un répertoire à une app existante |
sh0 open | ~50 | 2 | Ouvrir l'URL de l'app dans le navigateur |
sh0 config | ~100 | 2 | Gérer le fichier de configuration |
sh0 restart | ~15 | 3 | Redémarrer l'application |
sh0 stop | ~30 | 3 | Arrêter avec confirmation |
sh0 start | ~15 | 3 | Démarrer une application arrêtée |
sh0 delete | ~40 | 3 | Supprimer avec confirmation par le nom |
sh0 domains | ~100 | 3 | Sous-commande de gestion des domaines |
sh0 watch | ~130 | 4 | Auto-push sur changement de fichier |
Nouveau code côté serveur
| Endpoint | Objectif |
|---|---|
POST /api/v1/apps/:id/upload | Re-upload vers une app existante |
GET /api/v1/deployments/:id/stream | Streaming des logs de build via WebSocket |
Infrastructure de support
| Composant | Objectif |
|---|---|
Deployment::has_active_by_app_id() | Garde contre les déploiements concurrents |
ApiError::Conflict | Variante de réponse HTTP 409 |
Struct StreamResult | État terminal unifié depuis WS/HTTP |
update_phase_from_log() | Détection de phase de build partagée |
should_ignore_public() | Logique d'exclusion partagée pour push/watch |
Documentation
| Surface | Pages/Commandes |
|---|---|
Page marketing (sh0.dev/cli) | 31 commandes, workflow en 5 étapes |
Page tableau de bord (/cli) | 29 commandes, 5 langues |
| Docs vue d'ensemble | 4 workflows principaux |
| Docs installation | Guide par plateforme |
| Docs push & deploy | Référence complète de push |
| Docs commandes | Référence complète des commandes |
Le tableau de bord de l'audit
| Métrique | Phase 1 | Phase 2 | Phase 3 | Global | Total |
|---|---|---|---|---|---|
| Critique | 3 | 0 | 1 | 2 | 6 |
| Important | 8 | 1 | 0 | 5 | 14 |
| Mineur | 9 | 2 | 4 | 7 | 22 |
| Corrigé | 11 | 1 | 1 | 7 | 20 |
| Tests ajoutés | 1 | 0 | 0 | 0 | 1 |
| Régressions | 0 | 0 | 0 | 0 | 0 |
Six découvertes Critiques sur 3 200 lignes de code. Soit un bug Critique pour 533 lignes. Pour contexte, les études industrielles estiment une vulnérabilité de sécurité pour 1 000 à 2 000 lignes de code en production. Le processus d'audit a trouvé et corrigé les problèmes à environ deux fois le taux de détection habituel.
Le compteur de régressions à zéro mérite d'être noté. Vingt corrections appliquées à travers six sessions d'audit, sans qu'aucune correction n'introduise un nouveau bug. Cela suggère que les corrections étaient chirurgicales (modifications ciblées de fonctions spécifiques) plutôt que structurelles (refactorisations pouvant se propager en cascade).
Décisions clés
Décision 1 : auditer après chaque phase, pas après toutes les phases
Nous aurions pu construire les quatre phases et auditer l'ensemble du code une seule fois. Cela aurait été plus rapide (moins de sessions) mais moins efficace. Les bugs trouvés dans l'audit de la Phase 1 ont informé les motifs utilisés dans l'implémentation de la Phase 2. Le motif d'écriture atomique, découvert comme correction de bug en Phase 1, a été appliqué proactivement en Phases 2 et 3.
Décision 2 : audit global après les audits par phase
Les audits par phase examinent le code à l'intérieur d'une frontière. Ils ne peuvent pas détecter les incohérences entre les frontières. L'audit global a trouvé des problèmes transversaux qu'aucun audit par phase n'aurait pu détecter : incohérence du masquage de token, divergence de la logique d'exclusion, limites de pagination.
La leçon : la correction locale n'implique pas la correction globale. Une fonction peut être correcte en isolation et quand même être erronée en contexte.
Décision 3 : réutiliser l'infrastructure existante
Les 16 commandes ont toutes été construites sur du code sh0 existant :
- Détection de stack :
sh0_builder::detector::detect_stack() - Vérification de santé :
sh0_builder::health::check_health() - Pipeline d'upload :
sh0-api/src/deploy/pipeline.rs - Résolution d'application :
Sh0Client::resolve_app() - Client WebSocket :
tokio-tungstenite(depuissh0 logs) - Serveur WebSocket :
axum::extract::ws(depuis le terminal)
Aucune nouvelle dépendance n'a été introduite pour le code côté serveur. Les seules nouvelles dépendances côté client étaient notify (surveillant de fichiers), percent-encoding (encodage d'URL) et des feature flags sur des crates existants.
Décision 4 : WebSocket avec repli HTTP
Le streaming des logs de build aurait pu être exclusivement WebSocket. Nous avons choisi de garder le polling HTTP comme repli car :
- Certains proxys inverses suppriment les en-têtes de mise à niveau WebSocket
- Le CLI doit fonctionner avec des serveurs sh0 antérieurs au endpoint de stream
- Le repli est gratuit -- c'est le code existant de la Phase 1, refactorisé dans une fonction
Le coût du repli est d'environ 50 lignes de code. Le bénéfice est la rétrocompatibilité sans aucune rupture.
Ce que nous avons appris
1. L'écart constructeur-auditeur est réel
Le même modèle qui construit le code ne peut pas l'auditer efficacement dans la même session. Non pas à cause d'une limitation de capacité, mais à cause d'une limitation de contexte. Le constructeur a accumulé des centaines de micro-décisions et d'hypothèses. L'auditeur part sans aucune.
Ce n'est pas propre à l'IA. Les développeurs humains ont les mêmes angles morts pour leur propre code. La solution est la même : une revue indépendante par quelqu'un qui ne l'a pas écrit.
2. Les audits transversaux sont non négociables
Cinq des vingt corrections provenaient de l'audit global. C'étaient des bugs qui existaient dans l'espace entre les commandes, invisibles à toute revue d'une seule commande. Masquage de token, logique d'exclusion, limites de pagination -- toutes des préoccupations transversales qui nécessitent de voir toute la surface en une fois.
3. La dette documentaire s'accumule plus vite que la dette technique
Entre les docs de la Phase 1 et la mise à jour de la Phase 5, dix commandes étaient complètement non documentées. L'écart représentait environ six sessions. Dans une équipe de développement traditionnelle, cela représenterait des semaines ou des mois de dérive. Avec le développement assisté par IA, c'étaient des heures -- mais la dette était la même : des fonctionnalités qui existent mais que personne ne peut découvrir.
La correction consiste à regrouper les mises à jour de documentation après que l'implémentation est terminée, pas à documenter de manière incrémentale pendant l'implémentation. Les docs incrémentales deviennent obsolètes quand les phases suivantes modifient la surface.
4. Les fines couches d'interface sont sous-estimées
Les cinq commandes de la Phase 3 font chacune moins de 50 lignes. Ce sont de fines couches d'interface autour d'appels API avec des invites de confirmation. Il n'y a rien d'ingénieux dedans. Ce sont aussi les ajouts les plus immédiatement utiles du CLI, car elles remplacent des actions courantes du tableau de bord par des commandes uniques.
La leçon : chaque fonctionnalité n'a pas besoin d'être techniquement intéressante. Parfois, le travail à plus haute valeur est le code le plus simple.
Le CLI complet
sh0 est désormais livré avec plus de 30 commandes :
Authentification push, login, whoami, config
Déploiement push, init, link, watch, deploy
Cycle de vie restart, stop, start, delete, open
Domaines domains list, add, remove
Gestion status, logs, env, ssh, scale
Infrastructure templates, compose, cron, yaml
Avancé hooks, preview, export, users, projectsChaque action du tableau de bord a un équivalent CLI. Chaque commande CLI a de la documentation. Chaque morceau de code a été audité au moins deux fois. Le CLI n'est pas une interface secondaire -- c'est un citoyen de première classe de la plateforme sh0.
De zéro à là en une journée. Non pas parce qu'une journée c'est rapide, mais parce que la méthodologie -- construire, auditer, auditer, documenter -- rend possible d'avancer vite sans avancer imprudemment.
Le propos plus large
Cette série d'articles ne parle pas d'un CLI. Elle parle d'une méthodologie pour construire du logiciel de production avec l'IA.
La méthodologie a trois propriétés :
- Séparation des préoccupations : le constructeur optimise pour les fonctionnalités. L'auditeur optimise pour la correction. Le documenteur optimise pour la clarté. Aucune session unique n'essaie de faire les trois.
- Le contexte vierge comme fonctionnalité : chaque session d'audit démarre sans les hypothèses du constructeur. Ce n'est pas une limitation des fenêtres de contexte de l'IA -- c'est un choix de conception délibéré qui produit de meilleures revues.
- Convergence par la diversité : construire, auditer, auditer, approuver. Chaque passe voit le code sous un angle différent. Le résultat converge vers quelque chose qu'aucune passe unique ne produirait.
Seize commandes. Six sessions d'audit. Vingt corrections de bugs. Zéro régression. Une journée.
Voilà ce que la méthodologie produit. Pas la perfection -- mais une réduction systématique de l'écart entre « ça fonctionne sur ma machine » et « ça fonctionne en production ».
Cet article fait partie de la série « Comment nous avons ajouté un CLI à sh0 ». Commencez par le début : Une seule commande pour déployer : comment nous avons construit sh0 push.