The docs site has a changelog page, but the changelog is not written as Markdown. It is a TypeScript module (docs/lib/changelog-data.ts) exporting a structured array of releases. The choice of format is the design.
The shape
{
version: 'v0.12.0',
date: '2026-08-12',
tag: 'v0.12.0',
description: 'Generated types become a real runtime module...',
sections: [
{ title: 'Features', items: ['...', '...'] },
{ title: 'Bug Fixes', items: ['...'] },
],
}Each release is an object: a version string, a date, the matching git tag, a one-paragraph description, and categorized bullet lists. The renderer in docs/app/changelog/page.tsx simply maps over the array, grouping by section, stamping a tag badge, printing the date.
Why structured beats prose
- Rendered consistently. The tag badge, date, and section grouping are decided once in the renderer, not re-typed in every release.
- Typechecked. This is a TS file in a TS codebase, a malformed release object fails the build instead of rendering broken.
- Programmable. The array is importable. Future features (an RSS feed, an "unreleased" block, a diff view between versions) read the same data.
- Git-friendly. A release entry is a small, reviewable diff appended to a data file, exactly like the version bump it documents.
The version string is not for humans
Note the two formats in one entry: the version/tag fields use the tag form (v0.12.0), matching the GitHub tag exactly, while the prose says 0.12.0. The tag field is data, it exists to link or compare against git and GitHub. Keeping it byte-identical to the real tag is what makes the changelog a reliable index of the repo's release history.
The release ritual includes the changelog
The changelog entry is part of the release PR, sitting next to the version bump and the lockfile sync. The discipline: a version without a changelog entry is not released. Since the GitHub Release auto-generates notes from PR titles, and the changelog is curated prose, the two complement each other, GitHub notes say what merged; the changelog says what it means.
The lesson
A changelog is documentation, but it is also data about releases. Modeling it as data (version, date, tag, sections, items) costs a little upfront structure and pays off in rendering consistency, type safety, and future tooling. When content has a stable schema, store it as a schema, not as prose.

pyRPC