Advanced

Versioning inside a monorepo

In one big tree, everything moves together, so a single version number describes the whole tree. That is often simpler than per-package versions, and it removes the version-skew class of bugs entirely. The trade is that you cannot release one part alone.

What you will see

The version from the previous release commit. Rebuilding from that commit is the rollback; no registry is involved.

About 1 min of reading

Try It

Stamp the tree version on every build

1

Derive the version

printf 'v%s\n' "$(date +%Y.%m)" > VERSION

Before you run it, say out loud what it will print. Then open the answer.

What it prints
One file at the root holding the tree version, derived from the date.
2

Read it in code

python -c "print(open('VERSION').read().strip())"

Before you run it, say out loud what it will print. Then open the answer.

What it prints
The version string, read from the file rather than hardcoded in any module.
3

Stamp the build

docker build --build-arg VERSION="$(cat VERSION)" -t app:$(cat VERSION) .

Before you run it, say out loud what it will print. Then open the answer.

What it prints
The tag and the artifact agree with the tree. No component can carry a different number.
4

Prove rollback is a commit

git log --format=%H -2 | tail -1 | xargs -I{} git show {}:VERSION

Before you run it, say out loud what it will print. Then open the answer.

What it prints
The version from the previous release commit. Rebuilding from that commit is the rollback; no registry is involved.
Do not let a subdirectory carry its own version file. The moment two numbers exist, the skew class of bugs is back.

Data Flow

Commit to artifacts
1 Land a change in the tree
↓
2 Stamp the tree version on every build
↓
3 Consumers import by path
↓
4 Rollback rebuilds from an older commit
Why this works
What a monorepo buys

Atomic changes across components, one version for everything, and rollback by commit. If your releases are already atomic, most of the versioning machinery above becomes unnecessary.

What it costs

You cannot ship one package without the rest, and a broken package blocks every release. That is the same atomicity, applied in the wrong direction.

Examples

One number, whole tree

The tree is at 2026.10. Every artifact built from it carries that date. There is no question of whether the library and the app agree, because they are the same commit.

Cut by path, not by version

A change to one package is gated by which paths a consumer imports, not by a version floor. Compute the set of paths and let that be the dependency edge.

Rollback is a commit

Releasing is a commit and a build. Rolling back is building from an earlier commit, which is a normal git operation with no special registry support.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← An LTS line for a moving product
Previous Lesson
An LTS line for a moving product
Next Lesson
A release plan for a containerized product
A release plan for a containerized product →