Articles from Oracle
- Incompatibilities in J2SE 5.0 (since 1.4.2)
- Java SE 7 and JDK 7 Compatibility
- Java SE 6 Compatibility
- Java 2 Platform, Standard Edition Version 1.4.0 Compatibility with Previous Releases
- Kinds of Compatibility: Source, Binary, and Behavioral
- javac: Cross-Compilation Options
Six kinds of compatibility
Each Joda-Time relase has descriptions of incompatible changes categorized by six kinds:
Compatibility between 2.8 and 2.9
Build system — Yes
Binary compatible — Yes
Source compatible — Yes
Serialization compatible — Yes
Data compatible — Yes
— DateTimeZone data updated to version 2015g
Semantic compatible — Yes
When binary compatibilities are broken then Majour Version changed. For example v2.0 changelist
Explanation from Stephen Colebourne, author of Joda-Time:
Not part of compatibility, just a fact about the build system in use
Example: in v2.2
- Ant build removed. Build only on Maven now.
Also, I think, it may be changing in artifact coordinates: groupId, artifactId or even changed repository. Maybe some changes in manifest, for example required dependencies. Or added OSGI manifest info. Maybe some classes was repackaged. Or old artifact was built with ANT and doesn’t contains a Maven’s pom.xml manifest like horrible Xerex library and then was mavenized.
It also may be recompilation with optimizations or without debug information.
But I think that changing in packaging may be a separate kind of compatibility change.
Binary compatible and Source compatible
Whether the whole API is binary compatible or source compatible. See this document
Whether the serialization format is compatible with the previous version.
Whether the data, time-zone data in this case, has changed.
For example: some time zone was changed or even removed. If your database stores old timezone id and application trying to create a date object with this timezone id you’ll get an exception.
Whether there is some other semantic change. This is relevant when the classes and methods have not changed, but the meaning of a method has.
See section Serialization incompitables below
For example: v2.0 has a lot of semantic changes
Previously, DateTimeZone.forID matched time zone names case-insensitively, now it is case-sensitive
I think it always hard to separate semantic changes when dealing with bugs. For example, JDK has bug when using null as key in Map
And that was a question is this a bug or feature.
Another one example, is Apache Commons Lang library. In version 3 the methods StringUtils.isAlpha(), isNumeric() and isAlphanumeric() now all return false when passed an empty String. Previously they returned true.
Semantic looks like correlated with behavior compatibility. When changed not signatures but they flow or contract.
Also it’s often situation when behavior was just undefined by contract. For example old class Vector doesn’t declare behavior about removing elements during iteration. That’s why was created new class ArrayList and Vector was accepted as Superseded.
So, deprecation, as any changes in contract may be also some kind of semantic change.
Also I think that it should be some kind of «reverse compatibility» that means a contract usage. For example, I saw an issue when subclass of HashMap doesn’t follow a contract. This change was compatible in all ways but all clients become incompatible. How to predict it, I don’t know.
Each of developer has experience when you can’t change something in your API because there is some clients that abuse it or implemented incorrectly. So it should have it’s own classification and developers should think about this kind of reverse incompatibility.
I mean the complexity of program in wide sense: algorithm speed, it’s derivate and consumption of resources (memory, i/o, CPU, disk space), and even source code beautification and structural changes like refactoring. You know, some algorithms may use more memory but use low processor.
For example, in some early versions of 64 bit JDK your application may fail with OutOfMemory error. This was change absolutely compatible in all categories mentioned before.
Another one sample is when new version of program is working more slowly than previous.
It may not change a contract and flow or anything else may it’s also may be changes that requires an attention.
Tool for checking API and BPI
- Java API Source Compatibility Checker
- Versioning java smells for animal-sniffer
- Java API Compliance Checker (Java ACC)
- Andrey Ponomarenko developed few tools for tracking API changes like Java API Compliance Checker (Java ACC) and you can hire him.
- Clirr (used by Joda-Time)
- Evolving Java-based APIs 2
Service provider interface (SPI)
Hey guys, I had in a project the following:
String variable = grailsApplication.config.grails.property.name + grailsApplication.config.grails.anotherproperty.name
And was working for almost a year but suddenly it stop working and throwing an error regarding the ConfigObject doesn’t have a plus method, did something similar happened to anyone?
Did someone knows why it was working and suddenly stop working ?
It doesn’t work because one of ‘property.name’ or ‘anotherproperty.name’ is not set.
If some option is not set, then ConfigSlurper return instance of ConfigObject.
That’s a common mistake.
Good news is that empty ConfigObject can be casted to false by the Groovy Truth.
Thus, to avoid this kind of mistakes and get null instead of ConfigObject you can use Elvis operator write something like:
// return empty list if supportedLocales isn't set grailsApplication.config.supportedLocales ?:  // return null if defaultLocale isn't set grailsApplication.config.defaultLocale ?: null
I found a great advise in article Optimal Logging and it worth to mention separately:
When errors occur, the log should contain a lot of detail. Unfortunately, detail that led to an error is often unavailable once the error is encountered. Also, if you’ve followed advice about not logging too much, your log records prior to the error record may not provide adequate detail. A good way to solve this problem is to create temporary, in-memory log queues. Throughout processing of a transaction, append verbose details about each step to the queue. If the transaction completes successfully, discard the queue and log a summary. If an error is encountered, log the content of the entire queue and the error. This technique is especially useful for test logging of system interactions.
It’s cool idea, I’ll try in practice.
And Michael Würtinger created a SLF4J extension for doing it.