Monorepo changelogs: one changelog or many?
A monorepo breaks the comfortable assumption that one repository equals one changelog.
Suddenly there are twelve packages, three deployable apps, and a shared design system in one
tree β and a single CHANGELOG.md at the root stops making sense. This guide covers
where changelogs should live in a monorepo, what the popular tooling actually does, and the
aggregation problem everyone hits six months in.
The rule: changelogs follow release units, not repositories
A changelog documents a thing people consume. So count your independently consumed things, and that's how many changelogs you need:
- Packages that version and publish independently (npm workspace packages,
Go modules, crates) β one changelog per package. A user of
@acme/authshould not have to read about@acme/chartsto learn what changed under them. - A product that ships as one unit β even from a monorepo of 40 internal packages β one changelog. Your users experience one product; internal package boundaries are your business, not theirs.
- Both at once β a platform with published SDKs and a hosted app β β per-package changelogs for the SDKs, plus one product-level stream. More on the layering below.
The repository layout is irrelevant to all of this. That's the whole trick: stop asking "where does the file go in the repo" and ask "who upgrades what".
What the tooling actually does
The mainstream monorepo release tools all converged on per-package changelogs:
- Changesets
(the de-facto standard in JS monorepos) has contributors write a small "changeset" file
at PR time β affected packages, bump type, and a human sentence about the change.
At release, it aggregates pending changesets into each package's
CHANGELOG.mdand bumps versions, including cascading bumps for internal dependents. Its core insight is worth stealing even without the tool: capture the human description while the author still remembers why, not at release time. - release-please in manifest mode maintains per-package versions and changelogs from Conventional Commits, using commit scopes and paths to route entries to the right package.
- Lerna (older JS monorepos) generates per-package changelogs from commits in each package's directory.
All three produce drafts. The curation rule from our git-commits guide applies double in monorepos: auto-routed entries are written in internal vocabulary ("bump @acme/core to 4.1") that means nothing to the people reading the changelog.
The aggregation problem
Per-package changelogs are correct for consumers of packages β and useless for everyone else. Six months in, someone asks "what did we actually ship this quarter?" and the answer is scattered across fourteen files, most entries reading "fix: internal refactor". Meanwhile your actual customers follow none of those files, because nobody subscribes to fourteen changelogs.
The fix is layering, not merging:
- Per-package changelogs stay mechanical. Compatibility records for programmers: versions, breaking changes, migration notes. Generated + curated.
- One product-level stream carries the story. Human-written, dated entries about outcomes: "SDK v5: token refresh is now automatic". It links down to package changelogs for details. This is the page you put in front of customers, the one with the visible heartbeat.
The two layers have different audiences, different vocabularies, and different cadences β which is exactly why one merged document serves neither.
Anti-patterns
- One root CHANGELOG.md for twelve packages. Every entry needs a package prefix, nobody can filter, and consumers of one package wade through eleven others' noise. This is the default failure mode of "we'll just keep it simple".
- Publishing the commit stream. Auto-generating from a busy monorepo's commits without curation produces hundreds of "chore(deps)" lines. A changelog nobody can read is a changelog nobody reads.
- Internal names in the product stream. "Bumped @acme/design-tokens to 3.0" is not a product update. If a change isn't visible to users, it doesn't belong in the product layer β that's what package changelogs are for.
- Cross-package breaking changes announced once. If upgrading
@acme/authforces a@acme/clientupgrade, say so in both changelogs. Consumers read the one they depend on, not your dependency graph. (Breaking-change playbook here.)
Practical setup
# Per-package: let the tool do the bookkeeping npx changeset # contributor describes the change at PR time npx changeset version # release: writes per-package CHANGELOGs + bumps # Product layer: one human entry per meaningful ship, posted from CI
The product layer is deliberately low-tech: when something user-visible ships, someone writes two honest sentences. The discipline is editorial, not technical β the same rules as any changelog: outcomes not diffs, dates, tags, name the pain when fixing.
Where Wakelog fits
Wakelog's account model maps onto the layering directly: projects are free, so you can run
one project per published package (each with its own public page, RSS feed,
and README badge) plus a product-level project for the human stream.
Each CI job posts to its own project with one curl or the
CLI β a matrix release job just sets a different project slug per package.
Existing per-package CHANGELOG.md files import in one paste each, and everything exports back
to plain markdown anytime.
Start a free changelog Preview your CHANGELOG.md β no signup
Related guides
- Design system changelogs: shipping changes to people who build with your components
Your components are someone elseβs building blocks: every change lands in screens you donβt own. How to write a changelog for a design system β code and Figma both β that adopting teams actually read before they upgrade. - Automate your changelog with GitHub Actions (and any other CI)
Changelogs die of friction, not bad intentions. Wire the posting step into the pipeline that ships the code β and automate the plumbing without automating the judgment. - SDK changelogs: shipping one API in seven languages
One canonical API changelog plus per-SDK changelogs, lockstep vs independent versions (and the mapping table you owe), generated-SDK regen walls, parity honesty, and the entry that survives a bump PR.
Last updated 2026-07-26 Β· All guides