-
Notifications
You must be signed in to change notification settings - Fork 0
Compatibility Considerations
Compatibility refers to the ability of one version of a software component to work in place of another version of the same software component. When "upgrading" versions we refer to "backwards compatibility": can the new version be dropped seamlessly into a working system in place of the older version and the system continue to work properly. This is especially crucial for libraries, like Hibernate.
In an ideal world, this backwards compatibility would be maintained indefinitely. In the real world, as software is developed over time and evolves it is sometimes necessary to change signatures of methods and constructors or to refactor the code in ways (e.g. package changes) that might be disruptive to this backwards compatibility. Here we will discuss the considerations and guidelines followed by the Hibernate team in regards to this.
There are 2 topics related discussions when discussing .
The first topic when discussing our view on compatibility is how we name versions and the semantics of that. For the most part we follow the guidelines as defined by JBoss Project Versioning Guidelines, so that is a good background material.
Hibernate, however, applies some additional semantics. As mentioned in the project page the first number is the major version; the second number is the minor version. Hibernate considers the {major}.{minor} combination a release "family". We will discuss the implication of release families on compatibility below.
OSGi/JBoss refer to the next number in the version as the "micro". For Hibernate, this correlates to maintenance/bugfix releases within a release family. Again, we will discuss the implications of this below.
We conceptually divide the classes of the codebase into 3 broad categorizations:
- The API is the set of contracts exposed to the application. For example, Session#save. It represents the intended means for the application to interact with Hibernate. Specifically speaking, this is code that we fully expect to be directly linked to (by the compiler) as part of your application bytecode.
- An SPI represents an "integration point" with Hibernate and the contracts needed to perform those integrations.
- The internals are classes and code meant only for Hibernate usage internally.
For the most part we try to denote these distinctions through the placement of classes into packages. Classes in packages with internal in the name are part of that internals categorization. Classes in packages with spi in the name are part of the SPI. Classes in packages with neither internal nor spi in the name are considered part of the API.
Grouping code into packages in this manner is an ongoing process and is still incomplete. The reason we could not do it en masse is, oddly, backwards compatibility.
The use of package names for this is unfortunately not granular enough oftentimes. Ultimately I would envision a better solution (annotations?)
In terms of compatibility considerations, the SPI categorization is unfortunately too broad. We have been working on defining it in a more granular fashion. A good high level break down (good enough for this discussion anyway) is:
- The contract itself. E.g., the interfaces a "cache provider" would implement to provide caching services as an integration.
- Inputs and outputs defined as part of those SPI contracts.
With the understanding of versioning philosophy and categorizations, we can now discuss the expectations users should have in terms of compatibility:
API contracts should be considered stable across all releases within a major version (3.x or 4.x, etc). For example, if you develop an application initially using Hibernate version 4.0.0 and the application only relies on the defined APIs, the expectation is that you can drop in any newer Hibernate 4.x version and it will JustWork. This is what is called backwards compatibility: meaning that any changes done in 4.3, e.g., are done in such a way as to remain compatible with older (backward) versions all the way back to 4.0. I personally find the terms confusing because I think of it in terms of "porting" the application forward to use a newer version of a library. The standard term for this is backward compatibility.
The inverse is something we actually do not guarantee in regards to APIs and going back to an older version (reverting). An example here would be developing an application using the natural-id API developed in 4.2 and then trying to drop Hibernate 4.1 or 4.0 into that application. That won't work.
So within a major version we guarantee APIs to be backwards compatible, but not forward compatible. Newer releases might add to an API, but they should not alter or remove.
There are times when exceptions to this need to be made. E.g., if the design of the API itself is deemed to be problematic.
SPI contracts should be considered stable within a release family, not necessarily across different release families. We do strive to maintain backwards compatibility for SPI contracts across families, it is just not guaranteed.
Users should have no expectations of any kind for compatibility when it comes to "internal" code.