X. Parité dev/prod

Gardez le développement, la validation et la production aussi proches que possible

Historiquement, il y a eu un fossé conséquent entre le développement (un développeur qui fait des modifications sur un déploiement local de l’application) et la production (un déploiement de l’application accessible aux utilisateurs finaux). Ce fossé se manifeste de trois manières :

Les applications 12 facteurs sont conçues pour le déploiement continu (en) en gardant un fossé étroit entre le développement et la production. Si l’on regarde les trois fossés décrits plus haut :

Si l’on résume cela en un tableau :

Application traditionnelle Application 12 facteurs
Temps entre les déploiements Semaines Heures
Auteurs du code et ceux qui le déploient Des personnes différentes Les mêmes personnes
L'environnement de développement et celui de production Divergents Aussi similaires que possible

Les services externes, tels que la base de données, la file de messages, ou le cache sont des éléments importants de la parité développement/production. La plupart des langages fournissent des bibliothèques qui simplifient l’accès à ces services externes, en fournissant des adaptateurs pour différents types de services. Voici quelques exemples dans le tableau ci-dessous.

Type Langage Librairie Adaptateurs
Base de données Ruby/Rails ActiveRecord MySQL, PostgreSQL, SQLite
File de messages Python/Django Celery RabbitMQ, Beanstalkd, Redis
Cache Ruby/Rails ActiveSupport::Cache Mémoire, système de fichiers, Memcached

Les développeurs trouvent parfois agréable d’utiliser des services externes légers dans leur environnement local, alors qu’un service externe plus sérieux et robuste est utilisé en production. Par exemple, utiliser SQLite en local, et PostgreSQL en production; ou bien, durant le développement, mettre les données en cache dans la mémoire des processus locaux, et utiliser Memcached en production.

Les développeurs des applications 12 facteurs résistent au besoin d’utiliser des services externes différents entre le développement local et la production, même lorsque les adaptateurs permettent d’abstraire en théorie beaucoup de différences entre les services externes. Les différences entre les services externes signifient que de petites incompatibilités surviennent, ce qui va faire que du code qui fonctionnait et qui passait les tests durant le développement ou la validation ne fonctionnera pas en production. Ce type d’erreurs crée de la friction en défaveur du déploiement continu. Le coût de cette friction et son impact négatif sur le déploiement continu est extrêmement élevé lorsqu’il est cumulé sur toute la vie de l’application.

Les services locaux légers sont moins attirants aujourd’hui qu’ils ne l’étaient autrefois. Les services externes modernes tels que Memcached, PostgreSQL, et RabbitMQ ne sont pas difficiles à installer et à faire fonctionner grâce aux systèmes de paquets modernes comme Homebrew et apt-get. Autre possibilité, des outils de provisionnement comme Chef et Puppet, combinés à des environnements virtuels légers comme Docker et Vagrant permettent aux développeurs de faire fonctionner des environnements locaux qui reproduisent de très près les environnements de production. Le coût d’installation et d’utilisation de ces systèmes est faible comparé aux bénéfices d’une bonne parité développement/production et du déploiement continu.

Les adaptateurs à ces différents systèmes externes sont malgré tout utiles, car ils rendent le portage vers de nouveaux services externes relativement indolores. Mais tous les déploiements de l’application (environnement de développement, validation, production) devraient utiliser le même type et la même version de chacun de ces services externes.