All news

Factory

19 packages, one version

· Toolkit · 2 min read

The number 19 with the caption: NuGet packages, one version, no lockstep upgrades.

Before 1.0.0, each of our five products carried its own copy of the shared SaaS modules: tenancy, onboarding, organizations, entitlements, usage, billing and admin. The copies lived inside each repository, and the oldest was about eight months behind the newest. When we wrote the upgrade notes for the first release, Gostly needed one list, MojRazpis and SnapMind a longer one. A fix made in one copy reached the others only if someone carried it over by hand.

One version for every package

On 10 October 2026 we tagged v1.0.0 and published the shared code as 19 MicroSaas.Toolkit.* NuGet packages with a single version. One Directory.Build.props holds the build settings, and the tag pipeline passes -p:Version=X.Y.Z from the tag to every project, so the packages also depend on each other at that version. The publish job stops if CHANGELOG.md has no section for the version, or still marks it unreleased, and it counts what it packed:

[ "$count" -eq 19 ] || { echo "expected 19 packages, packed $count"; exit 1; }

The package registry is set to refuse a version it already holds, so 1.0.0 is the same code for every product.

In a product, the version is one property in its own Directory.Build.props:

<MicroSaasToolkitVersion>1.0.0</MicroSaasToolkitVersion>

Every toolkit PackageReference reads that property. Products reference different subsets: Leadgener 6 of the 19 packages; Reputo 16; Gostly, MojRazpis and SnapMind 17 each.

What it costs, and what it does not mean

The cost is written into our design: a product cannot take an Organizations fix without the Billing changes of the same release. With one maintainer, one version line is cheaper than nineteen, and the changelog makes the cost visible in advance. It flags each entry that needs action as [migration], [contract] or [behaviour], and lists, for each starting point, what a product has to change before it upgrades.

One version does not mean lockstep. A release never requires two products to upgrade together. Each product moved to 1.0.0 through its own merge request in its own repository. All five merged on the same day because all five were leaving their vendored copies, not because the release demanded it. The next release is a one-line change in whichever product wants it, made when that product chooses.

For a team building on the toolkit, that means one number to pin, the changelog sections between your version and the new one to read, and an upgrade on your schedule, not ours. Two products on different toolkit versions are both valid, because they share code, never a running instance.