Aller au contenu principal

Les 12 facteurs : la méthode qui explique pourquoi une application casse en production

Une application démarre en local, les tests passent, la démo se déroule bien. Elle part sur un serveur et refuse de se lancer. Le code est le même, le comportement non.

Outils & environnement ·
Adel LATIBI
Adel LATIBI

Le Briefing Dev - les ressources et actus de la semaine, droit dans ta boîte chaque vendredi gratuitement.

En vous inscrivant, vous acceptez de recevoir notre newsletter. Désinscription possible à tout moment.

Ce décalage revient dans presque tous les premiers déploiements que j'accompagne en formation. La cause est rarement dans le code métier. Elle est dans la manière dont l'application est reliée à la machine qui l'exécute : un chemin absolu, un mot de passe écrit en dur, une dépendance installée à la main six mois plus tôt et oubliée depuis.

Une méthode publiée en 2011 par Heroku liste douze règles pour couper ces liens un par un. Elle s'appelle The Twelve-Factor App. Cet article la traduit en langage utilisable, avec pour chaque facteur l'erreur qu'il corrige, un exemple de code, et les pièges que je vois le plus souvent chez les personnes en reconversion et les développeurs juniors.

Le problème que ces douze règles résolvent

Prends une petite API Python posée dans un dossier sur ton disque. Elle lit un fichier de données situé dans /home/adel/projets/data/clients.csv. Elle se connecte à une base de données locale avec le mot de passe root écrit ligne 12. Elle utilise une bibliothèque installée un jour avec pip install, sans trace nulle part.

Sur ta machine, tout va bien. Ces trois éléments sont des dépendances invisibles à ton environnement personnel, et le serveur n'a ni ce chemin, ni cette base, ni cette bibliothèque. L'application ne démarre pas, et le message d'erreur pointe souvent sur autre chose que la cause réelle, ce qui transforme un déploiement de quinze minutes en soirée entière passée à lire des logs.

Le même mécanisme se rejoue à chaque étape suivante : quand un collègue clone le dépôt, quand la CI lance les tests, quand tu passes de un à trois serveurs. J'ai détaillé le versant humain de cette situation dans l'article sur la phrase « ça marche sur ma machine ». Les douze facteurs en sont la traduction technique.

D'où vient cette liste

Adam Wiggins, cofondateur de Heroku, publie le document en 2011. Heroku est alors une plateforme d'hébergement qui reçoit des milliers d'applications écrites par des développeurs différents, dans des langages différents. Certaines se déploient sans accroc, d'autres cassent systématiquement. Les douze facteurs sont la synthèse de ce que les premières faisaient et que les secondes ne faisaient pas.

Le texte a quinze ans et il n'a pas pris une ride, pour une raison simple : il ne mentionne aucun outil. Ni Docker, ni Kubernetes, ni GitHub Actions, aucun de ces noms n'existait ou ne comptait à l'époque. La méthode décrit des propriétés que ton application doit avoir, pas la technologie qui te sert à les obtenir. C'est ce qui la rend encore lisible aujourd'hui alors que la moitié des tutoriels de 2020 sont périmés.

Depuis fin 2024, le manifeste est passé en open source et une réécriture communautaire est en cours dans un dépôt GitHub public, avec des issues et des pull requests comme pour du code. Une remarque personnelle : je trouve la version d'origine plus utile pédagogiquement que les variantes étendues à quinze ou vingt facteurs qu'on croise parfois. Douze règles, on les retient. Vingt, on en applique six.

Les douze facteurs, regroupés par intention

La liste officielle est numérotée de I à XII dans un ordre qui n'aide pas à la mémoriser. Voici le même contenu réorganisé en quatre familles, ce qui rend l'ensemble beaucoup plus simple à tenir en tête.

Famille 1 : le code et ce dont il a besoin

Facteur I, une base de code unique. Un dépôt Git, une application. Le même code part en développement, en préproduction et en production, seule la configuration change. Deux dépôts pour une seule application signifient qu'on maintiendra deux versions divergentes d'ici trois mois.

Facteur II, dépendances déclarées et isolées. Tout ce que ton application utilise est écrit dans un fichier versionné : requirements.txt, package.json, composer.json. Et rien ne repose sur un outil supposé présent sur le système, comme curl appelé depuis un script ou une commande convert d'ImageMagick installée localement.

Facteur V, séparer build, release et run. Trois étapes distinctes. Le build fabrique un artefact à partir du code, la release y associe la configuration de l'environnement visé, le run exécute le résultat. La conséquence directe est qu'on ne modifie jamais un fichier directement sur le serveur de production. Si tu as déjà vu quelqu'un ouvrir nano en SSH pour corriger une ligne en prod, ce facteur existe pour ça.

Famille 2 : ce qui varie d'un environnement à l'autre

Facteur III, la configuration vit dans l'environnement. Tout ce qui diffère entre ta machine et le serveur sort du code : URL de base de données, clés d'API, adresse du service mail, niveau de log. Ces valeurs arrivent par variables d'environnement. Le test proposé par la méthode est efficace : est-ce que tu peux publier ton dépôt en public à l'instant, sans fuiter un seul secret ? Si la réponse est non, le facteur III n'est pas respecté.

Facteur IV, les services externes sont des ressources attachées. Base de données, cache Redis, stockage S3, service d'envoi de mails : chacun est joignable par une URL rangée dans la configuration. Remplacer une base PostgreSQL locale par une base managée doit se faire en changeant une variable, sans toucher au code. Cette règle rejoint le principe de couplage faible détaillé dans le hub des 20 principes de code.

Famille 3 : la manière dont l'application tourne

Facteur VI, des processus sans état. Ton application ne garde rien en mémoire entre deux requêtes. Pas de session stockée dans une variable globale, pas de fichier uploadé écrit dans un dossier temporaire local, pas de compteur en RAM. Tout ce qui doit survivre part dans une base ou un cache partagé. Ce facteur est le plus contraignant des douze, et celui qui rend possibles les quatre suivants.

Facteur VII, exposition par port. L'application est autonome et écoute sur un port. Elle n'a pas besoin qu'on la dépose dans un dossier d'Apache pour exister. Elle peut ensuite être placée derrière un reverse proxy, ce qui reste un choix d'infrastructure et pas une dépendance de l'application.

Facteur VIII, concurrence par les processus. Pour encaisser plus de trafic, on lance davantage d'instances au lieu d'acheter une machine plus grosse. C'est la différence entre scale-out et scale-up. Sans le facteur VI, cette règle est inapplicable : deux instances qui gardent chacune leurs propres sessions en mémoire déconnectent l'utilisateur une requête sur deux.

Facteur IX, jetabilité. Un processus démarre en quelques secondes et s'arrête proprement quand il reçoit un signal SIGTERM : il finit la requête en cours, ferme ses connexions, puis rend la main. Une application qui met deux minutes à booter rend chaque déploiement pénible et chaque autoscaling inutile.

Famille 4 : ce qui se passe autour

Facteur X, parité entre développement et production. Même version de base de données, même version de langage, même système partout. Développer sur SQLite et déployer sur PostgreSQL est la source d'un nombre impressionnant de bugs découverts au pire moment, parce que les deux moteurs ne traitent pas les dates, les types et les contraintes de la même façon.

Facteur XI, les logs sont un flux. L'application écrit ses événements sur la sortie standard et s'arrête là. Elle ne gère ni fichiers de log, ni rotation, ni archivage. C'est l'environnement d'exécution qui collecte ce flux et l'envoie où il faut. Un print() bien placé est déjà conforme au facteur XI, ce qui en fait le plus facile à appliquer de toute la liste.

Facteur XII, les tâches d'administration sont des processus ponctuels. Migration de base, script de nettoyage, création d'un compte administrateur : ces tâches tournent dans le même environnement que l'application, avec le même code et la même configuration. Elles sont versionnées avec le reste, pas copiées à la main sur le serveur.

À quoi ça ressemble dans le code

Voici la version qui casse au déploiement, en Python :

import psycopg2
 
conn = psycopg2.connect(
    host="localhost",
    dbname="boutique",
    user="root",
    password="motdepasse123",
)
 
UPLOAD_DIR = "/home/adel/projets/boutique/uploads"

Trois facteurs sont violés ici : la configuration est dans le code (III), le service est codé en dur au lieu d'être attaché (IV), et le stockage local des fichiers empêche de lancer une deuxième instance (VI). La version conforme :

import os
import psycopg2
 
DATABASE_URL = os.environ["DATABASE_URL"]
S3_BUCKET = os.environ["S3_BUCKET"]
PORT = int(os.environ.get("PORT", 8000))
 
conn = psycopg2.connect(DATABASE_URL)

Le os.environ["DATABASE_URL"] sans valeur par défaut est délibéré : si la variable manque, l'application refuse de démarrer immédiatement plutôt que de tomber en panne à la première requête utilisateur. Le port lu depuis l'environnement avec un repli sur 8000 couvre le facteur VII, la plupart des plateformes d'hébergement imposant leur propre port.

Côté fichiers, un docker-compose.yml minimal montre la même logique appliquée à l'ensemble du projet :

services:
  api:
    build: .
    environment:
      DATABASE_URL: ${DATABASE_URL}
      S3_BUCKET: ${S3_BUCKET}
    ports:
      - "8000:8000"
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

La version de PostgreSQL est épinglée en 16 pour tenir le facteur X, et aucun secret n'apparaît dans le fichier versionné. Si cette partie te parle mais que la manipulation reste floue, c'est le contenu de la formation Docker pour développeurs web, qui reprend cette mise en conteneur pas à pas.

Les pièges les plus fréquents

Le fichier .env commité par accident. C'est l'erreur numéro un, et elle arrive à des gens expérimentés. Le fichier .env sert à charger des variables en local, il n'a rien à faire dans Git. La règle qui fonctionne : .env dans le .gitignore dès la première ligne du projet, et un .env.example versionné qui liste les clés attendues avec des valeurs bidon. Un secret poussé une fois sur GitHub doit être considéré comme compromis, même après suppression du commit, et il faut le révoquer.

Confondre configuration et paramètre métier. Le facteur III vise ce qui change d'un environnement à l'autre. Le taux de TVA, la liste des statuts de commande ou le nombre d'articles par page ne sont pas de la configuration d'environnement, ce sont des règles de ton application. Les sortir dans des variables produit un fichier de trente lignes que personne ne sait relire six mois plus tard.

Appliquer les douze facteurs à un projet qui n'en a pas besoin. Un script d'automatisation qui tourne une fois par nuit sur une seule machine ne gagne rien à être découpé en processus sans état exposant un port. Les facteurs II, III et XI restent utiles partout, les autres ont un sens quand l'application est un service qui doit rester disponible.

Croire qu'un conteneur suffit. Une application avec des mots de passe en dur reste non conforme une fois mise dans une image Docker, et l'image devient même le problème puisqu'elle transporte les secrets partout où elle est publiée. Docker facilite l'application des facteurs, il ne les applique pas à ta place.

Donner tous les droits au compte de service. La méthode d'origine ne traite pas la sécurité en profondeur, ce qui laisse souvent l'utilisateur de base de données en superutilisateur alors qu'il fait des lectures et deux écritures. Le sujet est développé dans l'article sur le principe de moindre privilège.

Repousser le facteur X à plus tard. C'est celui que je vois le plus souvent sacrifié sur les projets d'apprentissage, parce qu'installer PostgreSQL en local paraît compliqué le premier jour. Le coût arrive plus tard, sous forme de bugs qui n'existent qu'en production et qu'on ne peut pas reproduire.

Par où commencer si tu pars de zéro

Reprends un projet existant, celui que tu montres en entretien ou celui sur lequel tu travailles en ce moment, et traite les facteurs dans cet ordre : II, III, XI, puis X. Ces quatre-là demandent une soirée sur un projet de taille moyenne et éliminent la majorité des surprises au déploiement.

Le facteur VI vient ensuite, parce qu'il touche à l'architecture et demande parfois de remplacer un dossier d'uploads par un service de stockage. Les autres suivent d'eux-mêmes le jour où tu mets en place un pipeline d'intégration continue, sujet couvert par la formation CI/CD avec GitHub Actions. L'ensemble du parcours est visible dans le catalogue DevOps et déploiement.

Questions fréquentes

Les 12 facteurs sont-ils encore d'actualité en 2026 ?

Oui, et la meilleure preuve est que les outils apparus depuis 2011 les appliquent par défaut. Un conteneur Docker impose l'isolation des dépendances, une plateforme d'hébergement moderne impose la configuration par variables d'environnement et la lecture des logs sur la sortie standard. Le texte d'origine est passé en open source fin 2024 pour une mise à jour communautaire, signe que la base reste jugée solide.

Faut-il faire des microservices pour respecter les 12 facteurs ?

Non. La confusion est courante parce que les deux sujets arrivent ensemble dans les articles sur le cloud. Une application monolithique respecte parfaitement les douze facteurs, et c'est même le point de départ que je recommande. Découper en microservices est une décision d'architecture qui apporte ses propres difficultés, indépendante de cette méthode.

Où stocker les variables d'environnement en production ?

Dans le mécanisme prévu par ton hébergeur : panneau de configuration d'une plateforme managée, secrets de dépôt sur GitHub Actions, gestionnaire de secrets d'un fournisseur cloud, ou fichier d'environnement lisible uniquement par l'utilisateur système sur un serveur que tu administres. Le point commun de ces solutions est que la valeur arrive au processus au moment du lancement, sans jamais transiter par Git.

Comment gérer les fichiers uploadés avec le facteur VI ?

En les envoyant vers un service de stockage objet plutôt que sur le disque de la machine qui exécute l'application. Le disque local est acceptable pour un cache reconstructible ou un fichier temporaire supprimé dans la même requête. Dès qu'un fichier doit encore exister demain, il part ailleurs, sinon il disparaît au premier redéploiement et devient invisible pour la deuxième instance.

Un projet personnel doit-il respecter les douze facteurs ?

Un projet destiné à être montré en entretien gagne à respecter au moins les facteurs II, III, X et XI. Un dépôt avec des dépendances déclarées, un fichier .env.example, aucun secret dans l'historique et un README qui explique le lancement en deux commandes se distingue immédiatement de la moyenne des projets de portfolio.

Quelle différence entre les 12 facteurs et le clean code ?

Les principes de clean code portent sur la lisibilité et la structure interne du code : nommage, taille des fonctions, responsabilités des classes. Les douze facteurs portent sur la relation entre l'application et son environnement d'exécution. Un code impeccable peut violer les douze facteurs, et une application conforme aux douze facteurs peut contenir des fonctions de trois cents lignes. Les deux sujets se complètent sans se recouvrir.
Crédits images : graphiqastock sur Magnific Tous les crédits

Vous êtes expert ?

Partagez votre expertise sur notre blog

Tutoriel, retour d'expérience, analyse - publiez un article invité et gagnez en visibilité.

Écrire pour nous