Why We Dropped Semantic Versioning
After three years of chasing backward compatibility, we found a simpler way to ship breaking changes without breaking trust.
I spent two weeks last winter rewriting our release pipeline. Not because it was broken — it worked, technically — but because every patch release had turned into a negotiation. Our team of twelve was spending more hours debating whether a change counted as “major” or “minor” than actually shipping features to the developers who needed them.
# forge.config.yaml
releases:
strategy: calver
prefix: "${YYYY}.${MM}"
breaking: require-guide
channels: [stable, nightly]
The Versioning Trap
Semantic versioning promises a clean contract: bump the major when things break, the minor when you add, the patch when you fix. In practice, that contract became a source of constant friction. Every internal API change triggered the same debate — does removing a deprecated flag count as breaking? What about changing the default output format of forge build?
We documented forty-seven version-bump decisions over eighteen months. Twenty-three of them were contested by at least one reviewer. The overhead was not theoretical — it was measured in pull-request cycle time, and it was growing.