API vendors (in a perfect world) shouldn't make a breaking change. However, as we live in an imperfect world, then I would expect to be alerted to this fact by a direct form of communication such as an email. It shouldn't be up to the client to read through every tweet etc to find out about these changes.
"There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult." - C.A.R. Hoare
I understand that an API normally always means Web API, but the meaning of API can be Web API and the software API, the SDK.
The reason I am mentioning the SDK here is because changes in the SDK are the only changes that can break your build ( for who ever thought of this survey). So I will also try to focus my answer on SDK, and now Web API, because a Web API change will be discovered on the runtime and that is a different story—API versioning can help here. Period.
Now, I have a good history of experiencing with these kind of issues, and being a Microsoft stack developer, I face this issue every now and then. Sometimes their (Docker) image names have changed for build environments, sometimes they change the API structure (ASP.NET Core being most prone to errors). What I think they should do, is what they are doing.
There are document releases for every update they make, and I feel like developers should pay attention and read the f manual while writing their code. Just because it worked, doesn't mean it would work 2 months from now.
An email, tweet, newsletter, website post, documentation, everything leads to one thing: CHANGELOG.md. As long as you keep reading it and implementing the changes, you're fine.
Oh, the actual problem comes when there is no way to fix the breaking changes than to migrate the platform from previous major version to the next one—yes, that is what a major version means—I personally believe vendors should make it easier to bring the projects to a latest version instead of writing a beautiful article on their site.
Would love to hear how you disagree with me on this, please enlighten me.
The sh*t I complain about
It's like there ain't a cloud in the sky and it's raining out - Eminem
~! Firewall !~
In the few cases that these are necessary (e.g. when a new language Standard is released, which implements the API differently), the change should (a) be prominently featured in the documentation and the change log, (b) a reasonable method should be provided to use the old functionality, e.g. a differently-named API, and (c) using the new API in the old manner should trigger an error (not a warning!).
Freedom is the freedom to say that two plus two make four. If that is granted, all else follows.
-- 6079 Smith W.
Functionality is extended over time, so how do you hanlde it? In the Windows API, you can find two distinctly different approaches:
If the original function was MyFunction(), the new one is claled MyFunctionEx(). The next version is MyFunctionExEx(), and so on. You can find MyFunctionExExExEx() - but I saw that many years ago, so maybe there is a MyFunctionExExExExEx() nowadays... These are really all new APIs, sharing part of the name. Usually, the parameter lists are fairly similar, but newer versions may have added more parameters at the end.
The other approach is to put all the parameters into a struct, which is the single parameter to the API. The first member of the struct is its size. So the API can be named MyFunction() through an arbitrary number of versions; revisions are usually distinguished by the size of the struct. (Sometimes, but certainly not always, one of the members is a version identifier; this allows new versions without adding new parameters.)
Guess which approach I prefer! The second one is fully backwards compatible; old programs will work perfectly fine. There are a couple of minor disadvantages: A new version may not need all the parameters/members used by previous versions, but cannot delete them from the struct; once a member is added, it must remain in the definition forever, even if no longer used. This doesn't occur very often, and the cost is minimal.
Second: Rather than just listing parameters in the call, the caller must copy every parameter into the struct. For the code size/speed, it doesn't matter too much: Essentially, the compiler is doing very much of the same when moving the parameter list into the stack frame. The difference is mostly in the source code, which may grow by a few lines. I think that is OK.
For maintenance of the API implementation, this approach is also a lot better: There is only one implementation of several versions of the API, rather than multiple method definitions with individual implementations. Unchanged functionality is coded once for several versions. For modified functionality, the old and new versions are available side-by-side, making it easy to verify the difference between them.
When the "parameter struct" approach is used, "breaking changes" ought to be completely unnecessary, especially if a version code member is included in the struct (as well as its size). So when I am in charge of defining APIs, I follow this philosophy whenever possible.
To display that the changes are indeed not ephemera, by a tattooed essay on the body of a member of the board of trustees. This also keeps the number of change announcements to a minimum, unless the company periodically flays members of the board to supply new writing surfaces.
Anything short of that - well it's really just spam trying to sell you something, isn't it?