Major would be for rewrites: (almost) a new product, but with the same name for brand recognition.
What bothers me about the current trends in release management is that there is way too much emphasis on iterating quickly, even for libraries where that's completely inappropriate.
It's much better to just put all your breaking changes into an unstable branch and only merge to stable (and change version numbers) infrequently, when you're sure the dust has settled. Anyone that really needs the latest updates can pull the unstable branch at their own risk, but everyone else doesn't have their build broken every other day. This is a really serious problem in the JavaScript ecosystem right now (even disregarding the left-pad debacle).
You can do that as much as you want in version 0.x.x
Get to v1 when you figured out the API and it is stable.
In my case, the library that I avoided making breaking changes to because I didn't want to bump the major version was already on v2, and I didn't want to make it v3 after just a few weeks at v2.
Introduce the new APIs in a new namespace, class, module, new function name, whatever.
Either you write one new function yourself, or you force all API users to rewrite their functions to accommodate you.
The choice will be a show of what you value more: yourself or your users.
This allows for new APIs to bake before having the experimental flag removed and stability guarantees introduced.
In RxJava [1], parts of the API are marked with @Experimental or @Beta. @Experimental provides no guarantees, while @Beta only guarantees no breakage in the same minor version.
That's kind of the point though, right. As an API consumer/library user breaking changes are a pain in the ass. A library that rapidly issues breaking changes is one that is just plain difficult to use. Part of the goal with semantic versioning is to push authors to slow down on that and respect the commitments of their users.
Also, another thought here is that if you're hitting that many breaking changes that quickly, you possibly went 1.0 too early.
> where the first component is for "significant" releases, and the second component is what you use when you're simply introducing breaking changes
From a user's point of view, a breaking change is a significant release. It means I can't just hit update and get bug fixes (hopefully) for no effort. I have to investigate changes and test everything to make sure my software will still work.