The compatibility of two software products is the ability to replace one with the other without affecting the behavior of the dependent software. Most often, the issue of compatibility is concerned with different releases of the same product. Compatibility may also concern two functionally equivalent software products based on two independent projects. This type of compatibility is not the subject of this paper since it typically is not reflected in versioning policies of the products.
Compatibility in software can be important at different levels. At the highest level, functional compatibility determines whether two software components behave similarly enough to produce the same results given the same input, i.e., whether they are functionally equivalent. An example of this may be the compatibility of the POSIX pax and tar commands.
Source compatibility, important at the source code level, determines whether two software components in their source form are similar enough so that any well-formed program that uses one of the components remains well-formed when the component is replaced with the other. An example of source compatible components are two implementations of the C Standard library, or two different implementations of the same Java specification.
Beyond functional and source compatibility, binary compatibility provides additional guarantees especially important to programs that rely on shared libraries.
For the purposes of compatibility, a versioning policy is usually established by the operating system. The policy precisely specifies how source code changes must be reflected in the versions of shared libraries in order for the latter to be correctly interpreted by the operating system software and in order for each release of the library to behave predictably when linked with and used by its software clients.
Even though most releases of popular shared libraries are versioned, not all libraries necessarily need to be, and not all of them always follow the established versioning policy. Different releases of shared libraries that do not use versioning often behave as releases of the same product based on independent projects (which may or may not be compatible), but may also behave as releases of completely independent and incompatible products.
Releases of shared libraries that do not follow the versioning policy do not behave consistently with those that do, and may behave unexpectedly to software that uses them with the implicit assumption of using a properly versioned library.
Software components other than shared libraries (such as executable programs) often adopt the same policy as shared libraries, as a matter of convenience, although the impact of deviating from the policy is often less dramatic than it is for shared libraries.
Binary compatibility is the compatibility of two software products, typically shared libraries, such that one can be replaced with the other without requiring any changes to the dependent software. Thus, binary compatibility provides a stronger guarantee than ordinary compatibility.
The binary compatibility of two releases of a shared library is determined by the binary size and layout of symbols such as functions, types, and objects, exposed directly or indirectly as part of the application programming interface (API) of the library. Binary compatibility also includes functional compatibility, i.e., the compatibility of the behavior of the new release with the old release. A release of a product whose API, at the binary level (sometimes referred to as ABI) is a superset of that of another release of the same product, and whose behavior is compatible with that of the other release, is said to be binary compatible with the other release. Note that with C++ features such as templates and inline functions, it is possible for binary incompatible changes to have no effect on the symbols exposed by the shared library binary.
Some changes in a release of a shared library, such as efficiency improvements (or regressions) or code refactoring, that have no programmatically detectable effects in behavior have no effect on the compatibility of that release of the library with the previous release. A release of a shared library containing only such and no other changes is said to be forward compatible with the prior release of the same library.
Note that adding new features to a library is not a forward compatible change since software written to take advantage of the new features will not function correctly or at all when the library is replaced with another (not necessarily prior) release not containing the features. Shared libraries are usually not designed to be forward compatible.
Changes in a release of shared library involving the addition of new features such as new functions, classes, or objects, as well as the removal of undefined behavior, i.e., changes that have no effect on the compatibility of the new release of the shared library with software that was built with a prior release of the library, are said to be backward (or sometimes upward) compatible. Software built to rely on a version of the shared library containing these changes may not be able to work correctly or at all with other releaared library binary, are not backward compatible changes. Shared libraries often, and system libraries almost always, are designed to be backward compatible.
Note that frequently, even in technical literature, the term forward Compatibility is misused in contexts where Backward Compatibility is really meant.
Source compatible changes to software are such that do not require any
modification to the source code that depends on the software. Source
compatibility also implies functional compatibility, although it does not
imply binary compatibility. Thus, source compatibility provides a weaker
guarantee than binary compatibility.
Note that source compatible changes to the shared library API are possible such that the binary layout of the data structures exposed by the library API changes, thus making such changes binary incompatible.
Functional compatibility is usually not considered alone, but instead it is subsumed by one of the other compatibilities, typically binary compatibility.
The version identifier is defined as the string <major>.<minor>.<micro> with the following semantics.
The major number starts at 1 and is incremented by one for each new release of a library that is source or binary incompatible with the previous release. Incrementing the major number resets both the minor and micro numbers to 0.
The minor number starts at 0 and is incremented by one for each new release of a library that is backward but not forward compatible with the previous release. Incrementing the minor number resets the micro number to 0.
The micro number starts at 0 and is incremented by one for each release of a library that contains source code changes that are forward compatible with the previous release. Incrementing the minor number resets the micro number to 0.