There is currently no guarantee of backwards compatibility. Absolute backward compatibility creates a maintenance burden, that currently has no benefits.
We do not break backward compatibility just for fun. Many API changes are located and categorized in the changelogs of the corresponding project. Breaking changes are tried to be omitted, but there is no guarantee for that.
You can try to decrease the likelihood of breaking a certain feature, by contributing an appropriate test case/suite for this feature. Regardless of that, keep in mind, that there is no guarantee of backwards compatibility.
If full backwards compatibility should every become a requirement, a central stable API project should be created, that defines the backward compatible API for all other projects. This API should consist of one fluent build interface, that constructs and runs a program. This way, internally great changes and refactorings are still possibles without breaking compatibility. The integration between the stable API and the other projects should be done in a dedicated project, so it's easy to statically check for a given project, if it really depends only on the API and not on a specific implementation.
Forwards compatibility is not a goal.
Make clear what is guaranteed and do this only via an explicit public interface. Prefer backward compatible changes to backward incompatible changes. Make as much undefined as possible in order to minimize the set of guarantees. Breaking other software, because it relied on implementation specifics is better, than breaking other software via incompatible API changes. Feature flags are your friend.
Exceptions are not considered to be part of the API. Only unchecked exceptions are used.
When a function is to be removed, that is important for code users and is used often, prefer the following protocol, in order to avoid broken builds or functionality. Steps can be skipped, if doing them are not useful for the project:
- Mark the function as deprecated and thereby create compile time warnings.
- Create a warning log message, when the deprecated method is executed.
- Create error log message, in order to create urgency.
- Create feature flag for function and enable function by default.
- Make the feature disabled by default via the feature flag.
- Drop support for the function and mark it as unsupported.
- Delete the function, if it is not important enough. Alternatively move the function to a different deprecation project, so it's not included in the original project. Instead, the function will be injected into the original project via the deprecation project.
If many slow function removals are present, this is called migration based development, as it allows to change many things quickly at low cost in the near future. This makes it possible to turn technical debt into a technical investment.