Cytoscape 3 Documentation : Backwards Compatibility Guide
Date: February 24th 2011
Status: First version
A key motivation for writing the 3.0 API is to avoid the constant change that the 2.X API currently suffers. Because nearly all classes in Cytoscape 2.X are effectively part of the public API it has become extremely difficult to change one part of the code, without suffering unintended consequences in other parts of the code. As a result, apps written against earlier versions of the API (e.g. 2.5) will often stop working in more recent versions (e.g. 2.7). We recognize that this is extremely frustrating for both app authors and users. To address this problem, we’ve written Cytoscape 3.0 with the goal of providing clear backwards compatibility guidelines and guarantees for core developers and app developers alike. Our goal is to guarantee that any app written against the 3.X API will continue to work unmodified up until version 4.0 of Cytoscape. This document describes how we intend to accomplish this.
Clearly Defined API
The first step is to define exactly what constitutes the public API. In Cytoscape 2.X, this was simply any public class available on the classpath, which meant that the API included over two thousand public classes. In 3.0, we are greatly restricting this space and clearly defining which classes and packages are public and which are private. The public API will be defined by a set of jars that are identified with the “-api” suffix and that contain OSGi public packages.
The API will be limited to specific types of classes to ensure that implementation and interface don’t mix. For Cytoscape 3.X, API jars should only contain a limited subset of Java classes. These include:
- Abstract implementations
- Static classes
- Final classes
- Unit tests
The majority of the Cytoscape public API will consist of interfaces. Most interfaces will define OSGi services that will be provided by implementation jars. Interfaces will be one of two types, interfaces to be implemented (SPI); and interfaces to be used (API). Because any change to an interface that is implemented (e.g. a listener interface) will break backwards compatibility, SPI interfaces will rarely change. API interfaces that we expect people to use, but not necessarily implement (e.g. CyNetwork), may see new methods added in future versions. We will clearly distinguish between the two types of interfaces.
In many cases we will provide abstract implementations of interfaces as a convenience for users. Any abstract implementation will be designed for inheritance, meaning all methods will be properly documented regarding what other methods they call and what (accessible) internal state gets changed.
With the OSGi service model we have very limited need for static singleton classes, as such there will be far fewer static classes in 3.0. In the rare case that we do provide a static class, the class will be strictly stateless.
There are some instances where we do want to provide implementations of certain classes as part of the API. These are generally very simple classes that are referenced by an interface and are free of any external dependencies. One example is the implementation of various event objects paired with their listener interfaces. These classes will be made final so that they cannot be extended or modified by users. This means that we can add methods to these classes without breaking backwards compatibility!
Since enums are static and can't be extended or supplemented these are appropriate for inclusion in API jars. However, in contrast with the multiple fairly complicated enums found in 2.x (e.g. VisualPropertyType), enums will be quite simple in 3.0.
All interfaces and abstract classes in 3.0 should have public, abstract unit tests that can be extended and executed by implementation jars. The intent is for these these abstract test cases is to provide implementation independent functional requirements that all implementations must pass. This has the twin benefits of providing a clear implementation standard as well as making it extremely easy for implementations write a test suite. All static classes, final classes, and enums will be tested in the API jar.
Clearly Documented API
Every class in the public API will have a description of the backwards compatibility guarantees provided by the class included in its Javadoc. For instance, the documentation will clearly distinguish interfaces that we expect users to extend (and thus will never change) and interfaces that we do not expect users to extend, which means we may add methods in the future.
The Semantic Versioning standard (http://semver.org) defines clear guidelines for versioning jars. We will use Semantic Versioning beginning with version 3.0.0 of Cytoscape. Briefly, these rules state that:
- The Major version number must be incremented if any backwards incompatible change is introduced. This means that if an method is removed or the signature of an existing method is changed, then the major version must be incremented. This means, that we
- The minor version must be incremented if any backwards compatible features are added. This means that any time your code can do something new (e.g. new method, new constructor, etc.), then minor version number must be incremented.
- The patch version must be incremented for changes that fix bugs but do not alter the API.
All library jars developed by the Cytoscape team, whether they are API or implementation libraries, will be versioned according to the Semantic Versioning standard.
The Cytoscape application in 3.0 just consists of a collection of jar files. This means that the application version number will be a function of the API jars that comprise the application. This means that if an API jar changes major version number, then the application must as well. Likewise for minor and patch versions.
Cytoscape implementation jars, on the other hand, will only ever impact the patch version number of the application, and this includes the entire replacement of one Cytoscape implementation for another. This is because all Cytoscape implementation jars are considered “hidden” dependencies, something that we will use OSGi to enforce. No one should even be able to use a Cytoscape implementation jar, so changes to such a jar shouldn’t cause any problems.
Changes to third party library jars will also only result in the change of the patch version number of the Cytoscape application. This is because dependencies on third party jars are considered implementation details of the Cytoscape implementation code. Furthermore, OSGi allows different versions of the same library to co-exist within the same application, so apps can depend on a different version of the same library used by Cytoscape.
In the rare event that a Cytoscape API depends on a third party jar, then changes to that dependency will be subject to the semantic versioning rules.
All the rules in the world won’t help without an enforcement mechanism. For Cytoscape 3.0, OSGi provides that mechanism. OSGi allows the packages within a jar to be declared either public or private. If a package is public, then anyone may access the classes in the package like normal. On the other hand, if a package is declared private, then no one may access the classes in that package. Private packages make themselves useful to the outside world, but providing instances of OSGi services whose interfaces are declared in different, public packages.
In Cytoscape 3.0 all API jars will consist only of public packages whereas all implementation jars will be strictly private. This means that OSGi will only allow users to create dependencies to the API and not to the implementation.
Any methods, fields, or classes that become deprecated in the API will continue to exist in the API and have a provided implementation (as necessary) for all releases up until the next major version change. This is because removing deprecated code is a necessarily backwards incompatible change.
Expressing Dependencies on the Cytoscape API
App authors will be obligated to express their dependencies on Cytoscape in terms of a version range, rather than a single version. This will allow any app bundles to work with all versions greater than or equal to the version minimum. For example, if an app author depends on version 3.1.0 of the model-api jar, then that dependency should be expressed a [3.1.0,4.0.0), which means the range from version 3.1.0 up to, but not including version 4.0.0.
Through the use of a clearly defined API, a strict versioning protocol, and a mechanism to enforce which dependencies can be established, we expect that app authors who’ve written code against a proper range of versions should not be required to recompile, let alone rewrite their apps.
List any issues, conflict, or dependencies raised by this document
Add comment here…
How to Comment
Edit the page and add your comments under the provided header. By adding your ideas to the Wiki directly, we can more easily organize everyone's ideas, and keep clear records. Be sure to include today's date and your name for each comment. Try to keep your comments as concrete and constructive as possible. For example, if you find a part of the documentation makes no sense, please say so, but don't stop there. Take the extra step and propose alternatives.