This commit is contained in:
parent
56a0c9d7b3
commit
925f5e47d5
2 changed files with 379 additions and 0 deletions
181
content/en/post/pragmatic-architecture-for-productions/index.md
Normal file
181
content/en/post/pragmatic-architecture-for-productions/index.md
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
---
|
||||||
|
title: The pragmatic architecture of my production projects
|
||||||
|
date: !!timestamp '2026-03-05 15:00:00'
|
||||||
|
tags:
|
||||||
|
- architecture
|
||||||
|
- web
|
||||||
|
- infrastructure
|
||||||
|
---
|
||||||
|
|
||||||
|
Reading some discussions between developers or software architects, one might
|
||||||
|
think that the smallest web application today requires a distributed
|
||||||
|
infrastructure, a Kubernetes cluster, and several specialized cloud services.
|
||||||
|
|
||||||
|
Yet many web services (including those that receive several thousand visitors
|
||||||
|
per day) can work perfectly well with a much simpler architecture.
|
||||||
|
|
||||||
|
Here is a hands-on account of the infrastructure I use for my production
|
||||||
|
projects, some of which exceed 2,000 daily visitors, and which I also applied
|
||||||
|
for years during a cybersecurity competition with more than 250 on-site
|
||||||
|
participants.
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
# Context
|
||||||
|
|
||||||
|
For a long time, I hosted all my projects from home. My websites, services, Git
|
||||||
|
forge, and even my continuous integration infrastructure ran on small ARM
|
||||||
|
machines (mainly Pine64 boards).
|
||||||
|
|
||||||
|
Contrary to what I hear everywhere, it worked very well.
|
||||||
|
|
||||||
|
The main reason I stopped is not a problem of computing power or reliability,
|
||||||
|
but simply my shift to a more nomadic lifestyle. A stable, permanent residential
|
||||||
|
connection becomes hard to guarantee when you travel often.
|
||||||
|
|
||||||
|
I therefore moved the public-facing part of my services to OVH, trying to keep
|
||||||
|
the same philosophy: simplicity and pragmatism.
|
||||||
|
|
||||||
|
|
||||||
|
# A simple observation
|
||||||
|
|
||||||
|
In many modern web projects, the architecture looks something like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
user → CDN → frontend → API → database
|
||||||
|
```
|
||||||
|
|
||||||
|
Every request triggers server-side computation, even when the content changes
|
||||||
|
very rarely.
|
||||||
|
|
||||||
|
In my case, several of my sites share an important characteristic: **the main
|
||||||
|
content changes once a day.**
|
||||||
|
|
||||||
|
Take the [daily game Yakazu](https://yakazu-gratuit.fr/) as an example: for the
|
||||||
|
vast majority of visitors, the only thing that matters is **the grid of the
|
||||||
|
day**, which changes just once a day.
|
||||||
|
|
||||||
|
Under these conditions, generating the page dynamically on every request does
|
||||||
|
not make much sense.
|
||||||
|
|
||||||
|
# Chosen architecture
|
||||||
|
|
||||||
|
The solution is therefore very simple: the site is entirely static.
|
||||||
|
|
||||||
|
Every day, a scheduled event triggers the CI pipeline which:
|
||||||
|
|
||||||
|
1. generates the new grid;
|
||||||
|
2. rebuilds the site;
|
||||||
|
3. publishes the static files.
|
||||||
|
|
||||||
|
These files are then served directly by the static hosting included with the
|
||||||
|
domain name at OVH.
|
||||||
|
|
||||||
|
Yes, the free 100 MB plan.
|
||||||
|
|
||||||
|
This gives a very simple architecture:
|
||||||
|
|
||||||
|
```
|
||||||
|
CI → site generation → static files → OVH
|
||||||
|
```
|
||||||
|
|
||||||
|
All traffic is absorbed by the hosting provider's infrastructure, which is
|
||||||
|
exactly what it is designed for.
|
||||||
|
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
|
||||||
|
The site is built with SvelteKit.
|
||||||
|
|
||||||
|
This choice might seem surprising since I don't use the server-side capabilities
|
||||||
|
of this framework at all. But it allows:
|
||||||
|
|
||||||
|
- easily generating a static site (`@sveltejs/adapter-static`);
|
||||||
|
- clean routing;
|
||||||
|
- well-structured code;
|
||||||
|
- building Progressive Web Apps.
|
||||||
|
|
||||||
|
In other words: a modern framework, but without depending on a permanent
|
||||||
|
backend.
|
||||||
|
|
||||||
|
# And when a backend is needed?
|
||||||
|
|
||||||
|
Some features still require a bit of server-side logic.
|
||||||
|
|
||||||
|
For instance, regularly held championships need to verify participations and
|
||||||
|
record results and times.
|
||||||
|
|
||||||
|
In that case, I spin up a small separate backend server, which only receives a
|
||||||
|
very limited number of requests.
|
||||||
|
|
||||||
|
The architecture then becomes:
|
||||||
|
|
||||||
|
```
|
||||||
|
static site → CORS → small backend
|
||||||
|
```
|
||||||
|
|
||||||
|
This backend is deliberately minimal and only handles actions that cannot be
|
||||||
|
performed on the client side.
|
||||||
|
|
||||||
|
The key point is that **the vast majority of traffic never touches it**.
|
||||||
|
|
||||||
|
# Results
|
||||||
|
|
||||||
|
With this architecture:
|
||||||
|
|
||||||
|
- almost all traffic is served as static files;
|
||||||
|
- there is virtually no server-side computation;
|
||||||
|
- the failure surface is very small.
|
||||||
|
|
||||||
|
The issues I encounter are almost always related to the generation pipeline:
|
||||||
|
|
||||||
|
- an outdated Docker image;
|
||||||
|
- a CI trigger that fails;
|
||||||
|
- something missing in the data generation (the absence of a game grid, for example).
|
||||||
|
|
||||||
|
But these problems are easy to detect and usually fixed within minutes.
|
||||||
|
|
||||||
|
# Why keep it simple?
|
||||||
|
|
||||||
|
Looking back, I realize this approach follows a few simple principles:
|
||||||
|
|
||||||
|
1. **pre-compute what can be pre-computed**;
|
||||||
|
2. **serve files rather than computation**;
|
||||||
|
3. **reserve the backend for cases that truly need it**.
|
||||||
|
|
||||||
|
These principles often make it possible to handle significant load with very
|
||||||
|
modest resources.
|
||||||
|
|
||||||
|
For more than ten years, I organized a cybersecurity challenge that eventually
|
||||||
|
hosted around 300 simultaneous participants, with very basic machines.
|
||||||
|
|
||||||
|
The secret was not the hardware's power, but the system's design. I invite you
|
||||||
|
to watch my [presentation of this
|
||||||
|
infrastructure](https://youtube.com/watch?v=naFxBW5yoUA), with the trial and
|
||||||
|
error, the mistakes, and the constraints that shaped it.
|
||||||
|
|
||||||
|
# What about user accounts and payments?
|
||||||
|
|
||||||
|
A "login" to a user account does not necessarily require systematic validation
|
||||||
|
by a backend (and the notion of a user account is not always meaningful anyway).
|
||||||
|
|
||||||
|
A single API request can handle authentication (whether verifying a payment or
|
||||||
|
an email address, ...) and then you store that response in the browser to use in
|
||||||
|
the interface: right away... or even later. That way, there is no need to make
|
||||||
|
another API request for information you already have.
|
||||||
|
|
||||||
|
Not only does this reduce server load, it also speeds up page loading for the
|
||||||
|
user, who sees their data without a network call — and therefore even offline.
|
||||||
|
Everyone wins.
|
||||||
|
|
||||||
|
# Conclusion
|
||||||
|
|
||||||
|
Modern infrastructure offers many very powerful tools. But it is sometimes worth
|
||||||
|
remembering that many problems can be solved in a much simpler way.
|
||||||
|
|
||||||
|
Before adding a new technological layer, it is often worth asking a very simple
|
||||||
|
question:
|
||||||
|
|
||||||
|
**does this content really need to be generated on every request?**
|
||||||
|
|
||||||
|
In many cases, the answer is no.
|
||||||
198
content/fr/post/pragmatic-architecture-for-productions/index.md
Normal file
198
content/fr/post/pragmatic-architecture-for-productions/index.md
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
---
|
||||||
|
title: L'architecture pragmatique de mes projets en production
|
||||||
|
date: !!timestamp '2026-03-05 15:00:00'
|
||||||
|
tags:
|
||||||
|
- architecture
|
||||||
|
- web
|
||||||
|
- infrastructure
|
||||||
|
---
|
||||||
|
|
||||||
|
Lorsqu'on lit certaines discussions entre développeurs ou architectes
|
||||||
|
logiciels, on pourrait croire que la moindre application web nécessite
|
||||||
|
aujourd'hui une infrastructure distribuée, un cluster Kubernetes et
|
||||||
|
plusieurs services cloud spécialisés.
|
||||||
|
|
||||||
|
Pourtant, de nombreux services web (y compris ceux qui accueillent
|
||||||
|
plusieurs milliers de visiteurs par jour) peuvent fonctionner
|
||||||
|
parfaitement avec une architecture beaucoup plus simple.
|
||||||
|
|
||||||
|
Voici un retour d'expérience sur l'infrastructure que j'utilise pour
|
||||||
|
mes projets en production, dont certains dépassent les 2000 visiteurs
|
||||||
|
quotidiens, et que j'ai également appliqué pendant des années lors
|
||||||
|
d'une compétition de sécurité informatique avec plus de 250
|
||||||
|
participants en présentiel.
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
# Contexte
|
||||||
|
|
||||||
|
Pendant longtemps, j'ai hébergé l'ensemble de mes projets directement
|
||||||
|
chez moi. Mes sites web, mes services, ma forge Git et même mon
|
||||||
|
infrastructure d'intégration continue tournaient sur de petites
|
||||||
|
machines ARM (principalement des Pine64).
|
||||||
|
|
||||||
|
Contrairement à ce que j'entends partout, cela fonctionnait très bien.
|
||||||
|
|
||||||
|
La principale raison pour laquelle j'ai arrêté n'est pas un problème
|
||||||
|
de puissance de calcul ou de fiabilité, mais simplement mon passage à
|
||||||
|
un mode de vie plus nomade. Une connexion résidentielle stable et
|
||||||
|
permanente devient difficile à garantir lorsque l'on se déplace
|
||||||
|
souvent.
|
||||||
|
|
||||||
|
J'ai donc déplacé la partie publique de mes services chez OVH, en
|
||||||
|
essayant de conserver la même philosophie : simplicité et pragmatisme.
|
||||||
|
|
||||||
|
|
||||||
|
# Une observation simple
|
||||||
|
|
||||||
|
Dans beaucoup de projets web modernes, l'architecture ressemble à
|
||||||
|
quelque chose comme ceci :
|
||||||
|
|
||||||
|
```
|
||||||
|
utilisateur → CDN → frontend → API → base de données
|
||||||
|
```
|
||||||
|
|
||||||
|
Chaque requête déclenche du calcul côté serveur, même lorsque le
|
||||||
|
contenu change très rarement.
|
||||||
|
|
||||||
|
Dans mon cas, plusieurs de mes sites ont une caractéristique
|
||||||
|
importante : **le contenu principal change une fois par jour.**
|
||||||
|
|
||||||
|
Prenons par exemple le [jeu quotidien
|
||||||
|
Yakazu](https://yakazu-gratuit.fr/) : pour la majorité des visiteurs,
|
||||||
|
la seule chose qui compte est **la grille du jour**, qui ne change
|
||||||
|
qu'une fois par jour.
|
||||||
|
|
||||||
|
Dans ces conditions, générer la page dynamiquement à chaque requête
|
||||||
|
n'a pas beaucoup de sens.
|
||||||
|
|
||||||
|
# Architecture retenue
|
||||||
|
|
||||||
|
La solution est donc très simple : le site est entièrement statique.
|
||||||
|
|
||||||
|
Chaque jour, un événement déclenche la CI qui :
|
||||||
|
|
||||||
|
1. génère la nouvelle grille ;
|
||||||
|
2. reconstruit le site ;
|
||||||
|
3. publie les fichiers statiques.
|
||||||
|
|
||||||
|
Ces fichiers sont ensuite servis directement par l'hébergement
|
||||||
|
statique fourni avec le nom de domaine chez OVH.
|
||||||
|
|
||||||
|
Oui, l'offre gratuite de 100 MB.
|
||||||
|
|
||||||
|
Cela donne donc une architecture très simple :
|
||||||
|
|
||||||
|
```
|
||||||
|
CI → génération du site → fichiers statiques → OVH
|
||||||
|
```
|
||||||
|
|
||||||
|
Tout le trafic est absorbé par l'infrastructure de l'hébergeur, ce
|
||||||
|
qui est exactement ce pour quoi elle est conçue.
|
||||||
|
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
|
||||||
|
Le site est construit avec SvelteKit.
|
||||||
|
|
||||||
|
Ce choix peut surprendre puisque je n'utilise pas du tout les
|
||||||
|
capacités serveur de ce framework. Mais il permet :
|
||||||
|
|
||||||
|
- de générer facilement un site statique (`@sveltejs/adapter-static`) ;
|
||||||
|
- d'avoir un routage propre ;
|
||||||
|
- de structurer le code proprement ;
|
||||||
|
- de construire des Progressive Web Apps.
|
||||||
|
|
||||||
|
Autrement dit : un framework moderne, mais sans dépendre d'un backend
|
||||||
|
permanent.
|
||||||
|
|
||||||
|
# Et quand il faut un backend ?
|
||||||
|
|
||||||
|
Certaines fonctionnalités nécessitent malgré tout un peu de logique
|
||||||
|
serveur.
|
||||||
|
|
||||||
|
Par exemple, les championnats organisés régulièrement doivent vérifier les
|
||||||
|
participations et enregistrer les résultats et les chronos.
|
||||||
|
|
||||||
|
Dans ce cas, je démarre un petit serveur backend séparé, qui ne reçoit
|
||||||
|
qu'un nombre très limité de requêtes.
|
||||||
|
|
||||||
|
L'architecture devient alors :
|
||||||
|
|
||||||
|
```
|
||||||
|
site statique → CORS → petit backend
|
||||||
|
```
|
||||||
|
|
||||||
|
Ce backend est volontairement minimal et ne traite que les actions
|
||||||
|
qui ne peuvent pas être réalisées côté client.
|
||||||
|
|
||||||
|
Le point important est que **la majorité du trafic ne le touche
|
||||||
|
jamais**.
|
||||||
|
|
||||||
|
# Résultat
|
||||||
|
|
||||||
|
Avec cette architecture :
|
||||||
|
|
||||||
|
- la quasi-totalité du trafic est servie sous forme de fichiers ;
|
||||||
|
- il n'y a quasiment pas de calcul côté serveur ;
|
||||||
|
- la surface de panne est très réduite.
|
||||||
|
|
||||||
|
Les problèmes que je rencontre sont presque toujours liés au pipeline
|
||||||
|
de génération :
|
||||||
|
|
||||||
|
- une image Docker qui n'est plus à jour ;
|
||||||
|
- un déclenchement CI qui échoue ;
|
||||||
|
- un oubli dans la génération des données (l'absence de grille de jeu par exemple).
|
||||||
|
|
||||||
|
Mais ces problèmes sont faciles à détecter et se corrigent
|
||||||
|
généralement en quelques minutes.
|
||||||
|
|
||||||
|
# Pourquoi faire simple ?
|
||||||
|
|
||||||
|
Avec un peu de recul, je me rends compte que cette approche suit
|
||||||
|
quelques principes simples :
|
||||||
|
|
||||||
|
1. **pré-calculer ce qui peut l'être** ;
|
||||||
|
2. **servir des fichiers plutôt que du calcul** ;
|
||||||
|
3. **réserver le backend aux cas réellement nécessaires**.
|
||||||
|
|
||||||
|
Ces principes permettent souvent d'absorber une charge importante
|
||||||
|
avec des ressources très modestes.
|
||||||
|
|
||||||
|
Pendant plus de dix ans, j'ai par exemple organisé un challenge de
|
||||||
|
sécurité informatique qui a fini par accueillir environ 300
|
||||||
|
participants simultanés, avec des machines très simples.
|
||||||
|
|
||||||
|
Le secret n'était pas la puissance du matériel, mais la conception du
|
||||||
|
système. Je vous laisse visionner ma [présentation de cette
|
||||||
|
infrastructure](https://youtube.com/watch?v=naFxBW5yoUA), avec les
|
||||||
|
tâtonnements, les erreurs et les contraintes qui l'ont façonné.
|
||||||
|
|
||||||
|
# Et les comptes utilisateurs, les paiements ?
|
||||||
|
|
||||||
|
Une "connexion" à un compte utilisateur n'a pas nécessairement besoin
|
||||||
|
d'une validation systématique par un backend (d'ailleurs la notion de
|
||||||
|
compte utilisateur n'a pas toujours de sens).
|
||||||
|
|
||||||
|
Une seule requête d'API peut faire l'authentification (que ce soit
|
||||||
|
la validation d'un paiement ou d'une adresse email, ...) puis on
|
||||||
|
stocke cette réponse dans le navigateur pour en faire usage dans
|
||||||
|
l'interface : maintenant ... ou même plus tard. Comme ça, inutile de
|
||||||
|
refaire une requête à l'API pour une information que l'on a déjà.
|
||||||
|
|
||||||
|
Non seulement cela réduit la charge du serveur, mais cela accroit
|
||||||
|
aussi la vitesse de chargement pour l'utilisateur qui voit ses données
|
||||||
|
sans appel réseau, et donc même hors ligne. Tout le monde est gagnant.
|
||||||
|
|
||||||
|
# Conclusion
|
||||||
|
|
||||||
|
L'infrastructure moderne propose de nombreux outils très puissants.
|
||||||
|
Mais il est parfois utile de se rappeler que beaucoup de problèmes
|
||||||
|
peuvent être résolus de manière beaucoup plus simple.
|
||||||
|
|
||||||
|
Avant d'ajouter une nouvelle couche technologique, il vaut souvent la
|
||||||
|
peine de se poser une question très simple :
|
||||||
|
|
||||||
|
**est-ce que ce contenu doit vraiment être généré à chaque requête ?**
|
||||||
|
|
||||||
|
Dans bien des cas, la réponse est non.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue