Thank you for contributing. Please read the following guidelines before submitting changes.
| Tool | Version | Why |
|---|---|---|
| JDK | 17 to build, 8 to verify | The published artifact targets Java 8; see below |
| Maven | 3.9.x | Maven 4 requires Java 17+ at runtime |
The artifact is compiled with --release 8, so it runs on Java 8 and up. You can develop on any modern JDK — but a JDK 9+ API that slips past --release is a bug that only shows up on a user's Java 8 runtime, which is why CI runs the whole suite on JDK 8 as well.
Careful with a global
~/.m2/settings.xml. A profile that setsmaven.compiler.source/target/releaseisactiveByDefaultin every build, including this one, and it can silently remove the Java 8 API ceiling. This POM pins all four compiler parameters withcombine.self="override"for exactly that reason — do not "simplify" them away.
Before submitting changes, make sure the following pass:
mvn clean test # the full suite, on your default JDK
JAVA_HOME=/path/to/jdk8 mvn clean test # again on Java 8
mvn -q verify # adds animal-sniffer and japicmp
mvn javadoc:javadoc # the release requires a javadoc jarThe build compiles with -Xlint:all. New warnings are worth fixing rather than ignoring.
Creating a task is billed, and it is billed whether or not the worker succeeds. Every HTTP test goes through MockWebServer; nothing in src/test may reach api.ez-captcha.com. getBalance is the only free endpoint, and even that has no business being in an automated test.
Five places, and the tests tell you when you have missed one:
| # | Where | What |
|---|---|---|
| 1 | model/task/TaskType.java |
The constant, plus an entry in KNOWN — and in SYNC_TYPES if the service documents it as synchronous |
| 2 | model/task/XxxTaskParams.java |
The parameter model, unless an existing one already fits — several types share one |
| 3 | model/solution/XxxSolution.java |
The solution model, same caveat |
| 4 | EzCapSolverClient.java |
Both solveXxx and syncSolveXxx, named after the wire type with the prefix swapped |
| 5 | examples/ + both README.mds |
One example file, plus the index tables |
TaskTypeCoverageTest guards the first four with reflection — eight assertions covering completeness, the naming rule, both methods agreeing on parameter and return types, the model's declared solutionType() matching the method signature, and no orphaned models on the classpath. Step 5 has no test behind it; examples and README stay correct by review.
Field names. A Lombok getter's implicit property name can differ from the field name, and Jackson then emits two keys for one field. Never start a field name with is, and never start one with a single lowercase letter followed by a capital (sToken). Put the wire name in @JsonProperty instead. TaskParamsWireTest.noPhantomProperties compares the whole key set and catches this.
Default values. @SuperBuilder silently ignores a plain field initializer, so a defaulted field needs @Builder.Default. javac warns about this, but a warning is easy to miss.
Commit messages follow Conventional Commits:
| Type | Description |
|---|---|
feat: |
New feature |
fix: |
Bug fix |
docs: |
Documentation change |
refactor: |
Refactor that is neither a feature nor a bug fix |
perf: |
Performance improvement |
test: |
Test-related change |
chore: |
Build, toolchain, or miscellaneous maintenance |
Release notes are generated by git-cliff from commit history, so keep commit messages consistent. There is no hand-written CHANGELOG.md — do not add one.
- Create feature branches from
main. - Keep each pull request focused on a single topic.
- Pull requests must pass CI, which includes the test suite on JDK 17 and JDK 8, javadoc, animal-sniffer, japicmp, and typos.
- Public API changes must update both
README.mdandREADME.zh-CN.md. There is no separate documentation site; the READMEs are the documentation.