Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

README et CHANGELOG : l'entrée et l'historique d'un dépôt

Par mis à jour le

Améliorer cette page

Le README est la porte d'entrée d'un dépôt : il doit permettre à une personne qui n'a jamais vu le projet de l'installer et d'obtenir un premier résultat sans rien deviner. Le CHANGELOG est son historique : une liste rédigée, version par version, de ce qui a changé pour les utilisateurs. Le seul moyen de savoir si un README tient sa promesse est de le tester comme un inconnu, dans un dossier vierge. Cette leçon le fait sur un petit projet, puis installe un CHANGELOG au format Keep a Changelog.

  • À qui s'adresse un README, ce qu'il contient et ce qu'il laisse à d'autres documents.
  • Comment tester un README et quels défauts l'exécution révèle.
  • Le format Keep a Changelog 1.1.0 : Unreleased, six types, dates, liens, et le lien avec le numéro de version.
  • Pourquoi un changelog n'est pas un export de git log.
  • Comment un linter (outil qui vérifie le style d'un fichier) Markdown réagit à ce format.

Le projet fil rouge s'appelle healthcheck : un script Python, sans dépendance, qui vérifie qu'une URL répond. C'est un exemple créé pour cette leçon : l'adresse GitHub exemple-fictif n'existe pas, et aucun dépôt n'est à cloner. Pour vous exercer, appliquez la méthode à votre propre dépôt, ou recopiez dans un dossier vide les deux fichiers montrés (README.md et CHANGELOG.md) : le script lui-même n'est pas reproduit.

Le README est le premier fichier qu'une personne lit en arrivant sur un dépôt. Elle ne connaît ni le projet, ni son équipe, ni ses conventions : elle veut savoir à quoi il sert, si c'est pour elle, puis le faire tourner. Un README répond à ces trois questions, dans cet ordre.

Pour ne pas partir d'une page blanche, The Good Docs Project publie un gabarit de README (page du gabarit), personnalisable : on réordonne ou retire les sections inutiles. C'est un menu, pas une obligation.

Section du gabaritQuestion à laquelle elle répond
Project name, descriptionÀ quoi sert ce projet ?
Who this project is forEst-ce pour moi ?
Project dependenciesDe quoi ai-je besoin avant ?
Instructions : Install, Configure, Run, TroubleshootComment l'installer, le régler, le lancer, le réparer ?
Contributing guidelinesComment participer ?
Additional documentationOù lire la suite ?
How to get helpQui contacter en cas de blocage ?
Terms of useSous quelle licence ?

Le gabarit prévoit aussi, en option, un logo avec des badges et une table des matières. Pour healthcheck, projet minuscule, on retient la description, le public, les prérequis, l'installation, le premier usage, la configuration, le dépannage, l'aide et la licence.

Le README ne contient pas tout : référence complète des options, architecture, récit d'une décision de conception ou historique des versions n'y ont pas leur place. Il renvoie vers le document adapté : la documentation détaillée, une décision d'architecture (l'ADR, objet de la leçon suivante) ou le CHANGELOG. La leçon Choisir le bon document explique ce découpage et le piège du README fourre-tout.

Les conseils d'écriture du gabarit (décrire l'utilité avant les technologies, un verbe en tête de chaque instruction, des listes numérotées pour les étapes) rejoignent ceux de Écrire clair.

Un README relu par son auteur paraît toujours complet : l'auteur sait déjà ce que le texte omet. Le geste central est donc de jouer le README à la lettre, comme le ferait un inconnu, dans un dossier vierge, sans rien corriger de tête. Chaque écart entre ce que dit le texte et ce qui se passe est un défaut du README, pas du lecteur.

Chaque étape suit la même boucle : une action, un résultat attendu écrit dans le README, une comparaison avec ce qu'on observe. Le schéma ci-dessous la résume : suivez la flèche épaisse tant que le résultat observé est égal à l'attendu, et sortez par la flèche fine au premier écart.

Boucle d'une étape de README : une action à l'impératif, puis la question « résultat observé égal résultat attendu ? ». Si oui, on passe à l'étape suivante ; sinon, on corrige ou on s'arrête.ouinonActionune seule, à l'impératifRésultat observé =résultat attendu ?Étape suivanteQue faire si différent :corriger ou s'arrêter
Chaque étape d'un README : une action, un résultat attendu, une comparaison. Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Le premier jet de healthcheck tient en cinq sections : titre, une phrase, prérequis (« Python », « git »), installation (git clone <url-du-dépôt> puis cd healthcheck), utilisation (healthcheck.py http://localhost:8000) et licence (« MIT »). Il paraît correct. Rejoué dans un dossier vierge, il échoue de plusieurs façons. Chaque commande rend un code de sortie, un nombre que le shell conserve après son exécution (0 pour un succès, autre chose pour un échec), que l'on lit avec echo $?.

$ healthcheck.py http://localhost:8000
healthcheck.py: command not found (code de sortie 127)
$ python healthcheck.py http://localhost:8000
OK 200 http://localhost:8000 (11 ms) (code de sortie 0)

Premier échec : le script n'est pas dans le PATH, la liste des dossiers où le shell cherche les commandes. Le code 127 signifie « commande introuvable ». Il faut écrire python3 healthcheck.py ..., avec python3 et non python, parce que certaines distributions, dont Debian et Ubuntu, ne fournissent pas de commande python par défaut (la PEP 394 le prévoit ; sur Debian et Ubuntu, le paquet python-is-python3 la crée).

Le second résultat est pire : OK 200 s'affiche, donc tout semble marcher. Or le README ne lançait rien sur le port 8000 : un autre service de la machine y répondait déjà. Le lecteur croit son installation réussie alors qu'il a testé le service d'un autre. Un exemple qui réussit par hasard est pire qu'un exemple qui échoue. Le README doit fournir sa propre cible, sur un port peu courant.

Les autres défauts relevés tiennent en une liste :

  1. Aucune sortie attendue : le lecteur ne peut pas reconnaître le succès.
  2. Un placeholder <url-du-dépôt> : sans l'URL réelle, personne ne clone.
  3. « Python » sans version ni commande pour vérifier qu'il est présent.
  4. Rien sur la configuration (le délai d'attente) ni sur les erreurs.
  5. « MIT » sans renvoi vers le fichier de licence, et aucun moyen de demander de l'aide.
  6. Aucun public cible : on ne sait pas à qui l'outil s'adresse.

Jouer l'URL sans schéma ajoute un cas limite : localhost:8765 produit FAIL localhost:8765 (unknown url type: localhost). Le message cite localhost et non l'URL entière, trompeur pour un débutant : il mérite une ligne de dépannage.

Voici le README final, rejoué à son tour dans un dossier vierge. Il ne cite que la première ligne de --help : la ligne options: s'appelait optional arguments: avant Python 3.10. Règle : n'afficher en sortie attendue que la partie qui ne change pas d'une version à l'autre. L'URL de clonage est fictive : dans votre README, mettez l'URL réelle du dépôt.

# healthcheck
**healthcheck** vérifie en une commande qu'une URL répond, et le dit par un
code de sortie que vos scripts peuvent lire.
Il s'adresse à toute personne qui veut surveiller un site ou une API depuis un
terminal ou un script, sans installer d'outil de supervision.
## Prérequis
- **Python 3** (testé avec 3.12) : `python3 --version` doit afficher un numéro
de version.
- **git**, pour récupérer le dépôt.
Aucun paquet Python supplémentaire n'est nécessaire.
## Installation
1. Clonez le dépôt, puis entrez dans le dossier :
```bash
git clone https://github.com/exemple-fictif/healthcheck.git
cd healthcheck
```
2. Vérifiez que le script démarre :
```bash
python3 healthcheck.py --help
```
Sortie attendue (début) :
```text
usage: healthcheck [-h] [--timeout TIMEOUT] [--quiet] url
```
## Premier usage
Dans un second terminal, lancez un petit site local sur le port 8765 (le port
8000 est souvent déjà pris) :
```bash
python3 -m http.server 8765 --bind 127.0.0.1
```
Dans le premier terminal, vérifiez-le :
```bash
python3 healthcheck.py http://127.0.0.1:8765
```
Sortie attendue (la durée varie) :
```text
OK 200 http://127.0.0.1:8765 (5 ms)
```
Le script rend aussi un **code de sortie**, que vous lisez avec `echo $?` juste
après la commande :
| Code | Signification |
| --- | --- |
| `0` | L'URL répond avec un code HTTP 2xx ou 3xx |
| `1` | Réponse en erreur (4xx, 5xx) ou connexion impossible |
| `2` | Délai dépassé (timeout) |
## Configuration
Le délai maximal d'attente est de **5 secondes** par défaut. Deux façons de le
changer, la première l'emportant sur la seconde :
```bash
python3 healthcheck.py --timeout 2 http://127.0.0.1:8765
HEALTHCHECK_TIMEOUT=2 python3 healthcheck.py http://127.0.0.1:8765
```
L'option `--quiet` n'affiche rien : seul le code de sortie renseigne, ce qui
suffit dans un script.
## Dépannage
| Symptôme | Cause probable | Solution |
| --- | --- | --- |
| `FAIL ... (Connection refused)` | Rien n'écoute sur ce port | Lancez le site de test, ou corrigez l'URL |
| `FAIL ... (unknown url type: ...)` | L'URL n'a pas de `http://` | Écrivez l'URL complète, schéma compris |
| `FAIL 404 ...` | Le serveur répond, mais pas sur ce chemin | Corrigez le chemin de l'URL |
| `TIMEOUT ...` | Le serveur met trop de temps à répondre | Augmentez `--timeout`, ou vérifiez le serveur |
## Obtenir de l'aide
Ouvrez une issue : <https://github.com/exemple-fictif/healthcheck/issues>.
## Licence
Distribué sous licence MIT, voir le fichier [LICENSE](LICENSE).

Le parcours de l'inconnu y est celui que tout README devrait offrir : il découvre (titre et public), installe (prérequis, clone, vérification), obtient un premier usage avec un résultat attendu, configure, puis trouve de l'aide. Chaque section répond à une étape de ce parcours, et chaque commande est suivie de ce qu'il doit voir.

La session complète de ce README a été rejouée avec Python 3.12.13 et git 2.43.0 : l'aide s'affiche, OK 200 sur le site de test, FAIL 404 sur un chemin absent, FAIL ... (Connection refused) serveur arrêté, et l'URL sans schéma produit l'erreur décrite en dépannage. Seule la version 3.12 a été testée : le README écrit « testé avec 3.12 » et n'affirme aucun minimum.

Comment tenir un CHANGELOG au format Keep a Changelog ?

Section intitulée « Comment tenir un CHANGELOG au format Keep a Changelog ? »

Le CHANGELOG répond à une autre question que le README : « qu'est-ce qui a changé depuis la version que j'utilise ? ». Keep a Changelog (version 1.1.0, keepachangelog.com) le définit comme un fichier contenant « une liste sélectionnée (en anglais curated) et classée chronologiquement des changements notables de chaque version ». Son slogan : ne laissez pas vos amis déverser le journal git dans un changelog.

Le fichier s'appelle CHANGELOG.md. La version la plus récente est en premier, chaque version a son titre avec sa date au format AAAA-MM-JJ (la page recommande ce format ISO, sans ambiguïté entre les usages régionaux). En tête, une section Unreleased accumule les changements pas encore publiés : on voit ce qui arrive, et à la publication on déplace son contenu dans la nouvelle version.

Sous chaque version, les entrées sont groupées par six types :

TypeContenu
AddedNouvelles fonctionnalités
ChangedChangements dans des fonctionnalités existantes
DeprecatedFonctionnalités bientôt retirées
RemovedFonctionnalités retirées
FixedCorrections de bogues
SecurityCorrectifs de vulnérabilités

Une version n'affiche que les types qui ont des entrées : l'historique de la page signale que les sections vides occupent de la place et font du bruit. Une version retirée après publication se marque [YANKED] à la fin de son titre.

Voici le CHANGELOG du projet, avec deux versions publiées et un changement en attente. En bas du fichier, les liens de comparaison transforment chaque titre [0.2.0] en lien vers l'écart entre deux tags (une étiquette nommée, comme v0.2.0, posée sur un commit pour marquer une version).

# Changelog
Tous les changements notables de ce projet sont consignés dans ce fichier.
Le format s'inspire de [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
et ce projet suit le [versionnage sémantique](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Option `--quiet` : n'affiche rien, seul le code de sortie renseigne.
## [0.2.0] - 2026-10-08
### Added
- Option `--timeout` et variable d'environnement `HEALTHCHECK_TIMEOUT` pour
régler le délai maximal d'attente.
### Changed
- Le délai maximal par défaut passe de 3 à 5 secondes.
### Fixed
- Un délai dépassé affiche `TIMEOUT` et rend le code de sortie `2`, au lieu
d'une trace d'erreur Python.
## [0.1.0] - 2026-10-02
### Added
- Vérification d'une URL : affiche le code HTTP et la durée de la réponse.
- Code de sortie `0` si l'URL répond en 2xx ou 3xx, `1` sinon.
- Délai maximal fixé à 3 secondes.
[Unreleased]: https://github.com/exemple-fictif/healthcheck/compare/v0.2.0...HEAD
[0.2.0]: https://github.com/exemple-fictif/healthcheck/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/exemple-fictif/healthcheck/releases/tag/v0.1.0

Les sections Deprecated, Removed et Security sont absentes : rien à y mettre, donc aucune section vide. La première version pointe vers releases/tag/ parce qu'il n'existe aucune version antérieure à comparer, comme dans le changelog de keepachangelog.com. Ces liens sont fictifs, comme l'adresse du dépôt.

Une entrée n'est utile que si elle est vraie : chaque ligne de la section 0.2.0 a été vérifiée par exécution. Avec un serveur qui accepte la connexion et ne répond jamais, la version 0.1.0 affiche une trace d'erreur Python (TimeoutError: timed out, code 1, environ 3 s), tandis que 0.2.0 affiche TIMEOUT http://127.0.0.1:8766 (> 5.0 s) avec le code 2 après environ 5 s. --timeout 1 ramène l'attente à 1 s, et l'option l'emporte sur la variable d'environnement. Keep a Changelog met en garde : un changelog qui ne mentionne qu'une partie des changements peut être « aussi dangereux que de ne pas avoir de changelog ».

Le dépôt de healthcheck compte cinq commits entre v0.1.0 et v0.2.0 :

$ git log --oneline v0.1.0..v0.2.0
55b5f7c chore(release): 0.2.0
193b00c docs: document timeout configuration and exit codes
92072ab fix: exit with code 2 on timeout instead of a traceback
8e0dd15 refactor: raise default timeout from 3 to 5 seconds
e535b8f feat: add --timeout option and HEALTHCHECK_TIMEOUT variable

Le CHANGELOG n'en retient que trois entrées. Le commit de release et celui de documentation n'intéressent pas l'utilisateur. Le commit refactor devient pourtant une entrée Changed, parce qu'il modifie un comportement visible : le délai par défaut. Le préfixe d'un commit ne décide pas ; la curation est un jugement humain sur ce que l'utilisateur observe. Keep a Changelog le dit ainsi : un commit documente une étape de l'évolution du code, une entrée de changelog documente la différence notable, souvent répartie sur plusieurs commits, pour la communiquer clairement. Un export brut est « plein de bruit » : commits de fusion, titres obscurs, changements de documentation.

Des outils génèrent un brouillon à partir de messages bien formés. Conventional Commits explique le format de ces messages et son lien avec les numéros de version, et Maintenir un projet Git compare les générateurs. Un brouillon généré se relit et se réécrit, comme n'importe quelle autre entrée.

Keep a Changelog ne définit pas les numéros : il demande seulement d'indiquer si l'on suit le versionnage sémantique (SemVer 2.0.0). Dans cette spécification, un numéro MAJEUR.MINEUR.CORRECTIF s'incrémente ainsi : MAJEUR pour un changement incompatible de l'API (l'interface que les utilisateurs emploient, ici la ligne de commande), MINEUR pour un ajout de fonctionnalité compatible, CORRECTIF pour une correction compatible. Pour healthcheck, l'option --timeout ajoute une fonctionnalité, d'où 0.1.0 vers 0.2.0. Le détail des tags est dans Tags Git.

L'ordre compte : on met à jour le CHANGELOG avant de poser le tag annoté, sinon la version taguée ne décrit pas ses propres changements. Dans le dépôt de healthcheck, le contenu de Unreleased est déplacé sous [0.2.0] - 2026-10-08 dans un commit de release chore(release): 0.2.0, et le tag annoté v0.2.0 est posé sur ce commit. Le geste de release est détaillé dans Maintenir un projet Git. Le schéma suivant suit la chaîne de gauche à droite ; le lien en pointillé rappelle que les liens de comparaison du bas du fichier se mettent à jour avec le commit de release.

Chaîne en quatre étapes : les changements fusionnés alimentent la section Unreleased, un commit de release la transforme en version datée, un tag annoté est posé sur ce commit. Un lien en pointillé relie le commit de release aux liens de comparaison en bas du fichier.Changements fusionnés(commits, pull requests)Section Unreleasedune ligne par changement notableCommit de releaseUnreleased devient [X.Y.Z] - dateTag annoté vX.Y.Zsur ce commitLien de comparaisonen bas du fichier
Du changement fusionné au tag : Unreleased devient une version datée avant le tag. Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Une différence mérite d'être connue : le README suit la branche principale, le CHANGELOG suit les versions publiées. Sur main, le README de healthcheck documente --quiet, alors que cette option, sous Unreleased, n'est pas encore dans une version.

Ce format n'est pas réservé au code : la leçon Versionner ses modules Terraform le recommande pour des modules, et il convient aussi bien à un contenu versionné, une documentation ou une formation.

Pourquoi un linter Markdown proteste-t-il contre un bon CHANGELOG ?

Section intitulée « Pourquoi un linter Markdown proteste-t-il contre un bon CHANGELOG ? »

Un linter Markdown comme markdownlint-cli2 applique des règles de style, numérotées MDxxx. Sur la première rédaction du README et du CHANGELOG, avec la configuration par défaut, markdownlint-cli2 0.23.3 (markdownlint 0.41.1) a rendu 16 erreurs. Dix venaient du séparateur de tableau |---|---| (règle MD060), à écrire | --- | --- |. Les autres sont plus intéressantes.

  • MD024 interdit deux titres identiques. Or Keep a Changelog répète ### Added dans chaque version. Ce n'est pas le CHANGELOG qui est faux : on règle la règle pour n'interdire les doublons que sous un même parent.
  • MD013 limite les lignes à 80 colonnes, ce que les tableaux dépassent. On l'exclut pour les tableaux.

La configuration, dans .markdownlint-cli2.jsonc à la racine du dépôt :

{
"config": {
"MD024": { "siblings_only": true },
"MD013": { "tables": false }
}
}

Avec elle, la commande ci-dessous (qui télécharge l'outil via npx et exige Node.js) rend « 0 issues » sur les deux fichiers.

Fenêtre de terminal
npx --yes markdownlint-cli2@0.23.3 README.md CHANGELOG.md

Brancher ce contrôle dans la CI, pour qu'il bloque une pull request, relève du niveau suivant de la formation. Retenez ici le principe : face à un linter, on configure l'outil, on ne déforme pas le format recommandé.

Voici le README d'un autre petit projet, resumeur, un script qui compte les lignes d'un fichier. Sans l'exécuter, listez au moins six défauts qu'un inconnu rencontrerait, puis comparez avec le corrigé.

# resumeur
Compte des lignes.
## Installation
Installez les dépendances et lancez le script.
## Utilisation
resumeur.py fichier.txt
## Licence
Voir le dépôt.

Pour chercher : relisez chaque section en vous demandant « que fait un inconnu exactement, et que voit-il ensuite ? ».

Corrigé
  1. Pas de description utile : « Compte des lignes » ne dit ni pourquoi l'utiliser, ni pour qui.
  2. Aucun prérequis : quel langage, quelle version, quels outils ?
  3. « Installez les dépendances » sans commande : lesquelles, comment ? Aucune URL de clonage non plus.
  4. Commande non exécutable telle quelle : resumeur.py n'est pas dans le PATH (code 127), il faut python3 resumeur.py fichier.txt ou ./resumeur.py, et dire laquelle.
  5. Fichier d'entrée absent : fichier.txt n'existe pas dans un dossier vierge. Le README doit fournir un exemple ou le créer.
  6. Aucune sortie attendue : le lecteur ne sait pas reconnaître le succès.
  7. Aucun dépannage ni aide : pas d'erreur courante, pas de lien d'issue.
  8. Licence invisible : « Voir le dépôt » ne désigne aucun fichier.

Les points 3 à 6 se trouvent en jouant le README dans un dossier vierge ; les points 1, 2, 7 et 8 se voient en le comparant au gabarit.

  • Le README est une porte d'entrée : à quoi ça sert, est-ce pour moi, comment le faire tourner. Le reste est renvoyé vers d'autres documents.
  • Le gabarit de The Good Docs Project est un menu : on garde les sections utiles au projet.
  • On teste un README en le jouant à la lettre dans un dossier vierge : commande introuvable, python contre python3, exemple qui réussit par hasard, sortie attendue absente, placeholder d'URL.
  • Chaque étape donne sa sortie attendue, limitée à la partie stable d'une version à l'autre.
  • Un CHANGELOG Keep a Changelog : Unreleased en haut, version la plus récente d'abord, date AAAA-MM-JJ, six types, sections vides omises, [YANKED] pour une version retirée, liens de comparaison en bas.
  • Un changelog est rédigé, pas exporté : une entrée décrit un changement observable, la curation reste un jugement humain.
  • On met à jour le CHANGELOG avant le tag.
  • Un linter se configure (MD024, MD013) plutôt que de déformer le format.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn