AI for Java code: agent, tests and Spring without extra edits
In a Java project an agent is valuable while it leaves your architecture and dependencies alone. Rules, build commands, test generation and a cost estimate.
5 min · · development · агенты
An AI for Java code is useful as long as it works inside your rules: your JDK version, your dependency injection style and the libraries already in the project. Without rules, an agent will gladly add Lombok to a project that never had it, pull a new library version into pom.xml or rewrite half the service layer for one small change. Below: how to set boundaries, which build commands to give, how to generate tests, how to check the result and what it costs.
What to hand to AI in a Java project
Tasks with a clear check of the result suit an agent well:
- unit tests for existing classes (JUnit 5, Mockito);
- routine code: DTOs, mappers, simple repositories;
- refactoring by example ("do it the same way as in
OrderService"); - reading stack traces, especially long
Caused bychains; - migrations between API versions inside the project.
Tasks without verification suit it worse: "improve the architecture", "optimize". Which model copes better is not known in advance, so check on your own tasks.
Rules for the agent: the instruction file
Put an instruction file in the project root: CLAUDE.md for Claude Code or AGENTS.md for Codex (see how to write CLAUDE.md). For a Java project this minimum is useful:
## Project
- Java 21, Spring Boot, Maven build (wrapper: ./mvnw).
- Dependency injection through the constructor only. No field injection.
- Lombok is not used in this project, do not add it.
- Tests: JUnit 5 and Mockito. Do not add new dependencies without asking.
## Commands
- Build and tests: `./mvnw -q test`
- One test class: `./mvnw -q test -Dtest=OrderServiceTest`
- Tests must pass before you say "done".
## Boundaries
- Do not change pom.xml, application.yml or database migrations unless asked.
- Do not rename public classes and methods outside the task.Substitute your own versions and build type. With Gradle the commands differ: ./gradlew test for all tests and ./gradlew test --tests "com.example.OrderServiceTest" for one class. If the project has no wrapper scripts, use the installed mvn or gradle.
The file should not be long: it goes into the context of every request, and every extra paragraph is billed as input tokens.
Test generation: an example
Suppose you have a service:
public class PriceService {
private final DiscountRepository discounts;
public PriceService(DiscountRepository discounts) {
this.discounts = discounts;
}
public long finalPrice(long basePrice, String promoCode) {
if (basePrice < 0) {
throw new IllegalArgumentException("basePrice must be >= 0");
}
int percent = discounts.findPercent(promoCode).orElse(0);
return basePrice - basePrice * percent / 100;
}
}A good request to the agent: "Write JUnit 5 tests for PriceService.finalPrice with Mockito. Cover: no promo code, 10 percent, 100 percent, negative price. Do not change the class itself." The expected result looks roughly like this:
@ExtendWith(MockitoExtension.class)
class PriceServiceTest {
@Mock
DiscountRepository discounts;
@InjectMocks
PriceService service;
@Test
void noPromoCodeMeansNoDiscount() {
when(discounts.findPercent("NONE")).thenReturn(Optional.empty());
assertEquals(1000, service.finalPrice(1000, "NONE"));
}The tests need to be read, not just run. Typical problems with generated tests: they check what the code does rather than what it should do (a bug gets locked into the test); needless verify calls on implementation details; Mockito stubs that are never used, which fail in strict mode. A good trick is to ask the agent to list edge cases separately before writing code, and compare the list with your own understanding.
How to check the result
- Run the tests with the command from the instruction file. Green tests from a model are not proof: make sure the new tests actually fail if you break the code (temporarily change the formula).
- Read the whole diff: no changes in
pom.xml, configs or other people's files. - Check imports and dependencies: models sometimes invent library methods that do not exist. Compilation will catch that, but non-obvious cases such as outdated APIs are better checked against the library documentation.
- For Spring, check annotations and the Spring Boot version separately: packages and behavior change between major versions. State the version in the instruction file.
To keep the agent within bounds, Claude Code and Codex can ask for confirmation before running commands; do not turn that off on your main repository. Setup is described in the Claude Code documentation; if you work in IntelliJ, see the article on JetBrains AI Assistant.
Which model to try
We do not claim any model writes Java better. Compare by price, context and strength index (Intelligence Index, Artificial Analysis), then check 5-10 of your own tasks. Zukko prices as of October 18, 2026, $ per 1M tokens:
| Model | Input / output | Official input / output | Context | Index |
|---|---|---|---|---|
claude-opus-5-5 | 0.8 / 4 | 4 / 20 | 1M | 58 |
gpt-6.1-sol | 0.27 / 1.32 | 2 / 10 | 1M | 52 |
claude-sonnet-5-5 | 0.4 / 2 | 2 / 10 | 1M | 48 |
glm-5.3-flash | 0.04 / 0.04 | 0.15 / 0.5 | 128K | 42 |
Calculation: tests for 30 classes
Assumption: per class 6,000 input tokens (the class, neighboring files, rules) and 1,500 output tokens (the test). Total input 30 × 6,000 = 180,000, output 30 × 1,500 = 45,000.
claude-sonnet-5-5: 0.18 × $0.4 + 0.045 × $2 = $0.072 + $0.09 = $0.162. At official prices: 0.18 × $2 + 0.045 × $10 = $0.36 + $0.45 = $0.81.gpt-6.1-sol: 0.18 × $0.27 + 0.045 × $1.32 = $0.0486 + $0.0594 = $0.108.glm-5.3-flash: 0.18 × $0.04 + 0.045 × $0.04 = $0.0072 + $0.0018 = $0.009.
This ignores cache and repeat iterations when a test has to be redone. See your real spend in the "Usage" section of the dashboard. Current list and prices: GET https://api.zukko.pro/v1/models.
Connecting
Claude Code: ANTHROPIC_BASE_URL=https://api.zukko.pro (no /v1), ANTHROPIC_AUTH_TOKEN=fetch_.... For OpenAI-compatible tools and scripts: https://api.zukko.pro/v1. If you need to call the model from the Java application itself, an example with Spring AI is in Spring AI and Claude. Create the key in the dashboard; set a daily limit while you test.
FAQ
Can I trust generated tests?
Only after reading them. Check that a test fails on broken code and that it verifies the required behavior rather than copying the implementation.
How do I stop the agent from changing pom.xml?
Write it in the instruction file and do not grant permission for edits when it asks for confirmation. Review the diff before committing.
Is a cheap model enough for Spring?
For routine work (DTOs, simple tests) often yes, but check on your code. For tangled things like transactions and configuration, compare with a stronger model.
What if the answer uses an outdated API?
State the Java and Spring Boot versions in the instruction file and ask it to reference specific classes. Check a doubtful fragment against the library documentation.
zukko
Pay 25×
less
for Claude Code & ChatGPT
One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.
Related articles
- Prompt Injection: What It Is and How to Protect a Bot and an AgentHow prompt injection works through user input and third-party documents, and which measures limit the damage: data isolation, tool permissions, key limits.
- Claude Code YOLO: Permission Modes and Working Without PromptsClaude Code has no mode literally called YOLO, but it has several permission modes and flexible rules. How to stop clicking Yes without losing files.
- Claude Code: a checklist of best practices for daily workHabits that make working with Claude Code predictable: a short CLAUDE.md, a plan before edits, small tasks, checking results and keeping spend under control.
- An MCP server in Java: a minimal example with one toolWe write a minimal MCP server in Java with one tool, register it in Claude Code, and learn to find the cause when the agent doesn't see it.