Engineering

Monorepo changelogs: one log, or one per package?

5 min read

A monorepo holds several deployable things in one repository, and a changelog has to answer one question first: does a reader care about the repo, or about one package inside it? Most teams never decide this on purpose. They start with one changelog because there is one repo, keep adding packages, and end up with a log where a CLI user has to scroll past forty unrelated backend entries to find the one that shipped their fix. What decides the right shape is not the repository’s structure but who reads the log and what they already know they are looking for.

What makes a monorepo’s changelog different from a single-repo one?

A single-repo changelog has one implicit audience: everyone using the one thing the repo builds. A monorepo’s audience splits by package, and packages inside the same repo often ship on different schedules, to different consumers, at different levels of stability. A library published to a registry and an internal admin tool can live in the same monorepo and have almost nothing in common as far as a changelog reader is concerned.

Repo shapeTypical readerChangelog that fits
Single deployable appEveryone using the productOne log, repo-wide
Library workspace (several published packages)Whoever depends on one specific packageOne log per package
App plus internal toolingTwo different audiences with no overlapSplit by audience, not by folder
App plus its own SDKProduct users, and SDK integratorsTwo logs: product-facing and SDK-facing

Should every package get its own changelog?

Only the ones with an independent audience. A package published to a registry needs its own log, because the person installing it has no reason to read anything else in the repo, and monorepo release tools such as Lerna and Changesets write a CHANGELOG.md per package, next to its package.json. An internal utility with one consumer, the app that already lives in the same repo, does not need a separate log; folding its changes into that app’s own entries is more useful than a second file nobody outside the team opens.

The test is the same one that decides whether any single entry belongs in a changelog at all: would the reader notice or care, and can they act on knowing it. Apply it per package, not per folder, and a repo with twelve packages can end up with two real changelogs and ten packages that simply do not need one.

How do you know which package caused which changelog entry?

Tag every entry with its package at the point the entry is written, not after the fact by inspecting which files a commit touched. A commit that fixes a shared internal library can produce a changelog entry in every package that depends on it, and file paths alone cannot tell you which of those downstream entries a reader actually needs to see; only a person deciding “this is user-visible in package A and not in package B” can. Conventional commits scopes help here mechanically, by naming the package in every commit, but the scope still only produces a draft. The same layer-two editing rule from that article applies per package: a draft tagged with the right scope still needs a human pass before it is worded for that package’s actual reader.

What does a shared changelog need that a single-repo one doesn’t?

A package label on every entry, placed first, before the description, so a reader scanning the log can skip everything that is not theirs in one pass. Without that label a shared log reads like a random feed, and a reader who cares about one package has no way to filter it except by memorizing which lines matter, which nobody does past the first week.

## 2026-09-07

### [cli] Added
- `acme push --dry-run` shows what would be sent without sending it.

### [core] Fixed
- Retry backoff no longer resets on a successful request that returns
  an empty body.

Two entries, two audiences, one glance to tell them apart. A Changesets-style workflow builds this labeling into the release process itself: a contributor writes a short, package-scoped note alongside their change, and the tool assembles per-package changelogs and version bumps from those notes at release time, rather than trying to reconstruct package boundaries from a merged commit history after the fact.

How does versioning interact with a monorepo changelog?

Independently-versioned packages need their own changelog because they have their own version number, and a shared changelog cannot express “package A went from 2.1 to 2.2 while package B stayed at 1.4” without becoming two logs wearing one file. Semantic versioning and your changelog covers how a version number should map to changelog categories; in a monorepo that mapping has to be applied per package, because a breaking change in one package is not a breaking change in a sibling that does not depend on it.

A repo that ships one product as one deployable unit, even if it is built from many internal packages, does not have this problem: the packages share a version because they are only ever released together, and a single changelog is correct.

How do tags fit a monorepo?

The same rule from git tags, releases, and your changelog applies, scoped per package: a package with its own version needs its own tag prefix, typically package-name@1.4.0 rather than a bare v1.4.0 that cannot say which package it belongs to. A monorepo tagged only with bare version numbers cannot later answer “what was in core when cli shipped 2.2”, because nothing on disk records which package that tag was actually for.

FAQ

Do I need a separate changelog for every package in a monorepo? Only for packages with an independent audience, usually anything published to a registry. A package with one internal consumer already in the same repo can fold into that consumer’s log instead of maintaining its own.

What labels a changelog entry with the right package? The person writing the entry, at the time they write it, not an automated scan of changed file paths. A shared library change can produce different entries in every package that depends on it, and only a human can decide what each of those downstream entries should actually say.

Should a monorepo use one version number for everything? Only if every package always ships together. If packages are ever released independently, they need independent versions, and independent versions need independent changelogs to make sense of.

Does a monorepo changelog tool replace the human editing step? No. Tools like Changesets automate collecting and assembling per-package notes at release time; the note itself, written in the reader’s language rather than the contributor’s, is still a person’s job, the same as in any other changelog pipeline.


The technical claims in this article have not been independently reviewed. If something here is wrong, tell us and we will correct it.

Related on changeloop: Changelog generator, Developer docs

changeloop
The team building a closed-loop changelog. Your users ask, your team ships, the person who asked gets told.