Aller au contenu principal
outilsUn projet qu'on peut reprendre

Un projet qu'on peut reprendre

Ce que ce chapitre apporte

  • Organiser un projet pour qu'un nouvel arrivant s'y retrouve sans explication orale.
  • Écrire un README qui répond aux questions qu'on se pose réellement.
  • Distinguer une dépendance déclarée d'une dépendance installée.
  • Expliquer à quoi sert un environnement isolé et ce qu'il évite.
  • Lire une contrainte de version et dire quelles versions elle accepte.
  • Justifier l'existence d'un fichier de verrouillage.
  • Sortir la configuration du code sans exposer de secret.
Où on va
« Ça marche chez moi » n'est pas une plaisanterie de développeur, c'est le symptôme d'un projet qui ne décrit pas ce dont il a besoin. Un projet reprenable tient dans une promesse simple : quelqu'un le clone, lit une page, tape deux commandes, et le programme tourne. Ce chapitre décrit ce qu'il faut pour tenir cette promesse, à savoir une structure lisible, des dépendances déclarées, un environnement isolé et une configuration qui n'est pas dans le code.

La structure minimale

Il n'y a pas de disposition universelle, mais il y a des attentes. Un projet qui les respecte se lit sans mode d'emploi.

projet/
├── README.md            # à quoi ça sert, comment on l'installe, comment on le lance
├── .gitignore           # ce qui ne se versionne pas
├── requirements.txt     # les dépendances déclarées
├── .env.example         # les variables attendues, sans les valeurs
├── src/                 # le code
│   ├── lecture.py
│   └── rapport.py
├── tests/               # les tests, séparés du code
│   └── test_rapport.py
└── data/                # les données d'exemple, petites

Deux idées derrière cette disposition. Le code applicatif est séparé des tests, ce qui permet de lancer les uns sans embarquer les autres. Et tout ce qui décrit le projet, dépendances comprises, se trouve à la racine, là où on le cherche.

Ce qui n'a rien à faire dans un dépôt
Les dépendances installées (node_modules, .venv), les produits de compilation, les fichiers de configuration personnels, les gros fichiers binaires, et bien sûr les secrets.
Le critère : si le fichier peut être régénéré à partir de ce qui est versionné, il ne se versionne pas. Un dépôt doit rester petit, parce que son historique conserve tout, à jamais.

Le README

C'est le fichier le plus lu du projet et le plus souvent négligé. Il répond à cinq questions, dans cet ordre.

  1. À quoi sert ce projet ? Deux phrases, en français, sans jargon interne.
  2. Comment l'installer ? Les commandes exactes, copiables, y compris la version de l'interpréteur attendue.
  3. Comment le lancer ? Un exemple complet qui produit une sortie visible.
  4. Comment lancer les tests ? Une commande.
  5. Où est le reste ? Documentation, suivi des anomalies, contacts.
L'épreuve du nouvel arrivant
Le seul test valable d'un README est de le faire suivre à quelqu'un qui n'a jamais vu le projet, sur une machine neuve, sans lui parler. Chaque fois qu'il doit poser une question, la réponse manquait dans le fichier, et il faut l'y ajouter.
Ce test se refait à chaque fois qu'un nouvel arrivant rejoint le projet, et il attrape toujours quelque chose.

Déclarer plutôt qu'installer

Définition

Une dépendance déclarée est inscrite dans un fichier versionné du projet : requirements.txt ou pyproject.toml en Python, package.json en JavaScript, pom.xml en Java.

Une dépendance installée est le code réellement présent sur la machine, dans un dossier qui ne se versionne pas.

La déclaration est la seule chose qui voyage avec le projet. Installer une bibliothèque sans l'ajouter au fichier de dépendances est la première cause de « ça marche chez moi » : le programme fonctionne sur la machine où la bibliothèque a été installée à la main, et nulle part ailleurs.

Terminal6 commandes
$python -m venv .venv# crée un environnement isolé
$source .venv/bin/activate# Windows : .venv\Scripts\activate
$pip install pandas# installe dans cet environnement seulement
$pip freeze > requirements.txt# écrit ce qui est installé, avec les versions
# sur une autre machine
$python -m venv .venv && source .venv/bin/activate
$pip install -r requirements.txt# reconstruit le même environnement

Pourquoi isoler

Sans environnement isolé, toutes les bibliothèques s'installent au même endroit, pour tous les projets. Deux projets qui ont besoin de deux versions différentes de la même bibliothèque deviennent alors incompatibles, et le second installé casse le premier.

Un environnement isolé donne à chaque projet son propre dossier de bibliothèques. Il est jetable : on le supprime et on le reconstruit à partir des dépendances déclarées, ce qui est aussi une bonne façon de vérifier que la déclaration est complète.

Les versions

Définition

La version sémantique s'écrit MAJEUR.MINEUR.CORRECTIF. On incrémente le majeur en cas de rupture de compatibilité, le mineur pour un ajout compatible, le correctif pour une correction sans changement d'interface.

Cette convention permet d'exprimer ce qu'on accepte comme mise à jour.

ContrainteAccepteRefuse
==2.1.3exactement cette versiontout le reste
~=2.1.3les correctifs : 2.1.4, 2.1.92.2.0
^2.1.3tout le majeur 2 : 2.4.03.0.0
>=2.1.3tout ce qui suit, y compris 3.0.0les versions antérieures
main.py
Sortie
>_ Prêt à exécuter…

Regardez la dernière colonne. La même déclaration, installée à trois mois d'écart, ne donne pas la même version installée, sauf pour ==. Une contrainte souple est pourtant souhaitable : elle laisse arriver les correctifs de sécurité. La contradiction se résout par un second fichier.

Définition

Un fichier de verrouillage (package-lock.json, poetry.lock, pnpm-lock.yaml) enregistre la version exacte de chaque dépendance, directe ou indirecte, telle qu'elle a été résolue. Il se versionne.

Les deux fichiers ont donc deux rôles distincts. Le fichier de dépendances dit ce que le projet accepte, le fichier de verrouillage dit ce qui a été installé. Le premier se modifie à la main, le second est produit par l'outil et ne se retouche pas.

Les dépendances de vos dépendances
Une bibliothèque déclarée en entraîne dix autres, que vous n'avez pas choisies et dont vous héritez le code, les défauts et les failles.
Deux conséquences pratiques : regarder le nombre de dépendances indirectes avant d'ajouter une bibliothèque pour une fonction de dix lignes, et mettre à jour régulièrement plutôt que par grands sauts, une montée de version après deux ans d'immobilité étant nettement plus douloureuse que douze petites.

La configuration hors du code

Une adresse de serveur, un identifiant, un chemin de fichier changent d'un environnement à l'autre. Écrits dans le code, ils obligent à modifier le programme pour le déployer ailleurs, et à commiter un secret pour le faire fonctionner.

# .env.example : versionné, sans aucune valeur réelle
API_URL=https://api.exemple.fr
API_TOKEN=
BASE_CHEMIN=./data

# .env : jamais versionné, présent sur chaque machine

Le code lit ces valeurs dans l'environnement, avec une valeur par défaut raisonnable quand c'est possible, et une erreur claire au démarrage quand la variable est obligatoire et absente. Échouer immédiatement au lancement vaut mieux qu'échouer trois heures plus tard, au milieu d'un traitement.

Exercices type

Pourquoi ne pas versionner le dossier des dépendances installées ?

Parce qu'il est régénérable à partir du fichier de dépendances : le versionner duplique une information qui existe déjà.

Parce qu'il est volumineux, souvent des dizaines de milliers de fichiers, et que l'historique Git conserve tout, définitivement.

Parce qu'il contient parfois du code compilé pour un système donné, inutilisable ailleurs.

Ce qui se versionne, c'est la déclaration et le fichier de verrouillage.

Un collègue obtient une erreur que vous n'avez pas, sur le même commit. Par où chercher ?

Par les versions installées. Comparez les sorties de pip freeze ou npm ls : si le projet n'a pas de fichier de verrouillage, vous n'avez probablement pas les mêmes versions.

Puis par l'environnement : variables absentes chez lui, fichiers de données différents, version de l'interpréteur, système d'exploitation.

Puis par ce qui traîne chez vous : une bibliothèque installée à la main et jamais déclarée fonctionne chez vous seul. Le test décisif est de reconstruire votre environnement à partir de zéro.

Quelle différence entre requirements.txt et un fichier de verrouillage ?

Le premier exprime des contraintes : ce que le projet accepte, souvent sous forme d'intervalles, et il est écrit par un humain.

Le second enregistre une résolution : la version exacte de chaque dépendance, directe et indirecte, telle qu'installée à un moment donné. Il est produit par l'outil.

Sans le second, deux installations à des dates différentes ne donnent pas le même environnement, alors que le dépôt est identique.

Pourquoi une contrainte trop stricte pose-t-elle aussi problème ?

Parce qu'elle bloque les correctifs de sécurité et les corrections d'anomalies, qui arrivent justement par les versions de correctif.

Parce qu'elle rend les mises à jour rares, donc énormes, donc risquées.

Et parce qu'avec plusieurs dépendances épinglées trop strictement, aucune combinaison ne satisfait tout le monde et l'installation devient impossible.

Le bon usage est une contrainte souple dans la déclaration, et un fichier de verrouillage pour la reproductibilité.

Pourquoi versionner un .env.example vide ?

Parce qu'il documente les variables attendues. Sans lui, un nouvel arrivant découvre les variables une par une, au fil des erreurs.

Parce qu'il donne la liste exacte à remplir, ce qui permet de vérifier au démarrage qu'aucune ne manque.

Et parce qu'il rend explicite que le vrai .env, lui, ne se versionne pas : le dépôt contient la forme, jamais les valeurs.

La méthode

  1. Crée un environnement isolé dès le premier install, jamais d'installation globale pour un projet.
  2. Déclare la dépendance dans le même geste que tu l'installes.
  3. Versionne le fichier de verrouillage, ne le modifie jamais à la main.
  4. Écris le README au fur et à mesure, et fais-le tester par un nouvel arrivant.
  5. Sors la configuration du code, et versionne un exemple sans valeurs.
  6. Reconstruis ton environnement de zéro de temps en temps : c'est le seul test de la déclaration.
  7. Mets à jour souvent et par petits pas plutôt qu'une fois tous les deux ans.

En résumé

  • Un projet reprenable se clone, s'installe et se lance en suivant une page.
  • Ce qui est régénérable ne se versionne pas.
  • Une dépendance déclarée voyage avec le projet, une dépendance installée reste sur la machine.
  • Un environnement isolé évite que deux projets se disputent la même version.
  • La version sémantique distingue rupture, ajout et correction.
  • Une contrainte dit ce que le projet accepte ; un fichier de verrouillage dit ce qui est installé.
  • Les dépendances indirectes s'ajoutent au projet sans avoir été choisies.
  • La configuration vit dans l'environnement, jamais dans le code, jamais dans le dépôt.

Et ensuite ? Un environnement isolé règle le problème des bibliothèques, pas celui du système : version de l'interpréteur, paquets système, base de données. Le chapitre suivant élargit l'isolement à toute la machine, avec les conteneurs.

Un projet qu'on peut reprendre | Plateforme ETS