« Ça marche sur ma machine » (en anglais : “Works on my machine”) — la phrase classique du développeur qui ne peut pas reproduire le bug dans son environnement local, alors que le bug se manifeste régulièrement chez d'autres membres de l'équipe ou en production. La situation survient en raison de différences de configuration, de versions de dépendances, de système d'exploitation ou de données entre la machine du développeur et l'environnement où le bug est reproduit. Selon le Stack Overflow Survey 2023, 58 % des développeurs disent cette phrase au moins une fois par mois, et 31 % — chaque semaine. Comprendre pourquoi le code ne fonctionne pas partout de la même manière et comment standardiser l'environnement.
Points clés
« Ça marche sur ma machine » — la phrase que prononce le développeur lorsqu'un collègue ou un testeur signale un bug, mais que le bug ne se reproduit pas sur sa machine. Extérieurement, cela ressemble à un déni du problème, mais techniquement, la situation est réelle : le code peut effectivement fonctionner dans un environnement et échouer dans un autre. Un seul bit de configuration différent et le comportement de l'application change radicalement.
La phrase est devenue un mème dans la communauté informatique parce qu'elle est à la fois vraie et inutile. Du point de vue du développeur, le code fonctionne vraiment sur sa machine. Du point de vue de l'équipe, le problème existe et doit être résolu, pas justifié. L'humour de la situation réside dans le fait que le développeur dit la vérité, mais cette vérité n'aide pas à corriger le bug. Le mème est si populaire que des milliers de publications sur Reddit, XKCD et les conférences DevOps lui sont consacrées.
Du point de vue des processus, la phrase « ça marche sur ma machine » est un indicateur de problèmes de reproductibilité de l'environnement. Si deux développeurs ne peuvent pas obtenir le même résultat sur le même code, alors le processus de configuration de l'environnement n'est pas standardisé. La pratique DevOps stipule que l'environnement doit être reproductible par une seule commande à partir du dépôt, sans actions manuelles.
L'environnement local du développeur est presque toujours différent de la production. Le développeur utilise macOS ou Windows, tandis que le serveur fonctionne sous Linux. Les différents systèmes d'exploitation ont des systèmes de fichiers, des encodages, des temporisations de threads et des appels système différents. Même si les deux environnements sont Linux — la version du noyau, glibc, OpenSSL peuvent être différents.
La deuxième cause — l'ensemble des logiciels installés. Sur la machine du développeur, une version globale de Node.js 20 peut être installée, tandis que dans la configuration CI/CD, la version 18 est spécifiée. Ou le développeur utilise PostgreSQL 16 localement, et en production — PostgreSQL 14. Les différences dans les versions mineures ne sont souvent pas perceptibles, mais les mises à jour majeures peuvent modifier le comportement des requêtes SQL. Selon npm Inc., 67 % des bugs liés aux dépendances sont causés par des différences dans les versions de patch.
La troisième cause — les conditions réseau. Sur la machine locale, il n'y a pas de latence, de limites de bande passante ou de problèmes DNS. En production, toute requête vers une API externe peut prendre 500 ms au lieu de 5 ms. Les timeouts, la logique de relance, les conditions de concurrence — tous ces problèmes n'apparaissent que sous charge réelle et dans des conditions réseau réelles. L'émulation réseau via des outils comme Toxiproxy aide à identifier ces problèmes avant le déploiement.
La première cause — l'absence de données. Le développeur travaille avec des fixtures de test, tandis qu'en production, il y a des millions d'enregistrements avec des valeurs inattendues. NULL dans un champ que le développeur considérait comme obligatoire, un caractère Unicode dans un nom, une chaîne trop longue — tout cela peut provoquer des bugs non reproductibles sur la base de données locale avec des données synthétiques.
La deuxième cause — des drapeaux de compilation et de build différents. La build de release (Release/Distribution) peut différer de la build de debug (Debug). Les optimisations du compilateur, la suppression des logs de debug, l'inlining de fonctions — tout cela peut cacher ou, au contraire, révéler des bugs. Exemple typique : dans la build de debug, un assert fonctionne, mais échoue dans la release en raison d'un ordre différent d'initialisation des variables.
La troisième cause — le cache local et les fichiers temporaires. Le développeur peut ne pas remarquer le bug parce que d'anciens scripts sont mis en cache dans le navigateur, des données obsolètes sont sauvegardées dans Redis, et des fichiers temporaires d'exécutions précédentes traînent dans le système de fichiers. Une exécution propre (mode incognito, vidage du cache, fresh install) reproduit souvent le bug qui ne se manifestait pas « tout seul ».
La quatrième cause — les conflits entre dépendances globales et locales. Des outils comme Ruby gems, Python pip, Node.js npm peuvent avoir des paquets installés globalement qui «aident» le code à fonctionner localement mais sont absents en production. L'utilisation d'environnements virtuels (virtualenv, venv, nvm) isole le projet des installations globales et rend l'environnement reproductible.
La phrase « ça marche sur ma machine » détruit la confiance dans l'équipe. Si un développeur ne parvient pas régulièrement à reproduire des bugs, les collègues commencent à douter de sa compétence ou de la rigueur de ses tests. Avec le temps, cela mène au micro-management : chaque modification nécessite une vérification par un second développeur, ce qui ralentit le développement. Selon Google Project Aristotle, la sécurité psychologique dans l'équipe affecte directement la productivité, et les disputes constantes à propos de l'environnement sont l'un des facteurs qui la réduisent.
Le deuxième problème — le ralentissement des revues de code. Si un développeur ne peut pas reproduire le bug localement, il peut rejeter la pull request d'un collègue avec les mots « ça marche chez moi — donc le problème vient de toi ». Cela provoque des conflits et retarde la livraison des fonctionnalités. La standardisation de l'environnement élimine ce conflit : si les deux développeurs travaillent dans le même conteneur Docker, la question « chez qui ça marche » perd son sens.
Le troisième problème — la perte de bugs dans le suivi. Les bugs qui « ne sont pas reproduits chez le développeur » sont souvent fermés avec la mention « Impossible à reproduire » (Cannot Reproduce). Un mois plus tard, le bug refait surface en production, et sa correction coûte 10 fois plus cher. Règle : si un bug est reproduit chez au moins une personne — il existe, indépendamment du fait qu'il fonctionne ou non chez le développeur.
Le premier moyen et le plus efficace — Docker. L'ensemble du projet doit pouvoir être lancé via docker-compose up sans actions supplémentaires. Base de données, cache, file d'attente de messages, serveur web — tout est démarré dans des conteneurs. Le développeur installe uniquement Docker et Git. Le reste — à l'intérieur des conteneurs. Cela garantit que tous les membres de l'équipe ont le même environnement, indépendamment du système d'exploitation.
Le deuxième moyen — les gestionnaires de versions. Si Docker n'est pas possible (restrictions de licence, infrastructure legacy), utilisez nvm (Node.js), rbenv (Ruby), pyenv (Python), sdkman (Java). Les gestionnaires de versions permettent de changer les versions des langages et des outils au sein du projet. Les fichiers .nvmrc, .ruby-version, .python-version doivent être dans le dépôt et vérifiés par CI/CD.
Le troisième moyen — Vagrant pour les machines virtuelles. Vagrant démarre une machine virtuelle avec un système d'exploitation et une configuration spécifiés sur VirtualBox ou VMware. À l'intérieur de la machine virtuelle, toutes les dépendances sont installées via des scripts de provisionnement (shell, Ansible, Puppet). Vagrant est plus lourd que Docker mais offre un isolement complet au niveau du système d'exploitation — utile pour les projets dépendant d'une version spécifique du noyau Linux.
Le quatrième — Makefile et scripts d'amorçage. Même un simple Makefile avec les cibles install, test, build, clean peut standardiser les tâches routinières. La commande make install doit installer toutes les dépendances, configurer la base de données et créer des données de test. Un point d'entrée unique pour tous les développeurs élimine les erreurs manuelles lors de la configuration de l'environnement.
L'outil principal — les fichiers de verrouillage des dépendances. package-lock.json (npm), yarn.lock (Yarn), Podfile.lock (CocoaPods), pubspec.lock (Flutter) fixent les versions exactes de chaque paquet. Sans fichier de verrouillage, deux développeurs installant les dépendances à des moments différents peuvent obtenir des versions mineures différentes. Le fichier de verrouillage doit être dans le dépôt et ne pas être modifié manuellement.
Le deuxième outil — .env.example dans le dépôt. Un fichier modèle de variables d'environnement avec des commentaires. Le développeur le copie dans .env et remplit ses valeurs. Le pipeline CI/CD vérifie que toutes les variables obligatoires sont définies. Selon GitLab 2023, les équipes qui utilisent .env.example réduisent le nombre d'incidents liés aux variables d'environnement de 40 %.
Le troisième outil — les hooks pre-commit. Une vérification automatique exécutée avant chaque commit : linter, formateur, vérification de types, tests. Si les hooks sont configurés de la même manière chez tous les développeurs, les erreurs de formatage ou de types qui ont « passé sur la machine locale » n'arriveront pas en production. Husky pour JavaScript et pre-commit pour Python sont des solutions populaires.
Le quatrième — un pipeline CI/CD qui exécute les tests dans un environnement propre. Si les tests passent dans le CI mais pas localement — le problème vient de la configuration de l'environnement local. Si les tests échouent dans le CI — la pull request n'est pas fusionnée. Cette règle stricte empêche les bugs « qui fonctionnent localement » d'entrer dans la branche principale.
Questions fréquentes
C'est une réaction de défense : le développeur passe beaucoup de temps à déboguer, et entendre que le code ne fonctionne pas est psychologiquement douloureux. La phrase donne le temps de « changer de cap » et de commencer à chercher la cause sans sentiment de culpabilité.
Demandez-lui de reproduire le bug dans un environnement propre (clean install, mode incognito). S'il ne se reproduit pas — comparez les versions des dépendances et les variables d'environnement. Si cela n'aide pas — lancez un environnement Docker identique à la production.
Docker fournit un conteneur isolé avec une configuration fixe qui fonctionne de la même manière sur n'importe quel système d'exploitation. Tous les développeurs utilisent le même Dockerfile, donc l'environnement est identique. Si le bug ne se reproduit pas dans le conteneur — le problème vient vraiment du code, pas du système.
Le fichier de verrouillage fixe les hachages et versions exacts de toutes les dépendances transitives. Même si une nouvelle version de dépendance sort dans le registre de paquets, l'installation via le fichier de verrouillage garantit que chaque développeur obtient le même ensemble de paquets que les autres.
Vagrant avec VirtualBox est justifié si le projet dépend de modules spécifiques du noyau du système d'exploitation ou nécessite un isolement complet au niveau du noyau. Pour 90 % des projets, Docker est plus léger, plus rapide et plus pratique. Le choix dépend de la profondeur de l'interaction du projet avec le système d'exploitation.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi