CHANGELOG.md: wat erin hoort en hoe je het actueel houdt
5 min lezen
Een CHANGELOG.md is een Markdown-bestand in de root van een repository dat per uitgebrachte versie opsomt wat er veranderde, in termen waar een gebruiker van het project iets mee kan. Het bestand wordt gelezen door mensen die beslissen of ze upgraden, door mensen die na een upgrade debuggen, en door package-registries en releasetooling die het op de projectpagina tonen. Het format dat de meeste projecten volgen is dat van keepachangelog.com: nieuwste versie bovenaan, een sectie Unreleased daarboven, en entries gegroepeerd onder Added, Changed, Deprecated, Removed, Fixed en Security.
| Sectie | Wat erin hoort | Voorbeeldregel |
|---|---|---|
Unreleased | Wijzigingen die gemerged zijn maar nog in geen versie zitten | - Export streamt nu, dus bestanden boven 100 MB geven geen time-out meer. |
Added | Nieuwe mogelijkheid die een gebruiker kan bereiken | - Vlag \—dry-run` op `deploy` toont het plan zonder het toe te passen.` |
Changed | Bestaand gedrag dat nu anders werkt | - Standaard time-out verhoogd van 30 s naar 60 s. |
Deprecated | Werkt nog, staat gepland voor verwijdering | - \legacyAuth()` wordt verwijderd in 3.0; gebruik `auth()`.` |
Removed | Weg in deze versie | - Ondersteuning voor Node 16 vervallen. |
Fixed | Een bug die een gebruiker kon tegenkomen | - Zoeken faalt niet meer op zoekopdrachten met een koppelteken. |
Security | Fixes waarvoor een lezer vandaag moet upgraden | - Path traversal in de static file handler opgelost (CVE in aanvraag). |
Wat is een CHANGELOG.md-bestand?
Het is de voor mensen leesbare geschiedenis van een project, versie voor versie, naast de code bewaard zodat het ermee wordt uitgeleverd. Het verschilt van git log in publiek en selectie: de git log registreert elke commit voor de mensen die hem schreven, terwijl de changelog de wijzigingen registreert die een gebruiker zou opmerken, geschreven voor wie ze niet schreef. Een commit die een interne variabele hernoemt krijgt geen changelogregel. Een commit die een standaard time-out verandert krijgt er wel een, ook als de diff één teken was.
Het bestand staat in de root van de repository onder precies die naam omdat tooling het daar zoekt. GitHub linkt het vanaf de repositorypagina, npm en PyPI tonen het als het in het pakket zit, en releasetools als release-please en semantic-release lezen en schrijven het ter plekke. Een kleingeschreven changelog.md en CHANGES.md werken ook voor mensen, maar sommige tools zoeken alleen naar de canonieke naam.
Hoe ziet een complete CHANGELOG.md eruit?
Een kort, correct voorbeeld is meer waard dan een lange beschrijving. Dit volgt de indeling van Keep a Changelog 1.1.0 en semantic versioning voor de nummers.
# Changelog
Alle noemenswaardige wijzigingen aan dit project staan hier.
Format: Keep a Changelog. Versies: Semantic Versioning.
## [Unreleased]
### Added
- Vlag `--dry-run` op `deploy` toont het plan zonder het toe te
passen.
## [2.3.1] - 2026-10-01
### Fixed
- Zoeken faalt niet meer op zoekopdrachten met een koppelteken.
Kapot sinds 2.3.0.
### Security
- Path traversal in de static file handler opgelost. Upgrade
vandaag als je door gebruikers geüploade bestanden serveert.
## [2.3.0] - 2026-09-18
### Added
- Streaming export. Bestanden boven 100 MB geven geen time-out
meer.
### Deprecated
- `legacyAuth()` werkt nog maar wordt verwijderd in 3.0.
Gebruik `auth()`.
[Unreleased]: https://github.com/acme/tool/compare/v2.3.1...HEAD
[2.3.1]: https://github.com/acme/tool/compare/v2.3.0...v2.3.1
[2.3.0]: https://github.com/acme/tool/compare/v2.2.0...v2.3.0
Vier details dragen het grootste deel van de waarde. Elke versie heeft een datum in ISO-formaat, zodat een lezer die kan leggen naast het moment waarop zijn probleem begon. Elke versiekop is een link naar de compare-weergave, dus de volledige diff is één klik weg. Een fix zegt welke versie het brak. En de sectie Security staat apart, zodat iemand die scant op “moet ik nu upgraden” het in seconden vindt.
Hoe houd je een CHANGELOG.md actueel?
Schrijf de regel in dezelfde pull request als de wijziging, onder Unreleased, en verplaats de sectie naar een versiekop wanneer je uitbrengt. Dat is de hele methode. Het bestand veroudert wanneer de regel later door iemand anders wordt geschreven, op releasemoment, uit een lijst commitberichten die hij niet zelf schreef.
Twee dingen laten de gewoonte beklijven. Het eerste is een check in CI die een pull request laat falen als hij src/ raakt zonder de changelog te raken, met een label om hem over te slaan bij wijzigingen zonder zichtbaar effect voor gebruikers; een changelog-check als GitHub Action toont de versie van veertig regels. Het tweede is een releasestap die Unreleased in één commando hernoemt naar de versie en de datum, zodat niemand eraan hoeft te denken dat met de hand te doen. Wie schrijft de changelog beschrijft hoe teams de vraag beslechten wiens taak de regel is.
Moet de changelog uit commits worden gegenereerd?
Genereren geeft je een compleet concept; het geeft je geen changelog. Tools gebouwd op conventional commits, en releaseautomatisering als release-please, lezen de prefixen feat:, fix: en BREAKING CHANGE: en produceren een gegroepeerde lijst met elke pull request gelinkt. De lijst is zo goed als de commitonderwerpen, en commitonderwerpen beschrijven de code.
De werkbare middenweg is genereren en dan bewerken. Laat de tool de sectie Unreleased uit commits aanmaken, en laat de release-pull-request de regels herschrijven die een gebruiker zou lezen: het symptoom in plaats van de functienaam, de instelling die verhuisde in plaats van het bestand dat veranderde. Van conventional commits naar een changelog loopt de pipeline door, en een changelog-generator in de browser volstaat om een concept te krijgen uit een geplakte git log zonder iets te installeren.
Wat hoort niet in CHANGELOG.md?
Interne refactors, dependency-bumps zonder gedragsverandering, commits die alleen tests raken, CI-configuratie en opmaakwijzigingen. Die horen in de git-geschiedenis, waar iedereen die ze nodig heeft ze kan vinden. Ze opnemen begraaft de regels die ertoe doen en leert lezers het bestand over te slaan.
Het andere wat je weglaat is de marketingversie van een wijziging. “Razendsnelle exports” vertelt een lezer niets wat hij kan controleren. “Export streamt nu; bestanden boven 100 MB geven geen time-out meer” wel. Het bestand is een naslagdocument, en de toon hoort daarbij te passen: korte regels, concrete zelfstandige naamwoorden, een versie of datum op alles. Keep a Changelog, geïmplementeerd behandelt de delen van de spec die teams het vaakst laten vallen, en changelog-bestandsformaten behandelt wanneer een JSON- of YAML-bron naast het Markdown-bestand zijn plek verdient.
Heeft een product met een changelogpagina het bestand nog nodig?
Een library wel, want het bestand is wat package-registries tonen en wat een developer offline leest. Een SaaS-product zonder installeerbaar artefact redt het met de pagina alleen, omdat zijn gebruikers de repository nooit zien. Veel teams draaien beide: het bestand als het engineeringregister, en een publieke changelogpagina die daaruit wordt geschreven voor een breder publiek, met het detail verdeeld zoals changelog vs release notes beschrijft. Wordt de pagina vanuit het bestand gevoed, houd dan het bestand als bron van waarheid en genereer de pagina, zodat er één plek is om te bewerken.
FAQ
Wat is het verschil tussen CHANGELOG.md en release notes? De changelog is het complete, doorlopende register van elke noemenswaardige wijziging, bewaard in de repository. Release notes zijn een bericht over één release, vaak korter en herschreven voor een breder publiek. Veel projecten publiceren de changelogsectie van een versie als zijn release notes.
Waar moet CHANGELOG.md staan? In de root van de repository, onder precies die naam, zodat GitHub, package-registries en releasetools het vinden. Een monorepo houdt er één per pakket, naast het manifest van elk pakket.
Moet ik CHANGELOG.md uit git-commits genereren? Genereer het concept en bewerk dan de regels die een gebruiker zal lezen. Commitberichten beschrijven de codewijziging; de changelog beschrijft wat een gebruiker opmerkt. Tools als conventional-changelog en release-please leveren een goed startpunt.
Heeft elke commit een changelog-entry nodig? Nee. Alleen wijzigingen die een gebruiker van het project kan opmerken: nieuwe mogelijkheden, veranderd gedrag, deprecaties, verwijderingen, fixes en beveiligingspatches. Refactors, testwijzigingen en CI-aanpassingen blijven in de git-geschiedenis.
De technische beweringen in dit artikel zijn niet onafhankelijk gecontroleerd. Klopt er iets niet, laat het ons weten, dan corrigeren we het.