J’ai shippé StackLint en une semaine. Ce que j’en retire.
Lundi 13 avril, le repo n’existe pas. Dimanche, stacklint.app est en prod avec waitlist Pro active, GitHub App validée, parité GitLab OAuth, grade A/B/C/D/F calculé, badge SVG embeddable, cron hebdomadaire, CI verte et smoke tests 8 sur 8.
Une semaine. Un développeur solo. Pas un proof of concept. Du shippé, qui tourne.
Voilà ce que j’en retire.
Ce que fait StackLint
StackLint scanne un repo GitHub ou GitLab, applique quatre filtres (CVE, dépendances obsolètes, zones sans test, duplication), retourne un plan priorisé top 10 et un grade A/B/C/D/F. Soixante secondes. Zéro compte requis pour l’analyse anonyme, authentification magic link pour sauvegarder et activer le scan hebdomadaire.
Le cœur produit n’est pas le sujet. Le build l’est.
Le pattern qui a tout changé
J’ai bootstrapé tout le backbone avec Claude Code en forçant un pattern de prompt très strict : Goal → Task → Deliverable.
Exemple minimal, c’est le prompt qui a produit la route /api/analyze :
## Goal
Build a full code analysis pipeline: accept a repo URL,
clone it, run analysis, return a JSON report.
## Task
Create a Next.js API route that [description exhaustive
en 10 lignes].
## Deliverable
Working endpoint, tested manually with a real public repo.
Include the test output in the result summary.
Trois sections. Pas quatre. Pas deux.
Le Goal donne la direction. Le Task donne le détail. Le Deliverable ferme la boucle : l’agent ne peut pas déclarer le travail fini sans preuve d’exécution.
C’est ce dernier point qui change tout. Sans Deliverable, l’agent rend un code qui « devrait marcher » et passe au suivant. Avec Deliverable, il clone un vrai repo, il exécute, il colle la sortie dans le résumé. Si ça ne passe pas, il corrige avant de rendre.
J’ai itéré sur cette structure pendant les premiers prompts. Au bout du troisième, le template était bétonné. Je ne l’ai quasi plus touché de la semaine.
Une précision : ce n’est pas du vibe coding. Je relisais chaque PR avant merge, je lançais les tests manuellement, je débuggais à la main quand Claude se vautrait. Ce qui change avec l’IA, ce n’est pas le fait de coder sans réfléchir. C’est le coût de l’implémentation qui s’effondre. La réflexion, elle, reste à ma charge.
La stack, choix tranchés
Le contexte : un seul développeur, une semaine, prod-ready. Pas le temps de tergiverser.
- Next.js 14 App Router : par défaut, pas de débat. Server components et API routes dans le même repo, zéro overhead d’infra.
- Drizzle + Neon : Drizzle pour les migrations SQL-first et la DX types-safe. Neon pour Postgres serverless en free tier, zéro cold start notable, branches pour la CI.
- Coolify sur un VPS Hostinger : là, je sors du mainstream. Vercel aurait été plus rapide à bootstrap, mais je veux pouvoir héberger du long-running (clone de repo, analyse statique) sans exploser une facture serverless. Coolify me donne l’ergonomie de Vercel sans le vendor lock-in.
- Plausible self-hosté pour l’analytics. Parce que je ne veux pas de Google Analytics sur une landing que je veux légère, et parce que je maîtrise les events.
Un piège m’a coûté deux hotfixes. Derrière Cloudflare et Coolify, req.url retourne l’adresse interne du container (http://0.0.0.0:3000), pas l’URL publique. Tout redirect absolu doit passer par AUTH_URL. Classique, et j’aurais dû le voir venir. Je ne l’ai pas vu.
Couper Stripe
Dans ma planification initiale, une Phase 6 Stripe était prévue juste avant le lancement. Checkout Pro à 29 $ par mois, Team à 99 $, payment links, webhooks.
J’ai coupé. Pas reporté : coupé.
Raison simple : je n’avais zéro signal de demande payante. J’allais dépenser 7 à 10 jours à optimiser une conversion qui n’existait pas. Shipper un checkout sans savoir si quelqu’un veut payer, c’est construire un entonnoir sans eau.
À la place, j’ai shippé une waitlist Pro : table pro_waitlist en base, formulaire sur la card Pro, rate limit par IP, event Plausible pro-waitlist-signup déclaré en Goal. Total : une demi-journée de développement.
La règle que je me donne : je n’ouvre Stripe qu’à 30-50 inscriptions waitlist minimum. En dessous, c’est du bruit, je n’ai pas de traction mesurable. Au-dessus, j’ai une liste à convertir et un signal d’intent à mes côtés le jour où je ship le checkout.
C’est une discipline que je vois rarement appliquée à ce niveau de rigueur chez les solopreneurs développeurs. On ship Stripe parce qu’on aime l’idée d’être payé, pas parce que la demande est mesurée. Moi aussi j’aime l’idée d’être payé. Mais j’aime encore plus l’idée de ne pas perdre une semaine pour rien.
Le code reste découpé pour qu’un feature-flag suffise à activer Stripe quand les chiffres parleront.
Le pari distribution : badge SVG
Pour la distribution, j’ai un seul pari : le badge SVG embeddable dans un README.
L’inspiration est connue des vieux développeurs : CodeClimate, Snyk, Coveralls. Un badge dans un README, c’est une publicité passive qui tourne 24/7 dans les repos de vos utilisateurs, lisible par toute personne qui visite le dit repo. Chaque utilisateur qui intègre le badge devient, techniquement, un vecteur de distribution.
StackLint calcule donc, pour chaque scan, un grade A/B/C/D/F. Le grade s’affiche dans un badge SVG exposé sur GET /api/badge/:provider/:owner/:repo.svg, avec cache 1h pour ne pas exploser la latence. Le dashboard pousse le snippet markdown à copier-coller, et chaque copie déclenche un event Plausible badge-copied, mon indicateur nord de distribution.
C’est élégant sur le papier. Est-ce que ça va marcher pour autant ? Aucune idée. Je saurai dans 2 à 4 semaines, quand j’aurai une poignée de badges en circulation et qu’une partie des signups landing viendra d’un clic sur badge.
Si ça ne prend pas, j’aurai perdu deux jours. Si ça prend, j’aurai un canal de distribution zéro-coût, auto-renforçant, et que personne ne peut facilement couper. Le rapport risque/reward est asymétrique. C’est le genre de pari que je prends sans hésiter.
Les gotchas qui coûtent
Les deux qui m’ont fait perdre le plus de temps sont sur Plausible. Je les consigne ici.
Ad-blockers qui bloquent les events
uBlock Origin et consorts bloquent le script Plausible quel que soit le domaine, y compris en self-hosté. Tous les visiteurs sous ad-blocker ne déclenchent aucun event. Dans une audience dev, ça creuse un trou dans les données. Depuis, je garde l’ad-blocker en tête chaque fois que mes chiffres Plausible paraissent trop bas par rapport au trafic serveur.
Events → Goals à déclarer à la main
Un trackEvent() dans le code ne remonte pas dans le dashboard Plausible tant qu’il n’est pas déclaré comme Goal à la main dans l’admin. Ce n’est pas documenté là où je l’ai cherché. J’ai mis trop de temps à comprendre que mes events marchaient très bien. C’est le dashboard qui les ignorait.
Ce que j’en retire vraiment
Une semaine, un SaaS en prod, une décision Stripe à contre-courant, un pari distribution à mesurer. Ce qui a rendu ça possible, ce n’est pas l’IA. C’est l’IA plus une discipline de prompt qui fait porter à l’agent le coût de la preuve, et un développeur qui ne dort pas tant qu’un chiffre n’est pas aligné.
Je n’ai plus le goût pour les projets où je passe trois semaines à choisir la librairie de validation de formulaires. Avec Claude Code, le coût marginal d’écrire du code tend vers zéro. Ce qui devient cher, c’est de savoir quoi construire, pourquoi, et comment le mesurer.
C’est peut-être ça, le vrai changement. Shipper plus vite n’est plus un problème. Shipper la bonne chose, toujours.