Aller au contenu principal

"Ça marche sur ma machine" : la phrase qui coûte une carrière, et comment ne plus la prononcer

Tu l'as dite. On l'a tous dite. Et à chaque fois, c'est ton crédit professionnel qui prend un coup. Voici pourquoi cette phrase apparaît, et la méthode pour l'éliminer définitivement de ton vocabulaire.

Soft skills & organisation ·
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.

Le contexte est toujours le même. Ton code passe en local, tu pousses, tu mergues. Quelqu'un dans l'équipe pull la branche, lance le projet, et plante au démarrage. Ta première phrase, presque réflexe : "bizarre, chez moi ça marche".

Toi, tu sais que cette phrase est mauvaise. Elle te fait passer pour quelqu'un qui ne maîtrise pas son environnement. Elle exaspère tes collègues, qui doivent perdre une heure à comprendre pourquoi leur setup diffère du tien. Et si elle revient trois fois dans la même semaine, tu sens bien que ton crédit baisse silencieusement.

Le problème, ce n'est pas que tu es négligent. C'est que personne ne t'a jamais expliqué qu'un projet en local et un projet "qui marche partout", ce n'est pas la même chose. On t'a appris à coder, pas à rendre ton code portable. Ces compétences-là ne sont presque jamais enseignées dans les formations, alors qu'elles font la différence entre un dev qu'on garde et un dev qu'on tolère.

Cet article démonte les causes réelles du "ça marche chez moi", et te donne les six réflexes qui te permettent d'éliminer 95% des cas. Pas un cours sur Docker, pas un plaidoyer pour Kubernetes. Une méthode concrète pour des projets reproductibles dès le premier commit.

Le problème : ton "local" est une boîte noire

Quand tu codes, tu utilises ta machine. Ta machine a Node 20.5, un PostgreSQL 16 installé via Homebrew, une variable d'environnement API_KEY dans ton .zshrc, un fichier .env que tu as oublié de mentionner dans le README, une version de ImageMagick installée il y a deux ans pour un autre projet.

Quand quelqu'un d'autre clone ton repo, il a Node 18, pas de PostgreSQL, pas ta variable d'environnement, pas ton .env, et une autre version de ImageMagick. Il ne sait rien de ces différences. Tu non plus, parce que tout cela est invisible pour toi : ce sont les conditions implicites de ton setup.

Le "ça marche chez moi" n'est jamais un mensonge. C'est une vérité partielle : ça marche dans des conditions précises que personne n'a documentées. Le boulot, ce n'est pas d'apprendre à débugger plus vite après. C'est de rendre ces conditions explicites avant.

Le coût caché est lourd. Une heure perdue par chaque nouveau collègue qui clone le projet. Une heure perdue à chaque déploiement bancal. Une heure perdue quand toi-même tu changes de machine et que ton projet ne tourne plus. Sur un an, ce sont des semaines.

Le principe : un projet portable rend ses conditions explicites

Un projet portable, c'est un projet où tout ce qui doit être présent pour qu'il tourne est nommé, versionné et reproductible. Pas oralement, pas dans un Slack, pas dans la tête de l'auteur original. Dans le repo.

Il y a six couches sur lesquelles ton projet peut dépendre de ta machine. Tu les traites une par une.

1. La version du runtime

Tu codes avec Node 20, ton collègue lance avec Node 18, ça plante sur une syntaxe récente. Tu codes avec Python 3.12, lui avec 3.9, certaines libs explosent.

La solution : tu fixes la version dans un fichier dédié. Pour Node, c'est .nvmrc avec une ligne comme 20.10.0. Pour Python, c'est .python-version ou la section requires-python dans pyproject.toml. Pour PHP, composer.json a un champ "require": {"php": "^8.3"}. Pour Java, le pom.xml précise la version. Cinq secondes pour ajouter ce fichier, des heures économisées.

2. Les dépendances avec leurs versions exactes

Tu installes une lib en local, elle marche, tu commit. Six mois plus tard, ton collègue installe la même lib mais récupère une version mineure plus récente, qui a changé un comportement. Mystère, ça plante.

Le réflexe : tu commit toujours ton lockfile (package-lock.json, yarn.lock, pnpm-lock.yaml, composer.lock, poetry.lock, Pipfile.lock). Le lockfile fige les versions exactes de toutes les dépendances, y compris les sous-dépendances. Sans lockfile commit, ton projet n'est pas reproductible.

3. Les variables d'environnement

Les clés API, les chaînes de connexion, les flags. Tu les as dans ton .env local, mais le .env est (à juste titre) dans le .gitignore. Ton collègue clone, lance, plante avec un message "undefined" sur une variable qu'il n'a aucun moyen de connaître.

La solution standard : un fichier .env.example commit dans le repo, qui liste toutes les variables nécessaires avec une valeur factice ou vide. Le collègue le copie en .env, remplit ce qu'il manque, et son projet démarre. Bonus : ce fichier sert de documentation vivante des dépendances de configuration.

4. Les services externes (base, cache, queue)

Ton projet a besoin de PostgreSQL, Redis, un broker de messages, un service de mail. Tu as tout installé en local via Homebrew ou apt. Personne d'autre n'a la même chose.

La solution moderne : un docker-compose.yml qui démarre tous ces services avec une commande. Plus de "il te faut PostgreSQL 16 avec l'extension uuid-ossp", juste docker compose up et tu as tout. Si Docker te fait peur, l'article sur pourquoi Docker est devenu incontournable explique le pourquoi avant le comment.

5. Le système d'exploitation

Tu codes sur macOS, tes collègues sur Linux ou Windows. Les chemins de fichiers diffèrent (/ vs \), les fins de ligne diffèrent (LF vs CRLF), certaines commandes ne sont pas dispo partout.

Les bons réflexes : tu utilises systématiquement la lib de gestion de chemins de ton langage (path.join en Node, os.path.join ou pathlib en Python). Tu mets un .gitattributes avec * text=auto pour normaliser les fins de ligne. Et si l'équipe est mixte macOS/Windows, Docker règle 90% des problèmes restants.

6. Le README qui explique comment démarrer

Le dernier rempart. Même avec tous les fichiers ci-dessus, il faut une page qui explique l'ordre des opérations. Cloner, installer les dépendances, copier le .env, lancer Docker, faire les migrations, lancer le serveur. Cinq à dix commandes, dans l'ordre.

Un bon README de démarrage tient en une vingtaine de lignes. Si tu n'arrives pas à le faire tenir, c'est probablement que ton setup est trop compliqué.

Exemple : un projet Node + PostgreSQL portable en cinq fichiers

Voici ce que devrait contenir, au minimum, un projet Node typique pour être portable.

.nvmrc

20.10.0

.env.example

DATABASE_URL=postgresql://user:pass@localhost:5432/myapp
API_KEY=
NODE_ENV=development
PORT=3000

docker-compose.yml

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: myapp
    ports:
      - "5432:5432"
    volumes:
      - dbdata:/var/lib/postgresql/data

volumes:
  dbdata:

README.md (section démarrage)

## Demarrage

1. nvm use
2. npm install
3. cp .env.example .env
4. docker compose up -d
5. npm run migrate
6. npm run dev

Plus package-lock.json commit dans le repo. Total : cinq fichiers, dont aucun ne demande plus de cinq minutes à créer. Le projet est maintenant portable. N'importe qui peut le cloner et le faire tourner en moins de dix minutes sans avoir à te poser de questions.

Cette infrastructure minimale est ce qui distingue un projet professionnel d'un projet de stage. Et elle ne demande aucune compétence particulière, juste la discipline de la mettre en place dès le début.

Pièges classiques à éviter

Croire qu'un README suffit

"J'ai écrit un README détaillé, ça suffit." Non. Un README qui décrit oralement les versions à installer est moins fiable qu'un fichier .nvmrc. Le README peut devenir obsolète, le fichier de version reste à jour avec le code.

L'objectif est de minimiser la quantité d'instructions à suivre manuellement. Plus c'est automatisable, plus c'est fiable.

Ne pas tester son setup régulièrement

Ton projet marche chez toi en continu, donc tu ne testes jamais le démarrage à froid. Trois mois plus tard, quelqu'un clone, ça plante, et tu découvres une étape manquante.

Une fois par mois, tu clones ton propre projet dans un dossier temporaire et tu suis ton propre README. Tu trouveras toujours quelque chose à corriger. Si tu as la chance d'avoir une CI, elle fait ce travail pour toi à chaque commit.

Mettre des secrets dans le repo

Tu pousses ton vrai .env "juste pour cette fois", "ça simplifiera la vie des collègues". Tes clés API se retrouvent publiques. Tu passes ta soirée à les régénérer.

Le .env n'est jamais dans Git. Le .env.example oui, avec des valeurs factices. Cette règle ne souffre aucune exception.

Installer des dépendances système sans les documenter

Ton projet a besoin de libxml2, de ImageMagick, de ffmpeg, parce qu'une de tes libs en dépend. Tu l'as installé via Homebrew il y a un an, tu as oublié. Le collègue galère.

Toutes les dépendances système doivent être listées dans le README ou (mieux) inclues dans un Dockerfile. Si ton projet a besoin de plus de deux dépendances système, Docker devient la solution la plus sensée.

Croire que CI = projet portable

"Ma CI passe, donc le projet est portable." Pas forcément. Si ta CI utilise une image qui contient déjà tout ce qu'il faut, elle peut masquer des dépendances cachées. Le vrai test, c'est qu'un humain sur une machine vierge puisse lancer le projet.

La CI est un excellent compagnon de la portabilité, pas un substitut. Les deux travaillent ensemble.

Bricoler en prod plutôt qu'en local

Tu corriges un bug en prod en SSH-ant sur le serveur, tu changes une variable d'environnement à la main, tu remets en route. Ça marche le temps que ce serveur soit redémarré ou remplacé, puis le bug revient et personne ne se souvient du fix.

Tout changement passe par le code, le code passe par Git, et la prod est rebuild à partir de ce code. Sinon, la prod devient un autre "ça marche chez moi", à plus grande échelle et avec des utilisateurs réels.

Pour aller plus loin

La portabilité de projet est l'expression concrète du principe Fail Fast : tu fais échouer le démarrage explicitement quand une condition manque, plutôt que de laisser le projet planter de manière obscure plus tard. Pour aller plus loin sur la robustesse de tes projets, l'article sur les principes de single source of truth donne le cadre mental qui sous-tend tous ces réflexes.

Côté formation, la formation Docker pour développeurs web couvre la partie containerisation qui élimine 80% des sources du "ça marche chez moi". La formation CI/CD avec GitHub Actions ajoute le filet de sécurité qui détecte automatiquement les régressions de portabilité.

Questions fréquentes

Faut-il forcément utiliser Docker pour avoir un projet portable ?

Non. Pour un projet simple avec un seul runtime (juste Node, juste Python), un fichier de version, un lockfile et un .env.example suffisent souvent. Docker devient indispensable dès qu'il y a une base de données, plusieurs services, ou des dépendances système exotiques. Le critère décisionnel : si ton README de démarrage dépasse dix lignes, Docker simplifie la vie de tout le monde.

Comment réagir quand un collègue dit "chez moi ça marche" ?

Tu ne le contredis pas, tu enquêtes ensemble. Tu lui demandes sa version exacte du runtime, ses variables d'environnement, ses dépendances système. Dans 9 cas sur 10, vous trouverez une différence en moins de cinq minutes. Profite de l'occasion pour proposer d'ajouter cette différence à la doc ou aux fichiers de config du projet, comme ça le problème ne se reposera plus jamais.

Que faire si je suis seul sur mon projet, est-ce que la portabilité compte ?

Oui. Tu vas changer de machine tôt ou tard. Tu vas peut-être déployer ton projet sur un serveur. Tu vas reprendre ce projet dans deux ans en ayant oublié comment tu l'avais setup. Toutes ces situations sont des "ça marche chez moi" différés. Un projet portable est d'abord un cadeau que tu te fais à toi-même.

Faut-il commit le node_modules pour garantir la reproductibilité ?

Non, surtout pas. Le node_modules pèse des centaines de Mo et contient des binaires spécifiques à ta plateforme (qui peuvent ne pas marcher sur la machine de ton collègue). Le lockfile fait le travail de reproductibilité de manière propre. Le node_modules reste systématiquement dans le .gitignore.

Comment gérer un projet legacy déjà mal documenté ?

Tu n'essaies pas de tout réparer d'un coup. À chaque fois que tu touches le projet, tu ajoutes une couche de portabilité. Cette semaine, tu ajoutes le .nvmrc. La semaine prochaine, le .env.example. Plus tard, le docker-compose. C'est exactement l'esprit du Boy Scout Rule : tu laisses le code dans un état un peu meilleur que celui dans lequel tu l'as trouvé. En six mois, le projet est métamorphosé sans que ça n'ait jamais été un gros chantier.

Que penser des dev containers ?

Les dev containers (VS Code, GitHub Codespaces) sont l'aboutissement logique de la portabilité : ton environnement de dev complet est défini dans le repo et identique pour tout le monde. C'est plus lourd à mettre en place que les techniques de base de cet article, mais pour une équipe distribuée ou un projet qui dure dans le temps, c'est très puissant. Tu peux commencer simple et adopter les dev containers plus tard si l'équipe le justifie.

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