Engineering

Why We Dropped Semantic Versioning

After three years of chasing backward compatibility, we found a simpler way to ship breaking changes without breaking trust.

MC Maya Chen · Jan 15, 2024 · 8 min read

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.