A few months ago I asked an AI agent to write some Quarkus code for me. The code came back, and it worked. It compiled, it booted, it answered. If the bar were “it works”, the story would end here.
Then I read the code:
@Produces
@ApplicationScoped
public PromptGuardAgent promptGuardAgent() {
ChatModel guardModel = OllamaChatModel.builder()
.baseUrl(baseUrl)
.modelName(guardModelName)
.temperature(0.0)
.think(Boolean.FALSE)
.timeout(Duration.ofSeconds(timeoutSeconds))
.build();
return AiServices.builder(PromptGuardAgent.class)
.chatModel(guardModel)
.build();
}
This is real output from an agent in one of my projects. It even shows some care: temperature zero, an explicit timeout. But look at what it is: a producer assembling the ChatModel by hand and calling AiServices.builder. That’s raw LangChain4j inside a Quarkus application. The Quarkus way is declarative: @RegisterAiService on the interface, the model named in application.properties. And the difference is expensive. With manual wiring you lose what the integration gives you for free: metrics and traces on every model call, declarative fault tolerance, guardrails you can plug in. None of that stops the app from running today. That’s exactly where the danger lives: the problem doesn’t show up on day one.
The bill always arrives
The gap between “works” and “well built” appears when the system lives on. It appears when someone has to maintain that code six months from now. It appears when traffic grows and resource usage becomes real money. And the pattern multiplies: a class with ten lines that could be one is a detail; ten thousand of them is an extra codebase you didn’t need, polluting the context of every future agent session that touches it.
Markus Eisele, who writes The Main Thread and is on my team at IBM, compressed all of this into one line at his JCON keynote this year: “Code is cheap now. Software is not. You just get there a lot faster now.” The cost of producing code collapsed. The cost of software (intent, correctness, maintenance, complexity) stayed exactly where it always was. He closes with two variations I keep coming back to: code is cheap, intent is not; code is cheap, verification is everything.
Matt Pocock inverted the same argument at the AI Engineer conference: bad code is the most expensive it has ever been. If your codebase is good, AI multiplies your speed. If it’s bad, AI sinks with it and produces more bad code on top. Engineering fundamentals gained value in the age of agents. Good practices stopped being a reviewer’s nitpick and became an economic argument.
And none of these pains are new. “It didn’t do what I asked”: Frederick Brooks, The Design of Design. “Too verbose”: Eric Evans, Domain-Driven Design. “Done, but broken”: Andy Hunt and Dave Thomas, The Pragmatic Programmer. “Hard to test”: John Ousterhout, A Philosophy of Software Design. These books didn’t break with AI. They became more important. We don’t need new fundamentals; we need the usual ones, written in a format the agent can use.
Venkat Subramaniam summed up the irony: developers say AI-generated code is bad; AI was trained on code written by developers; that’s called karma.
A good engineer with no memory
Why does the agent miss what a senior engineer gets right? Because everything an experienced engineer carries implicitly (the house patterns, the known traps, the right way to test) the agent doesn’t carry between sessions. It’s born again in every conversation. That knowledge has to be written somewhere the agent reads. If it isn’t written, for the agent it doesn’t exist.
Eisele has a phrase for the consequence, and I’ve adopted it: any structure you present to the model beats any clever prompt. A good prompt solves the task at hand. Structure (a conventions file, skills, MCP servers) solves every task that comes after. It’s the difference between asking well and teaching once.
Fabio Akita put it in a way that lands: AI reflects who you are. If your engineering practice is messy, the agent amplifies the mess. If it’s explicit and disciplined, the agent amplifies that instead. So the question stops being “does AI write good code?” and becomes: what am I giving it to read?
All of this lands on one thesis: the agent needs to read before it writes. The good news is that in our ecosystem, that work had already started.
What the Quarkus ecosystem already gives you
The Quarkus team attacked part of the problem with the Quarkus Agent MCP: a standalone Model Context Protocol (MCP) server that teaches your agent to code using Quarkus. It creates projects and manages the app lifecycle. It exposes skills per extension and runs semantic search over the whole documentation. And it carries a design detail I find brilliant: it runs outside the application process, so when the app crashes, the agent stays alive, reads the structured exception and goes fix the problem.
When I started using it, the quality of the Quarkus code my agents produced jumped immediately.
But a real application is bigger than the framework. There are the design patterns of the Java world, there are tests, there’s persistence. And there’s LangChain4j, which for me is the most critical case: a young ecosystem with a fast release pace, where the model has little training baggage. That’s exactly where being specific and opinionated pays the most. The stakes are not small either: the State of Java survey by Azul found that among companies building AI features, half use Java in the implementation. This problem is ours.
Quarkus Agentic Scaffolding
So I built Quarkus Agentic Scaffolding. In one sentence: an installable artifact that puts Quarkus + LangChain4j best practices in front of the agent before it writes the first line.
It’s open source under Apache 2.0. The templates are code that compiles, validated in CI. And it installs on practically any assistant that supports the Agent Skills format (Claude Code, Codex, Copilot, Cursor, IBM Bob and others) with one command:
npx skills add eldermoraes/quarkus-agentic-scaffolding
The architecture separates two things that usually come tangled together. On one side, an always-on conventions file (CLAUDE.md for Claude, AGENTS.md for the rest) that declares the rules the code must follow. On the other, three skills that execute procedures. The skills don’t repeat the rules; they point to the file. One source of truth.
A sample of what the conventions declare:
- Java 25+ as the baseline
- Virtual threads by default for blocking work
- records, sealed types and pattern matching where they clarify intent
- Platform BOMs instead of pinned extension versions
- CDI-first
- Declarative AI services with
@RegisterAiService - Declarative guardrails
- Easy RAG first, portable to something heavier later
- Zero-code observability with Micrometer and OpenTelemetry
Nothing in that list is generic clean-code advice. It’s the current way of building on this stack, and every line exists because it fixes a mistake agents make in the real world.
The plugin ships three skills.
/setup-agentic-scaffolding prepares the ground, and it carries a design decision I care about: the plugin doesn’t try to be an island. It verifies your toolchain: JDK 25 / GraalVM, JBang, a container runtime. It registers the Quarkus Agent MCP and Context7, for library documentation fresher than the model’s training data. And it recommends the Superpowers skill set for process. Each of these pieces expands what the agent can do well, and the plugin uses all of them along the way.
/scaffold-project covers both moments of creation: the new project end-to-end, and the new component inside a project that already exists (an AI service, tools, agents and multi-agent workflows, RAG, an MCP client or server, guardrails). The project skeleton comes from the Quarkus Agent MCP; the skill applies the layout, the base application.properties and the starter templates. You trigger it in natural language: ask for a RAG pipeline and it enters the flow on its own.
/audit-project closes the loop, because most projects won’t be born now; they already exist. It compares the project against the conventions and returns a prioritized list: what’s missing, where the evidence is, what the suggested fix looks like. It’s read-only by default and only applies fixes with your confirmation. There’s a foundation behind keeping this separate, and Andrej Karpathy named it: generating code and judging code are different capabilities. The audit puts the agent in the reviewer’s seat with an explicit ruler in hand. The same kind of agent that wrote the code at the top of this post, now enforcing the practices it used to skip.
Does it save tokens?
A fair question: do the skills reduce token usage, or do they only improve quality? Honest answer: I haven’t measured it yet. The runs were clearly faster, and my reading is that more upfront direction means less reasoning, because reasoning is largely the agent validating its own inferences. Fewer inferences to validate, fewer tokens burned. I intend to measure it properly and publish the numbers.
Skills: a new kind of open source asset
Conventions rot if nobody maintains them, so the project tracks the releases of Quarkus, Java and LangChain4j: when the stack moves, the target moves with it. Semantic versioning, a changelog, CI making sure the templates still compile.
There’s a line from the DevStar working group, inside the Quarkus project, that frames this whole space: tools do things; skills teach how to do things well. I believe this becomes a new kind of open source contribution: library maintainers and platform teams writing knowledge for agents to read. And it works inside your company too. Your team’s conventions can live in a file the agent reads before writing. The model is the same.
Try it on your own project
The sentence that made me start this project is the invitation I’ll leave you with: I want to shorten your path to building applications that follow best practices while using AI.
Install it, scaffold a new project, audit one an agent already wrote. Then tell me what you find. Issues, pull requests and criticism are welcome: github.com/eldermoraes/quarkus-agentic-scaffolding.