Le béton, une fois coulé, ne se renégocie pas

Sur Le Chantier, le fil rouge de cette série, arrive le moment où le maître d'œuvre choisit les matériaux. Béton armé coulé en place, ossature acier, bois lamellé collé, éléments préfabriqués livrés sur camion. Le programme est écrit, le type d'ouvrage est fixé, il faut maintenant décider avec quoi on le monte.

Ce choix a deux particularités. Il est presque irréversible : une fois la structure montée, on ne change pas de matériau, on démolit. Et il est chargé d'affect : chaque corps de métier défend celui qu'il maîtrise, avec des arguments techniques qui masquent mal des préférences.

Le choix de la stack n'est pas la décision la plus visible de l'architecture. C'est la plus difficile à défaire, et la plus polluée par les goûts personnels.

Le billet se lit en deux moitiés. D'abord les critères rationnels, ceux qu'on peut poser sur une table et défendre devant quelqu'un qui n'est pas d'accord. Ensuite les contraintes extérieures, qui tranchent très souvent avant que l'architecte ait fini sa comparaison.


Vue d'ensemble : trois décisions, une justification écrite

Le maître d'œuvre ne choisit pas « un matériau ». Il choisit une structure porteuse, un second œuvre avec sa façade, et des fondations adaptées au terrain. Trois familles de décisions, trois logiques différentes.

La stack technique se découpe pareil.

  • Le back : la plateforme serveur, celle qui porte la logique métier et les traitements.
  • Le front : ce que voit l'utilisateur, sur le web et sur mobile, chacun avec ses propres arbitrages.
  • Le magasin de données : les fondations, qui dépendent du terrain plus que de tes préférences.

Le troisième pan mérite son propre billet et sera traité au suivant, sur le choix entre relationnel et non relationnel. Ici, on reste sur le back et le front.

Les deux suivent la même méthode : lister les candidats crédibles, poser les critères, éliminer, puis écrire pourquoi. Cette dernière étape est celle que tout le monde saute. Le Chantier, lui, produit un dossier des ouvrages exécutés qui dit ce qui a été posé et pourquoi, parce que le bâtiment vivra cinquante ans et que plus personne ne se souviendra des arbitrages.

Une fiche de choix tient en quinze lignes et se range à côté du document d'architecture.

choix: plateforme du service back
retenu: JVM, Kotlin et Spring Boot
candidats_ecartes:
  - Node.js: équipe sans expérience serveur, montée en compétence hors délai
  - Python: écosystème adapté au besoin, exploitation sans expérience de mise en production
criteres_decisifs:
  - six développeurs sur huit déjà autonomes sur la JVM
  - exploitation déjà outillée pour superviser des services JVM
  - besoin de traitements concurrents soutenus
consequences_acceptees:
  - démarrage à froid plus lent, à surveiller sur les traitements éphémères
  - empreinte mémoire plus élevée par instance
revoir_si: le profil de charge devient massivement entrées/sorties sur des services très courts
Un choix de stack non écrit sera rejoué à chaque arrivée dans l'équipe, et perdu par celui qui parle le plus fort.

Le back : cinq familles, aucun vainqueur

Béton, acier, bois lamellé : les trois tiennent un immeuble debout. Aucun ne convient à tous les immeubles, et le débat ne se tranche pas dans l'absolu.

Les critères qui comptent tiennent en six questions. Le langage sait-il faire le travail demandé, compte tenu du type d'application retenu au billet précédent ? Quelle est la maturité de la plateforme et la durée de support de ses versions ? La communauté est-elle assez large pour que tu trouves des réponses et des développeurs ? Existe-t-il des bibliothèques pour ton domaine métier ? Quel est le profil de performance ? Et combien coûte l'entrée pour ton équipe ?

La JVM (Java, Kotlin, Scala) est la plateforme serveur la plus dense en bibliothèques d'entreprise. Typage statique, compilation vers un bytecode optimisé à l'exécution, écosystème mature autour de Spring et de Jakarta EE, versions à support long terme espacées de deux ans. Le prix : verbosité, démarrage à froid, empreinte mémoire.

.NET occupe une position très proche, avec C# comme langage principal. Multiplateforme depuis .NET Core, outillage intégré et cohérent, versions à support long terme publiées en novembre et maintenues trois ans. Le lien historique avec l'écosystème Microsoft joue dans les deux sens : décisif si ton exploitation vit déjà là, friction sinon.

Node.js permet d'écrire le serveur dans le même langage que le navigateur, JavaScript ou TypeScript. Son modèle mono-thread à boucle d'événements excelle sur les charges d'entrées/sorties concurrentes (API, passerelles, temps réel) et convient mal au calcul intensif, qu'on isole dans des fils de travail ou un autre service. Le registre npm est immense, avec son revers : la profondeur de l'arbre de dépendances, sujet que je traite côté outillage dans SAST, DAST, SCA, fuzzing : les outils classés par famille.

Python a le typage le plus souple des cinq, avec des annotations optionnelles vérifiées par des outils tiers. Son écosystème scientifique et son avance sur l'apprentissage automatique n'ont pas d'équivalent. Le code Python pur s'exécute lentement et le verrou global de l'interpréteur limite le parallélisme dans un processus, ce qu'on contourne en déléguant aux bibliothèques natives ou en multipliant les processus. Un mode expérimental sans ce verrou existe depuis Python 3.13.

PHP traîne une réputation construite sur ses versions de 2005, alors que PHP 7 a introduit les déclarations de types scalaires et que PHP 8 a apporté un compilateur à la volée. Avec Symfony ou Laravel, c'est une pile web crédible, hébergeable à peu près partout. Son périmètre reste plus étroit dès qu'on sort du web.

Famille À l'aise sur Typage Plateformes Écosystème Performance Courbe d'entrée
JVM API, services, traitements par lots, gros SI Statique Multiplateforme via la JVM Très dense, orienté entreprise Élevée en régime établi Raide
.NET API, services, desktop Windows Statique Multiplateforme depuis .NET Core Dense, cohérent, un seul éditeur Élevée en régime établi Moyenne
Node.js API, passerelles, temps réel Dynamique, statique avec TypeScript Multiplateforme Immense, très fragmenté Excellente sur entrées/sorties, faible sur calcul Douce si l'équipe fait du front
Python Data, apprentissage automatique, automatisation, API Dynamique, annotations optionnelles Multiplateforme Le plus fourni sur la data Modeste en pur, déléguée au natif Douce
PHP Applications et sites web Dynamique, types déclarables Multiplateforme, hébergement partout Solide sur le web, étroit ailleurs Correcte depuis PHP 7 Douce

Trois autres familles méritent d'être citées sans figurer au tableau. Go, pour ses binaires statiques, son démarrage instantané et sa concurrence légère, très présent dans l'outillage d'infrastructure. Rust, pour le contrôle mémoire sans ramasse-miettes, avec un coût d'entrée réel. Elixir, pour les systèmes à très forte concurrence. Les écarter d'une comparaison relève du périmètre retenu, pas d'un jugement de valeur.

La question n'est jamais « quel est le meilleur langage ». Elle est « lequel fait ce travail, avec l'équipe et l'exploitation dont je dispose réellement ».

Le front web : framework complet ou bibliothèque d'interface

Pour la façade, le maître d'œuvre a deux options. Un système complet livré par un industriel, avec ses fixations, ses menuiseries et sa garantie. Ou des éléments choisis un par un, assemblés par ses soins, plus adaptés au projet et plus longs à mettre au point.

Le front web pose exactement ce choix. Un framework complet comme Angular fournit le routage, les formulaires, le client HTTP, l'injection de dépendances et un outil en ligne de commande, dans un ensemble cohérent et imposé. Une bibliothèque d'interface comme React ne prend en charge que la vue : le routage, la gestion d'état, l'accès aux données et les formulaires viennent de paquets tiers que tu assembles. Vue se tient entre les deux, avec des paquets officiels autour d'un noyau réduit.

La différence ne se joue pas sur les performances de rendu, qui se valent pour l'écrasante majorité des applications de gestion. Elle se joue sur qui prend les décisions d'architecture front, et à quelle fréquence.

Critère Framework complet Bibliothèque d'interface
Périmètre couvert Routage, formulaires, HTTP, injection, outillage La vue, le reste à assembler
Décisions à prendre par l'équipe Peu, déjà tranchées par l'éditeur Beaucoup, à chaque nouveau projet
Arrivée d'un développeur Reconnaît la structure immédiatement Doit apprendre l'assemblage maison
Montées de version Groupées, guidées, parfois lourdes Étalées, paquet par paquet, à coordonner
Homogénéité entre projets Forte par construction À maintenir par discipline
Adaptation à un besoin atypique Contrainte par le cadre Large

Les cadres bâtis au-dessus des bibliothèques, comme Next.js pour React ou Nuxt pour Vue, ramènent une partie de ces conventions et brouillent volontairement la frontière. Ils déplacent aussi le choix vers le rendu serveur, question déjà posée au billet précédent sur les types d'applications.

Choisir entre framework complet et bibliothèque, c'est choisir qui prend les décisions d'architecture front : l'éditeur une fois pour toutes, ou ton équipe à chaque projet.

Le mobile : natif, cross-platform ou hybride

Imagine le même bâtiment à construire sur deux terrains soumis à deux réglementations différentes. Soit tu montes deux ouvrages sur mesure, soit tu poses un modèle préfabriqué qui satisfait les deux, en acceptant qu'il colle un peu moins à chaque terrain.

Le natif signifie deux bases de code : Swift et SwiftUI côté iOS, Kotlin et Jetpack Compose côté Android. Fidélité maximale aux conventions de chaque système, accès immédiat aux nouveautés, et deux fois le travail.

Le cross-platform compilé partage l'essentiel du code. React Native pilote de vrais composants natifs depuis du JavaScript ou du TypeScript. Flutter dessine lui-même son interface avec son propre moteur de rendu et le langage Dart. .NET MAUI joue le même rôle pour les équipes C#. Kotlin Multiplatform partage la logique métier en laissant l'interface native de chaque côté. Dans tous les cas, les fonctions très spécifiques repassent par du code natif.

L'hybride empaquette une application web dans une coque native, avec un outil comme Capacitor, ou s'en passe avec une application web progressive installable. C'est le moins cher, et le plus limité dès qu'il faut du matériel, du hors-ligne sérieux ou une fluidité irréprochable.

Approche Bases de code Accès aux capacités du téléphone Fidélité au système Compétences requises Bon choix quand
Natif Deux Complet et immédiat Maximale Swift et Kotlin L'application est le produit
Cross-platform compilé Une, plus des ponts Large, via des greffons Bonne à très bonne JS/TS, Dart ou C# Deux plateformes, une équipe
Hybride ou web progressive Une, partagée avec le web Limité Faible Compétences web existantes L'application prolonge un site

Le billet précédent raconte ce que le pari du web mobile a coûté à Facebook en 2012, et pourquoi l'entreprise est revenue au natif. La leçon vaut ici : ce n'est pas la bibliothèque qui coûte cher quand on se trompe, c'est la forme choisie.

Le mobile est le seul endroit où le choix de la stack décide aussi du nombre d'équipes que tu vas financer pendant cinq ans.

Les contraintes qui décident à ta place

Le maître d'œuvre a beau préférer l'acier. Si le maître d'ouvrage veut ouvrir dans dix mois, si les entreprises disponibles dans la région ne travaillent que le béton, si l'exploitant qui reprendra le bâtiment ne sait entretenir que ça, et si le budget est verrouillé, le débat sur les matériaux est déjà tranché. Le rôle de l'architecte est de le constater à voix haute plutôt que de le nier.

La moitié des choix de stack ne se prennent pas, ils se constatent. Le travail de l'architecte est de savoir lesquels, et de garder son énergie pour ceux qui restent ouverts.

Le délai

La bonne solution technique n'est pas toujours celle qui rentre dans le calendrier. Prends deux services qui doivent échanger des messages. Une file de messages découple les deux, absorbe les pics et permet de rejouer ce qui a échoué. Elle demande aussi un intergiciel à installer, à superviser et à sauvegarder, plus la gestion des rejeux, de l'idempotence et des messages morts.

Un appel REST, lui, est disponible tout de suite et compris par tout le monde. Si la mise en service est dans dix semaines, l'appel REST gagne, et la file de messages devient une dette écrite noir sur blanc dans le document d'architecture. Le détail de cet arbitrage fait l'objet d'un billet entier plus loin dans la série.

Les compétences de l'équipe

Le chef de chantier travaille avec les corps de métier disponibles, pas avec ceux d'un catalogue. Ton équipe est autonome sur la JVM et tu choisis Go parce que c'est techniquement plus adapté à ce service. Tu viens d'ajouter une montée en compétence, des revues de code sans relecteur expérimenté, et une astreinte que personne ne sait tenir.

Parfois le besoin le justifie, et on assume la formation. Souvent non. Le test qui tranche : qui débogue ce service à trois heures du matin, et avec quel niveau de maîtrise ?

Ceux qui exploiteront le produit

Une fois le bâtiment livré, c'est l'exploitant qui vient relever les compteurs et changer les filtres de la ventilation, avec sa gestion technique du bâtiment et ses habitudes. L'entreprise de gros œuvre, elle, a replié ses grues depuis longtemps.

En informatique, la scène est la même. Indice : ce ne sont pas les développeurs. Une équipe d'exploitation va superviser ton produit, appliquer ses correctifs, sauvegarder ses données et le redémarrer proprement. Si elle n'a jamais opéré ton runtime, ton choix élégant devient une boîte noire dès la mise en production. Les quatre questions à poser avant de trancher : comment on l'observe, comment on le met à jour, comment on le sauvegarde, et qui sait le faire.

Le coût, et l'arbitrage acheter ou construire

Le coût d'une stack ne se résume pas aux licences. Il additionne l'hébergement, la disponibilité et le prix des développeurs sur ton marché, le coût d'exploitation, et le coût de sortie le jour où il faudra en changer.

Vient alors la question qui économise le plus de temps sur un projet : faut-il écrire ce composant, ou en prendre un existant ? La règle tient en une phrase : construis ce qui te distingue, achète le reste.

Acheter ou construire
Commodité, offre mûre
Authentification, paiement, envoi de courriels, recherche plein texte. Tu intègres un composant existant. Écrire le tien coûte cher et ne te distingue de personne.
Commodité, offre immature
Prends le produit le plus proche et contourne l'écart, ou repousse le besoin. Développer une commodité que le marché va livrer dans six mois est un mauvais placement.
Différenciant, offre mûre
Intègre la brique technique, garde ta valeur au-dessus. Le moteur de recherche est un produit, la façon dont tu classes tes résultats est ton métier.
Différenciant, offre immature
Construis. C'est le seul quadrant qui mérite ton budget de développement et tes meilleurs profils, parce que c'est ce que personne ne peut te vendre.

Les quatre contraintes se résument à quatre questions, et à un signe qui indique qu'elles ont déjà tranché.

Contrainte La question à poser Le signe qu'elle a déjà décidé
Délai Quelle est la date d'ouverture, et est-elle négociable ? La solution élégante demande une brique d'infrastructure à monter d'abord
Compétences Combien de développeurs sont autonomes sur cette pile aujourd'hui ? La réponse est « ils apprendront vite »
Exploitation Qui supervise, met à jour et redémarre en production ? Personne dans l'équipe d'exploitation n'a déjà opéré ce runtime
Coût Que coûte ce composant sur trois ans, sortie comprise ? Le besoin est une commodité que trois éditeurs vendent déjà

L'enchaînement se met en arbre, et il se parcourt en quelques minutes par composant.

flowchart TD A["Un composant à réaliser"] --> B{"Le besoin est-il différenciant ?"} B -- non --> C["Intégrer un composant existant"] B -- oui --> D{"L'équipe maîtrise-t-elle une pile qui sait le faire ?"} D -- oui --> E{"L'exploitation sait-elle la supporter en production ?"} D -- non --> F{"Le délai laisse-t-il le temps de monter en compétence ?"} F -- non --> G["Rester sur la pile connue et documenter la dette"] F -- oui --> E E -- oui --> H["Retenir la pile et écrire la justification"] E -- non --> I["Choisir une pile supportable ou financer l'exploitation"] classDef q fill:#ede9e1,stroke:#b5651d,color:#1a1a1a classDef ok fill:#b5651d,stroke:#8a4d16,color:#f5f2ec classDef warn fill:#a0413e,stroke:#8a4d16,color:#f5f2ec class B,D,E,F q class A,C,H ok class G,I warn

Scénario B : Twitter passe de Ruby on Rails à la JVM

Twitter a démarré sur Ruby on Rails, et c'est ce qui lui a permis d'exister. Le produit a trouvé son public, puis le trafic a dépassé de plusieurs ordres de grandeur ce pour quoi la pile d'origine avait été montée.

En avril 2011, le blog d'ingénierie publie « Twitter Search is Now 3x Faster ». L'équipe y décrit deux mouvements successifs. D'abord le remplacement du moteur d'indexation, qui passe d'une base MySQL à un index temps réel bâti sur Lucene. Ensuite le remplacement de la façade Ruby on Rails par un serveur Java baptisé Blender. Résultat annoncé dans le billet : des latences de recherche divisées par trois.

Le point important est la raison donnée par l'équipe elle-même. Le billet parle de servir un trafic en croissance continue, d'améliorer la latence et la disponibilité perçues, et de pouvoir livrer plus vite de nouvelles fonctions de recherche. Personne n'écrit « Ruby est lent ». On décrit un profil de charge que l'architecture d'origine ne servait plus.

En août 2013, un second billet, « New Tweets per second record, and how! », raconte la suite. Twitter y explique le passage du monolithe Ruby à un ensemble de services sur la JVM, à l'occasion d'un pic mesuré à 143 199 tweets par seconde le 3 août 2013, pendant la diffusion d'un film à la télévision japonaise. Le changement de langage venait avec un redécoupage complet en services.

Deux nuances rendent ce cas utile plutôt que caricatural. Twitter a investi dans son propre outillage JVM, dont la bibliothèque Finagle publiée en source ouverte, un coût que la plupart des équipes ne peuvent pas payer. Et Rails a fait son travail au départ : il a porté le produit jusqu'au stade où le problème d'échelle devient réel.

Le bon choix de stack au démarrage n'est pas celui qui tiendra dix ans. C'est celui qui te permet d'exister assez longtemps pour avoir le problème de le remplacer.

Points clés

  • La stack se décompose en trois décisions distinctes : le back, le front, le magasin de données. Les deux premières relèvent de ce billet, la troisième du suivant.
  • Côté back, cinq familles couvrent la majorité des cas (JVM, .NET, Node.js, Python, PHP), et le critère utile est l'adéquation au type d'application, pas un classement absolu.
  • Côté front, l'arbitrage porte sur qui prend les décisions d'architecture : un framework complet les impose, une bibliothèque les laisse à ton équipe à chaque projet. Sur mobile, le choix engage le nombre de bases de code à financer.
  • Délai, compétences, exploitation et coût tranchent avant l'architecte dans la moitié des cas. Les nommer explicitement vaut mieux que de faire semblant de choisir.
  • Un choix de stack se documente : les candidats écartés, les critères décisifs, les conséquences acceptées, et la condition qui obligera à rouvrir le dossier.

Dans la série

Palier 2 : Dresser les plans. Domaine : Technology.

Pour aller plus loin