La tech derrière l’outil : la stack technique de SEOJuice

Vadim Kravcenko
Vadim Kravcenko
· 5 min read

Quand j’ai commencé à construire SEOJuice, la première vraie décision n’a pas porté sur une fonctionnalité ni sur un modèle tarifaire. La vraie question, c’était la tech derrière l’outil : est-ce que je pars sur la stack que je connais déjà, ou sur celle qui ferait bien dans un article “How We Built It” ? J’ai choisi la solution la plus simple. Et je referais exactement pareil, parce qu’une technologie ennuyeuse que tu maîtrises à fond bat presque toujours une technologie tendance que tu apprends en cours de route. (Demande-moi combien de temps j’ai perdu — trois semaines — à évaluer une base de données graphe avant d’admettre que PostgreSQL pouvait faire tout ce dont j’avais besoin.)

Cet article est un regard transparent sur chaque couche de l’infrastructure de SEOJuice — pas seulement les choix finaux, mais aussi les compromis derrière chacun d’eux. Si tu es fondateur et que tu évalues ta propre stack, j’espère que le raisonnement te sera plus utile que les étiquettes.

La tech derrière l’outil : philosophie

Ma philosophie tient en trois règles que j’ai apprises à la dure après des années à faire tourner des side projects avant SEOJuice :

A high-quality stock photo showing a technical professional analyzing data or AI-driven processing on a laptop or workstation, visually matching the section about processing pipelines and AI systems.
A high-quality stock photo showing a technical professional analyzing data or AI-driven processing on a laptop or workstation, visually matching the section about processing pipelines and AI systems.. Source: Semrush Blog
A realistic photo of a developer building or maintaining backend infrastructure, with code on screens and a professional workspace that supports the article's discussion of pragmatic stack decisions, Django, ASGI, and Nginx.
A realistic photo of a developer building or maintaining backend infrastructure, with code on screens and a professional workspace that supports the article's discussion of pragmatic stack decisions, Django, ASGI, and Nginx.. Source: Semrush Blog

Règle 1 : maîtrise le chemin critique. Si la fonctionnalité centrale de ton produit dépend d’une API tierce, tu as mis un bouton d’arrêt entre les mains de quelqu’un d’autre. Le moteur de crawl de SEOJuice, l’analyse des liens et les algorithmes de scoring tournent tous sur une infrastructure que je contrôle. À partir du moment où tu externalises ton différenciateur principal, tu n’es plus vraiment un produit — tu deviens un revendeur.

Règle 2 : choisis des technologies que tu peux déboguer à 2 h du matin. J’ai envisagé FastAPI pour le back-end. C’est plus rapide dans les benchmarks, plus moderne, et la gestion de l’asynchrone est plus propre. Mais j’utilise Django depuis 2016. Quand Sentry se déclenche à minuit, je n’ai pas envie de passer 40 minutes dans la documentation — je veux savoir immédiatement quel middleware fait des siennes. La familiarité, ce n’est pas de la paresse ; c’est de la maturité opérationnelle.

Règle 3 : réduis au minimum le nombre de pièces mobiles. Chaque service supplémentaire dans ton architecture, c’est un point de panne en plus, un élément à surveiller en plus, une dépendance à mettre à jour en plus. Je compte les services dans mon fichier docker-compose comme un randonneur compte les grammes dans son sac.

La tech derrière l’outil : back-end

Au cœur de SEOJuice, il y a Django. Je l’ai choisi plutôt que Flask (trop minimaliste pour un produit de cette complexité), FastAPI (excellent pour les API, mais j’avais besoin de l’admin Django, de l’ORM et du moteur de templates), et Rails (je ne connais pas assez Ruby pour le déboguer les yeux fermés). L’approche “tout compris” de Django m’a évité de passer les trois premiers mois à assembler un système d’authentification, un panneau d’administration et un framework de migration à partir de packages séparés. Le compromis, c’est que Django peut sembler lourd pour des microservices simples — mais SEOJuice n’est pas un microservice simple. C’est un monolithe avec plus de 15 apps, et Django gère ça très bien.

L’application tourne en ASGI via Uvicorn, derrière Nginx qui sert de reverse proxy. Je suis passé de Gunicorn (WSGI) à Uvicorn environ un an plus tard, quand j’ai ajouté le support WebSocket pour les notifications en temps réel et le serveur MCP. Cette migration s’est révélée étonnamment simple — un des avantages de l’adoption progressive d’ASGI par Django. Cloudflare se trouve en frontal de toute l’infrastructure et gère le DNS, la terminaison SSL et la protection DDoS. J’ai vu Cloudflare absorber des pics de trafic qui auraient mis mes serveurs à genoux sans même transpirer.

Tous les serveurs tournent sur Linux hébergé chez Hetzner. Pourquoi Hetzner plutôt que AWS ? Honnêtement, le coût. Un serveur dédié Hetzner avec 64GB RAM coûte à peu près ce qu’une instance EC2 équivalente coûte pour une semaine. Pour un produit lancé en bootstrap, cette différence compte énormément. Le compromis, c’est moins d’automatisation — pas d’auto-scaling, pas de Kubernetes managé. Je gère les déploiements avec Docker et quelques scripts shell. Ce n’est pas glamour, mais ça marche, et l’argent économisé va directement dans le développement produit.

La base de données est PostgreSQL avec l’extension pgvector. J’ai évalué MongoDB au début (tout le monde l’utilisait en 2022) et j’ai vite compris que les données SEO ont une structure fortement relationnelle — les pages appartiennent à des sites, les mots-clés appartiennent à des pages, les liens relient les pages entre elles. Une base orientée documents m’aurait forcé à réimplémenter la moitié de ce que Postgres offre gratuitement. L’extension pgvector a fini par me convaincre : elle me permet de stocker et d’interroger des vecteurs d’embedding directement à côté des données relationnelles, ce qui alimente notre détection de similarité de contenu et nos fonctionnalités d’IA sans avoir besoin d’une base vectorielle séparée.

Pour les tâches en arrière-plan, j’utilise Celery avec Redis comme file de messages. Celery prend pas mal de critiques dans l’écosystème Python, et une partie est méritée — la documentation est éparpillée, les options de configuration sont trop nombreuses, et déboguer des échecs de tâches peut rendre fou. Mais je n’ai rien trouvé de mieux pour le volume de travail en arrière-plan que SEOJuice traite : crawler des milliers de pages, lancer des analyses NLP, générer des rapports, surveiller des backlinks. J’ai envisagé Dramatiq et Huey comme alternatives plus légères. Dramatiq est vraiment bon, mais l’écosystème de Celery (planification avec beat, monitoring avec Flower, django-celery-results) m’a fait pencher de son côté.

L’authentification passe par Auth0. J’ai construit un système d’auth personnalisé pour un projet précédent et j’ai passé un temps franchement embarrassant à gérer les cas limites des réinitialisations de mot de passe, le rate limiting et les flux MFA. Auth0 s’occupe de tout ça. Le coût augmente avec le nombre d’utilisateurs, ce qui devient sensible à certains paliers, mais le temps d’ingénierie économisé rembourse largement l’abonnement, plusieurs fois.

Tout est conteneurisé avec Docker. Mon environnement de développement et mon environnement de production exécutent des conteneurs identiques. Ça a éliminé toute une catégorie de bugs “chez moi ça marche” qui empoisonnaient mes projets précédents. Sentry surveille les exceptions en temps réel — je peux remonter d’un bug signalé par un utilisateur jusqu’à la ligne de code exacte en moins d’une minute.

La tech derrière l’outil : front-end

J’ai pris la décision délibérée d’éviter React, Vue et tous les autres frameworks JavaScript. Le tableau de bord SEOJuice est en HTML rendu côté serveur avec Tailwind CSS et Alpine.js pour l’interactivité. C’est la décision qui me vaut le plus de remarques de la part d’autres développeurs, et je comprends pourquoi — ça donne l’impression de bricoler avec des outils à main alors que des outils électriques existent.

Voici mon raisonnement : SEOJuice est un tableau de bord très orienté données. Les utilisateurs consultent des tableaux, des graphiques et des rapports. Ils n’ont pas besoin d’édition collaborative en temps réel ni d’une gestion complexe de l’état côté client. Le rendu côté serveur signifie que chaque chargement de page est rapide, que la charge initiale est légère, et que je n’ai pas à maintenir un pipeline de build front-end séparé, une couche API distincte ou une bibliothèque de gestion d’état. Alpine.js gère les menus déroulants, les modales et les filtres interactifs. Pour les rares endroits où j’ai besoin d’une interactivité plus riche (le tableau de bord de monitoring en temps réel), j’utilise htmx pour remplacer des fragments HTML sans recharger toute la page.

Les assets statiques passent par le CDN de Bunny.net. J’ai testé le CDN de Cloudflare (que j’utilise déjà pour le DNS), mais j’ai trouvé la tarification par requête de Bunny plus prévisible pour mes schémas de trafic. La différence reste marginale — les deux feraient l’affaire.

La tech derrière l’outil : traitement et IA

Une part importante de ce qui rend SEOJuice utile, c’est la capacité à traiter de gros volumes de données et à utiliser l’IA pour des fonctionnalités qu’il serait impossible de construire manuellement.

Pour les modèles de langage, j’intègre OpenAI et Claude. J’utilise différents modèles pour différentes tâches, sur la base de plusieurs mois de tests : GPT-4o pour l’extraction de données structurées (il respecte les schémas JSON de manière plus fiable), Claude pour l’analyse de contenu et les suggestions (il écrit plus naturellement et gère mieux les nuances). J’ai essayé d’exécuter des modèles open source en local — Llama 2, puis Llama 3 — et l’écart de qualité pour un usage en production restait encore trop important pour justifier le coût d’infrastructure. Ce calcul change tous les quelques mois, donc je le réévalue régulièrement.

J’utilise pgvector pour la recherche par embeddings plutôt qu’une base vectorielle dédiée comme Pinecone ou Weaviate. Avoir les vecteurs dans PostgreSQL à côté du reste des données me permet de combiner les résultats de similarité d’embeddings avec des requêtes relationnelles dans une seule instruction SQL. Les performances sont suffisantes à notre échelle. Si je gérais des milliards de vecteurs, il me faudrait quelque chose de spécialisé. À l’échelle de millions, Postgres s’en sort très bien.

Côté NLP, NumPy, NLTK et Scikit-learn font le gros du travail pour l’extraction de mots-clés, le scoring de contenu et la classification. Ces bibliothèques ne sont pas excitantes, mais elles ont fait leurs preuves. Quand je lance une analyse TF-IDF sur 50,000 pages, j’ai besoin que le résultat soit correct, pas “innovant”.

Pour crawler les sites très dépendants de JavaScript, j’utilise Playwright dans un conteneur séparé. C’est plus gourmand en ressources que de simples requêtes HTTP, mais les sites modernes rendent souvent leur contenu critique via JavaScript. Si notre crawler ne peut pas le voir, on ne peut pas l’analyser. Playwright gère les SPA, le contenu chargé en lazy load et le routage côté client qui resteraient invisibles pour un crawler traditionnel.

La tech derrière l’outil : outils et services complémentaires

C’est sur l’infrastructure de support que j’ai été le plus à l’aise avec l’idée d’utiliser des services tiers, puisque rien de tout ça n’est un différenciateur central :

Crisp pour le live chat. J’ai essayé Intercom d’abord — trop cher et trop complexe pour une équipe support d’une seule personne. Crisp fait ce dont j’ai besoin pour une fraction du coût.

Customer.io pour l’email. Les emails transactionnels (réinitialisations de mot de passe, envoi de rapports), les séquences d’onboarding et les mises à jour produit passent tous par Customer.io. J’ai quitté une configuration plus simple (l’email natif de Django avec SendGrid) parce que j’avais besoin de déclencheurs comportementaux — “envoyer un email conseil si l’utilisateur n’a pas connecté son site WordPress dans les 3 jours”.

Paddle pour les paiements. J’ai commencé avec Stripe puis j’ai migré vers Paddle en tant que Merchant of Record. La raison était entièrement pratique : Paddle gère le calcul de la TVA, sa collecte et son reversement pour chaque pays. En tant que fondateur européen lancé en bootstrap qui vend dans le monde entier, construire mon propre système de conformité fiscale aurait été un job à plein temps. Paddle prend une commission plus importante que Stripe, mais élimine une catégorie entière de charge opérationnelle.

ChartMogul pour l’analyse du revenu. MRR, churn rate, LTV, expansion revenue — j’ai besoin que ces chiffres soient justes et mis à jour automatiquement. Je pourrais les calculer à partir de l’API de Paddle, mais ChartMogul le fait mieux et fait ressortir des tendances auxquelles je ne penserais même pas à regarder.

Pour l’analytics visiteurs, j’exécute une instance auto-hébergée de Plausible. Pas de cookies, pas de bannières de consentement, pas d’envoi des données visiteurs à Google. Ça me montre les sources de trafic, les pages les plus visitées et les référents — ce qui est, honnêtement, tout ce dont j’ai besoin. (J’ai écrit plus en détail sur Plausible dans notre guide des outils SEO open source.)

La tech derrière l’outil : ce que je changerais

Aucune stack n’est parfaite, et j’ai appris suffisamment de choses pour savoir ce que je ferais différemment si je repartais de zéro :

J’investirais plus tôt dans Kubernetes. Pas au premier jour — ce serait excessif pour un MVP — mais vers le moment où j’ai atteint 10 workers en arrière-plan. Orchestrer des conteneurs avec des scripts shell et systemd fonctionne… jusqu’au jour où ça ne fonctionne plus, et la transition vers une vraie orchestration tout en servant du trafic de production est franchement stressante.

Je séparerais l’API du monolithe plus tôt. Le fait que le tableau de bord, l’API publique, l’API du plugin WordPress et le serveur MCP partagent tous un seul processus Django signifie qu’un pic de trafic API peut ralentir le tableau de bord. Je suis en train d’extraire progressivement tout ça vers des services séparés, mais ça aurait été plus propre de le prévoir dès le départ.

Je ne changerais pas les choix de base — Django, PostgreSQL, Celery, HTML rendu côté serveur. Ils ont fait leurs preuves pendant deux ans de trafic en production, et la simplicité qu’ils apportent vaut plus que les gains de performance potentiels que je pourrais obtenir avec des alternatives plus à la mode.

Conclusion

En tant que fondateur solo, le piège le plus dangereux consiste à construire ta stack pour le public de Hacker News au lieu de la construire pour les vrais besoins de ton produit. Chaque choix technologique que j’ai décrit ici répondait à un besoin précis, a été évalué face à des alternatives, puis retenu parce qu’il me permettait de livrer plus vite avec moins de surprises. Le résultat, c’est un système que je peux faire tourner seul, déboguer rapidement et faire évoluer sans tout réécrire.

Mais ce n’est que le début. La stack évolue avec le produit — je travaille en continu à améliorer SEOJuice, et l’infrastructure doit suivre le rythme. Si tu es fondateur et que tu prends les mêmes décisions, j’espère que voir le raisonnement derrière les miennes t’évitera une partie des essais et erreurs par lesquels je suis passé.

Continuons à construire.

À bientôt,
Vadim

À lire aussi :

SEOJuice
Stay visible everywhere
Get discovered across Google and AI platforms with research-based optimizations.
Works with any CMS
Automated Internal Links
On-Page SEO Optimizations
Get Started Free

no credit card required

More articles

No related articles found.