Set It and Ship It: How I Let AI Agents Build My Java Services While I Sleep

java dev.to

I've been doing this job long enough to be suspicious of anything that promises to make it easy. So I want to be upfront: what follows still feels a little unreal to me, even after weeks of watching it work.

Here's the short version. The current crop of agents can run for five hours straight and build almost anything you ask for, at a quality level that genuinely holds up. There's a catch, and it's the whole point of this post: they only do that if you write your requirements carefully and force the work through hard gates before it comes back to you. Skip that part and you get five hours of confident, plausible garbage. Do it well and the ceiling is set by how clearly you can think, not by the model.

Why this is the thing to get right

Give a capable agent a loose prompt and a few hours, and it will produce a mountain of output. The problem is that the mountain drifts. Little assumptions pile up, the thing wanders off from what you actually wanted, and by hour three it's polishing something you never asked for.

Gates fix that. Three of them, specifically, and they work together:

First, requirements that are actually testable - no vibes, no "make it good." Second, a separate critique agent whose only job is to check the work against those requirements and refuse to pass it until every single one is met. Third, somewhere for all this to run uninterrupted, with the permission pop-ups turned off so nobody has to babysit it.

None of these is clever on its own. Together they're the difference between a demo and a workflow you'd actually trust.

The rules I don't break

I've boiled it down to five things I won't skip, because every time I've skipped one I've regretted it:

  1. Write the requirements before the agent starts. No exceptions, even when the task feels obvious.
  2. Treat every requirement as a gate, not a suggestion.
  3. Let a different agent judge the work. The one that built it doesn't get a vote.
  4. Demand a pass on all of them. Not most. Not "basically done." All.
  5. Run it always-on with the permission prompts off.

Writing requirements that hold up

The requirements list is the contract, and honestly it's where the real work moved to. If the list is fuzzy, the output is fuzzy and the agent will happily fill every gap with a guess.

What I aim for is requirements a machine can grade without me in the room. Each one names something specific: a behaviour, a file, an interface, a limit. Each one can be marked pass or fail with no argument. Between them they cover the boring stuff too - edge cases, performance, security, docs because that's exactly what gets quietly dropped otherwise.

A quick before-and-after. Here's the version that'll burn you:

"The API should be fast and secure."

And here's the version that actually protects you:

"All endpoints respond in under 200ms at p95 under 100 concurrent requests. Every input is validated server-side. No secrets in logs. Auth required on every route except /health."

Same intent, wildly different outcome. Time spent sharpening this list pays for itself many times over across a long run, so I don't rush it anymore.

The critique gate

A requirements list nobody enforces is just a wish. The enforcement is a separate critique agent, and keeping it separate is the whole trick and you don't let the builder grade its own homework.

The loop is simple. The builder produces the work. The critique agent gets that work plus the full requirements list, then goes down it item by item and marks each one pass or fail. Anything that fails goes back with the specific gap called out, and round it goes again. The work only lands on my desk once every requirement has a clean pass.

A few things I insist on from the critic: it checks everything, not a sample. It gives me a real verdict per item, not a warm "looks good." And I write its prompt to be adversarial on purpose since I want it hunting for the gap, not looking for a reason to approve.

Where it runs matters more than you'd think

Five hours is a long time for a laptop lid to stay open. So this runs in the cloud or on a machine that never sleeps, never times out, never loses state halfway through.

The part people flinch at is turning off the permission prompts. I get it. But every "can I edit this file?" pop-up turns an autonomous five-hour build back into a job you have to sit and watch, which defeats the entire point. The gates and the critique agent are what keep the run honest and not you clicking Allow forty times.

To be clear, "no permission gates" is about the agent's inner workflow, not about throwing safety out the window. Scoped credentials, a sandbox, an isolated branch, a spend limit; all of that stays. An unattended run should be contained. It just shouldn't need a chaperone.

A Tuesday with Mithun

Let me make this concrete with someone I made up who behaves exactly like the good engineers I know. Call him Mithun. He's on a Spring Boot team, and here's how one Tuesday goes now that the agents do the building.

8:45 AM - coffee and the contract

A ticket lands: add a PaymentReconciliation service that matches settled transactions against ledger entries and flags the mismatches. A couple of years ago that's two or three days of work. Mithun doesn't open his IDE. He opens requirements.md and writes the contract instead:

# Requirements: PaymentReconciliation Service

## Functional
- REST endpoint POST /reconciliation/run accepts a date range (from, to).
- Matches Transaction records against LedgerEntry by transactionId + amount.
- A mismatch = amount delta > 0.00 OR missing counterpart on either side.
- Persists a ReconciliationReport (JPA entity) with counts and line-item results.

## Non-Functional
- Java 21, Spring Boot 3.x, Maven build.
- p95 latency < 500ms for a 10,000-record range.
- 100% of new code covered by JUnit 5 tests; mismatch edge cases tested explicitly.
- No secrets in logs; all inputs validated with Bean Validation.
- Follows existing package layout under com.blute.reconciliation.

## Definition of Done
- mvn verify passes clean (compile + test + checkstyle + spotbugs).
- OpenAPI spec regenerated and committed.
- README section added describing the endpoint and mismatch rules.
Enter fullscreen mode Exit fullscreen mode

Fifteen lines. That's the job, really and the rest is typing he no longer has to do.

9:10 AM - launch and walk away

He points the builder agent at the repo and the requirements file, running in the team's always-on environment. The last line of his prompt is the one that matters:

"All work must be verified by separate critique agents against every requirement in requirements.md. Do not return until a critique agent confirms a pass on 100% of them. mvn verify must be green."

Then he closes the laptop and goes to standup. No pop-ups chase him.

9:15 AM to 11:30 AM - it runs without him

While he's in meetings, the whole thing plays out on its own. The builder scaffolds the service, the JPA entity, the controller, the matching logic. It writes the JUnit tests, including the fiddly ones - zero-delta matches, missing counterparts, rounding right at the cent boundary.

Then the critique agent picks up the diff and starts marking. It fails two items: the latency requirement isn't actually proven because there's no benchmark, and checkstyle is unhappy about the package layout. Both go back. The builder adds a timing test over a 10k-record fixture and moves two classes where they belong. The critic runs again, and this time it's clean - fifteen of fifteen, build green, OpenAPI regenerated, README updated. Nobody touched any of it.

11:35 AM - he reviews the output, not the process

Mithun gets back to a finished branch and a report waiting for him:

POST /reconciliation/run implemented ......... PASS
Mismatch rule (delta > 0.00 | missing) ....... PASS
ReconciliationReport persisted ............... PASS
Java 21 / Spring Boot 3.x / Maven ............ PASS
p95 < 500ms @ 10k records (measured 410ms) ... PASS
100% new-code test coverage .................. PASS
Bean Validation on all inputs ................ PASS
No secrets in logs ........................... PASS
Package layout com.acme.reconciliation ....... PASS
mvn verify clean (checkstyle + spotbugs) ..... PASS
OpenAPI regenerated & committed .............. PASS
README section added ......................... PASS
... 15/15 PASS
Enter fullscreen mode Exit fullscreen mode

He still reads the matching logic and the edge-case tests. Not to catch bugs - the gate already did that - but to check the approach lines up with how the team thinks about reconciliation. It does. He leaves one note about a naming choice he'd have made differently, then adds that preference to the team's requirements template so next time it's a gate instead of a comment.

2:00 PM - two more in parallel

Here's the part that reorganised his day. Because he's not hand-writing code, his bottleneck is now just thinking clearly about what he wants. So he spends the afternoon writing two more requirement lists - a Kafka consumer for settlement events, a caching layer for the ledger reads - and kicks both off to run into the evening.

6:30 PM - set it and ship it

He logs off. The critique loops grind away overnight. By morning there'll be two more branches, each with its own clean report waiting for a human read. His job quietly stopped being about writing lines and became about writing contracts, and the throughput that unlocks is honestly still hard for me to wrap my head around.

How I actually run this, step by step

If you want to copy the workflow, this is the shape of it:

  1. Scope the task and write the requirements list, every item testable.
  2. Put the hard rule in the prompt: separate critique agents check all work, 100% passes before it comes back.
  3. Provision an always-on environment with the permission gates off and the guardrails on.
  4. Launch the builder with the task and the full list.
  5. Let the critique loop run - pass/fail per item, failures bounce back to the builder.
  6. Accept the work only on a full pass.
  7. Review the output, not the process, and feed anything you learned back into the template.

The ways this goes wrong

Almost every failure I've seen traces back to one of these:

Vague requirements, where "make it good" gives the agent nothing to be checked against. Self-review, where the builder blesses its own work and the whole gate collapses. Partial passes, where "most of it's done" quietly lets the drift back in. Babysitting, where leftover permission prompts throw away the exact capability you were reaching for. And an under-powered environment, where a five-hour job dies on a machine that went to sleep at hour two.

Get the requirements right, keep the critic honest and separate, and give it somewhere to actually run - that's most of the battle. The rest is learning to trust the gates enough to close the laptop.

A quick disclaimer. Your mileage will vary. Everything above reflects what's worked for me on specific tasks with specific codebases, and none of it is a guarantee - model behavior, task complexity, and your existing test and review infrastructure all change the outcome. Treat this as a starting point, not a recipe. Run agents against isolated branches and sandboxed environments, keep credentials scoped and spend capped, and have a human read the output before anything ships to production. One thing to watch closely: token usage. Multi-hour runs with builder-plus-critique loops burn tokens fast, and every failed-and-retried requirement adds another pass over the work - costs can climb quickly and unpredictably. Set hard budget limits, monitor consumption during long runs, and price out a small task before you turn agents loose on a big one. The gates reduce risk; they don't remove it. Use judgement, start small on low-stakes work, and scale up only as you build confidence for your own context.

Source: dev.to

arrow_back Back to Tutorials