Conversation
api/surface.txt has one line for every exported type and every member a caller outside the module can name, and SurfaceTest regenerates it and compares. A change to the API is now a change to that file, in the same commit, where it is the first thing in a diff rather than something a user finds after the release. This is the same mechanism zu-go uses, held to the same file name and the same place, because a reviewer moving between the clients should not have to learn a second one. Write the file with mvn -pl zudb test -Dzu.surface.write=true, which is the one command to reach for when the change was meant, and running it is how you say so. Nothing here comes from Class.toGenericString. That method is not the same on every JDK the api job builds for: 21 leaves the sealed marker out and 25 puts it in, so the first version of this reported a change nobody made as soon as it ran on the matrix. Everything is spelled out off the class file instead, and the output is byte for byte identical on 21 and 25. The failure prints the lines that moved rather than the file twice. Five hundred lines against five hundred lines is a report nobody reads, and the two lines that changed are the report.
tamnd
added a commit
to tamnd/zu
that referenced
this pull request
Aug 22, 2026
api/surface.txt has a line for every exported type and every member a caller outside the module can name, and a test regenerates it and compares, so a change to the API is a change to that file in the same commit. Same file name and same place as zu-go, because a reviewer moving between the clients should not have to learn a second mechanism. No japicmp and no revapi: both compare against a published baseline and nothing is published yet. A checked-in file compares against the last commit, which is what a reviewer is looking at anyway. tamnd/zu-java#14. That takes the practice score to 70, and what is left is leaks and install, which are apparatus, and api-map and perf, which are reports the release collects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the
stabilityitem on the zu-java scorecard, which the clients spec words as: an API stability gate wired in, the tool that tells a reviewer the public surface moved before a user finds out.api/surface.txthas one line for every exported type and every member a caller outside the module can name, acrossdev.zudbanddev.zudb.spi.SurfaceTestregenerates it from the compiled classes and compares. A change to the API is then a change to that file, in the same commit, at the top of the diff, rather than an implementation-looking line somebody waves through.The file name and the place are zu-go's, deliberately. That repository already keeps
api/surface.txtin the shape of theapi/go1.N.txtfiles the language holds itself to, and a reviewer moving between the clients should not have to learn a second mechanism. Write it withmvn -pl zudb test -Dzu.surface.write=true, which is the one command to reach for when the change was meant, and running it is how you say so.There is no japicmp and no revapi here, and that is on purpose rather than for want of trying. Both compare against a published baseline, and nothing is published yet, so on this repository today they would either compare against nothing or hold the client to a version that does not exist. A checked-in file compares against the last commit instead, which is the thing a reviewer is actually looking at, and it goes on working the same way after the first release.
One thing worth reading in the test. Nothing in the generated file comes from
Class.toGenericString, because that method is not the same on every JDK theapijob builds for. JDK 21 leaves the sealed marker out of it and JDK 25 puts it in, so the first version of this passed locally and would have gone red on two rows of the matrix for a change nobody made. A gate that cries wolf on the version matrix is a gate people turn off. Everything is spelled out off the class file instead: modifiers masked to what a caller can see, the kind, the type parameters, the permitted subclasses, the supertype and the interfaces, and the same for members. Checked on both JDK 21 and JDK 25 and the output is byte for byte identical.The failure prints only the lines that moved:
There is a second test asserting the generator found something, because a generator that quietly found nothing would make every future change pass, and that is the one failure this test cannot report on itself.
No CI change is needed. The
apijob already runsmvn -pl zudb -am teston 17, 21, 25 and 26-ea, so the gate runs four times per push and reads nothing but the compiled classes, which means it answers on a clone with no library staged and no Rust installed.Checked on a Linux box: the gate passes, and it bites when the file and the code disagree. The full reactor is green, API, TCK, Panama, JNI, Arrow and benchmarks.
The README gains the inside-the-repository table the other clients have, with the paragraph that says what the file is and how to regenerate it.