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