nayuki / QR-Code-generator

High-quality QR Code generator library in Java, TypeScript/JavaScript, Python, Rust, C++, C.

Home Page:https://www.nayuki.io/page/qr-code-generator-library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Java version 1.8 is not backward compatible

manuelbl opened this issue · comments

The new version 1.8 is not backward compatible with version 1.7. This is in violation of the semantic versioning used by Maven and Gradle, and breaks existing software. Such a change would have been ok if the major version number was increased.

An example of a breaking change is the change from QrCode.encodeText(String, Ecc) to QrCode.encodeText(CharSequence, Ecc). While this isn't a problem if the code is recompiled, it affects software using the library indirectly such as my SwissQRBill library.

Would you accept a PR that fixes the incompatibilities (e.g. by reintroducing QrCode.encodeText(String, Ecc) in addition to the new method) and would you quickly release a new version?

Class java.lang.String implements interface java.lang.CharSequence. Where is the incompatibility?

The type of the method parameters becomes part of the method signature. So my library compiles to Java byte code that references a method encodeText(String, Ecc) from the class io.nayuki.qrcodegen.QrCode. Such a method no longer exists in version 1.8 of the QR-Code-generator library. So applications using my library will thrown an exception at run-time when the code of my library is loaded and the referenced methods cannot be found.

The fact that String implements CharSequence helps if I recompile my library and release a new version. But it does not help for the binary distribution of libraries via Maven Central and similar repository.

The rules and consensus for Maven and Gradle is to use semantic versioning at the binary level, not just at the source code level. So a minor version update should be binary compatible with previous versions. For that reasons, I've specified the dependency to the QR-Code-generator library with a version range from 1.6 to 1.999999. This adds flexibility if an application has multiple dependencies on the QR-Code-generator library, e.g. by using it directly and also indirectly via my library. They can then upgrade to a newer version of the QR-Code-generator library without having to ask for a new version of my library, e.g. because of bug fixes or new features in the QR-Code-generator library. But this flexibility only works if semantic versioning is adhered to.

I see, thanks for the detailed explanation. The thing about Java binary vs. source compatibility makes sense now that you mentioned it (and I read about it in Effective Java), but binary compatibility is essentially never on my mind as a developer. FYI I do publish source libraries on Maven too. What you said about an application having multiple dependencies on one library is an interesting point as well.

Semantic version is too onerous for me to follow. To date, each one of my versioned releases is a minor release in the traditional sense (think of Java 1.0, 1.1, 1.2, etc.), to convey that it's an incremental improvement and not a groundbreaking rewrite that changes how everything is done. Each of my releases is about a year apart and is a major release in the SemVer sense, with some features that are strictly incompatible.

To give examples of my past or future changes that break Semver:

  • Changing or removing methods.
  • Renaming types/modules/files.
  • Changing exception messages or types. I know some developers will parse error strings.
  • Changing the QR Code penalty calculation algorithm. The generated QR Code is still scannable, but the exact bits will differ due to the mask pattern changing. So this will break hard-coded test cases.
  • Changing the text segmentation algorithm. This means that some previously unencodable texts become encodable or vice versa. This will also break hard-coded test cases.

I apologize for the inconvenience I have caused you. But I really want to keep my codebase clean and minimal and avoid having to explain things like "this function exists here to preserve compatibility with 1.7.0; it wouldn't exist in a rewrite".

See also: #117

Ok, understood.

You should probably state on your web site that you are not using semantic versioning for the Java version (as is strongly recommended by Maven Central). It would have help me.