~/dev/valenciajug-2026 · java -jar turning-java-ai-ready.jar title · 01
OpenJDK Runtime Environment Temurin-25+36 (build 25+36-LTS)
OpenJDK 64-Bit Server VM (mixed mode, sharing)
2026-06-18 18:30:00.001 INFO c.m.valenciajug.Talk Starting Talk using Java 25 on stage:java
2026-06-18 18:30:00.018 INFO c.m.v.boot.ReactorLoader Discovered 6 acts on classpath
2026-06-18 18:30:00.042 INFO c.m.valenciajug.Talk No active profile set, falling back to default: "live"
Valencia JUG Meetup hosted at Xplore Group 18 June 2026 · 18:30 — 19:20 · Valencia

Turning Your Java Project Into an AI-Ready Codebase.

2026-06-18 18:30:00.118 INFO c.m.valenciajug.Talk speaker Marc Nuri @ Red Hat — Sr. Principal SWE · Developer Tools
2026-06-18 18:30:00.119 INFO c.m.valenciajug.Talk event Valencia JUG 2026 · Meetup
2026-06-18 18:30:00.122 INFO c.m.valenciajug.Talk Started Talk in 0.122 seconds · ready in PT50M
$
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-01-context/pom.xml act 01 / 06
$ mvn -pl :valenciajug-act-01-context -am verify
[INFO]Scanning for projects...
[INFO]────────< com.marcnuri.valenciajug:valenciajug-act-01-context >────────
[INFO] Building valenciajug-act-01-context 1.0.0-SNAPSHOT [1/6]
Act I · @Act(1) · public class Context

Context: why I care about this.

[INFO]Reactor Build Order:
[INFO] 
[INFO] valenciajug-act-01-context ....... BUILDING ...
[INFO] · valenciajug-act-02-unit-of-productivity .... PENDING
[INFO] · valenciajug-act-03-the-catch ..... PENDING
[INFO] · valenciajug-act-04-teach-agents .... PENDING
[INFO] · valenciajug-act-05-codebase-feedback .... PENDING
[INFO] · valenciajug-act-06-close ......... PENDING
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · about/DayJob.java act 01 · about/day_job · 02
// whoami · day_job

Marc Nuri

senior_principal_software_engineer @ red_hat  ·  developer_tools
// open-source I maintain @ work
kubernetes-mcp-server
github.com/containers/kubernetes-mcp-server
Go 1.6k
fabric8 kubernetes-client
github.com/fabric8io/kubernetes-client
Java 3.7k
Eclipse JKube
github.com/eclipse-jkube/jkube
Java 849
@manusa
@marcnuri.com
@MarcNuri
/in/marcnuri
marc nuri | red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · about/AfterHours.java act 01 · about/after_hours · 02
…and a bit unhinged.
// whoami · after_hours

…and on the side.

probably-unhealthy_obsession_with_software  ·  weekends & evenings
// side quests · open-source on my own time
helm-java
manusa/helm-java
Java 67
actions-setup-minikube
manusa/actions-setup-minikube
JavaScript 62
ai-beacon
manusa/ai-beacon
Go JS 25
electronim
manusa/electronim
JavaScript 251
YAKD
manusa/yakd
Java 55
podman-mcp-server
manusa/podman-mcp-server
Go 73
Full Stack Quarkus and React
Packt · 2022
blog.marcnuri.com // posts on Java, Kubernetes & dev tools
marc nuri | red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · FieldNotes.java act 01 · field-notes · 03
// whoami · field_notes

Field notes,
not doctrine.

These patterns come from maintaining several Java and open source projects with AI-assisted workflows.

They worked in my context. Your codebase, team, and constraints may differ.

Real projects Specific context Adapt, don’t copy
marc nuri | red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-02-unit-of-productivity/pom.xml act 02 / 06
$ mvn -pl :valenciajug-act-02-unit-of-productivity -am verify
[INFO]Scanning for projects...
[INFO]──< com.marcnuri.valenciajug:valenciajug-act-02-unit-of-productivity >──
[INFO] Building valenciajug-act-02-unit-of-productivity 1.0.0-SNAPSHOT [2/6]
Act II · @Act(2) · public class UnitOfProductivity

AI changes the unit of dev productivity.

[INFO]Reactor Build Order:
[INFO] 
[INFO] valenciajug-act-01-context ....... SUCCESS [04:12]
[INFO] valenciajug-act-02-unit-of-productivity .... BUILDING ...
[INFO] · valenciajug-act-03-the-catch ..... PENDING
[INFO] · valenciajug-act-04-teach-agents .... PENDING
[INFO] · valenciajug-act-05-codebase-feedback .... PENDING
[INFO] · valenciajug-act-06-close ......... PENDING
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · OldWorld.java act 02 · sequential · 04
The Old World · 10 months ago

One developer, one task at a time.

Synchronous · sequential · always on the implementation path
·
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
Yousolo dev
Email
Standup
Implement feature #42
Review PRs
Expenses & chores
Fix bug #18 + tests
1-1
Implement #42 (cont.)
Agent 1
Agent 2
Agent 3
Agent 4
Agent 5
Agent 6
»One task at a time. You're always on the implementation path.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · NewWorld.java act 02 · parallel · 05
The New World · today

You orchestrate. Agents execute.

Concurrent · many tasks in flight · the bottleneck moves from compute → to you
·
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
Youorchestrator
↑ A1 A2 A3
↑ A4
↑ A5
↑ A6
↑ research · overnight
Spawn
Email · standup
Review + iterate A1
Review A2
Iterate + merge A2
1-1
Review + merge A3
Review A4-A6
Spawn
Agent 1parallel session
Implement #42
Feedback v1
Round 2
Agent 2parallel session
Tests PR #37
Helper utils
PR feedback
Agent 3parallel session
Investigate & fix #18
Regression + cleanup
Agent 4parallel session
Triage + labels
Quick win #51
Agent 5parallel session
Draft API docs
Changelog
Agent 6parallel session
Refactor parser internals
Research
»Agents are fast. They finish and wait — for you to review, unblock, merge, spawn the next one.
»The key shift: implementation work can run in parallel.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-03-the-catch/pom.xml act 03 / 06
$ mvn -pl :valenciajug-act-03-the-catch -am verify
[INFO]Scanning for projects...
[INFO]────────< com.marcnuri.valenciajug:valenciajug-act-03-the-catch >────────
[INFO] Building valenciajug-act-03-the-catch 1.0.0-SNAPSHOT [3/6]
Act III · @Act(3) · public class TheCatch

The catch: AI amplifies your project.

[INFO]Reactor Build Order:
[INFO] 
[INFO] valenciajug-act-01-context ....... SUCCESS [04:12]
[INFO] valenciajug-act-02-unit-of-productivity .... SUCCESS [05:48]
[INFO] valenciajug-act-03-the-catch ..... BUILDING ...
[INFO] · valenciajug-act-04-teach-agents .... PENDING
[INFO] · valenciajug-act-05-codebase-feedback .... PENDING
[INFO] · valenciajug-act-06-close ......... PENDING
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · interlude/AudienceCheck.java act 03 · raiseHand() · 06
# a quick show of hands · be honest

Who tried AI on a real Java project
and got slop?

> Raise your hand if any of this feels familiar.
invented API brittle mocks huge diff CI red after 28m broke compatibility
agent.log — ci · pull-request #4271 FAILED
$ agent implement ISSUE-1337 --project legacy-java-service
opened a 42-file PR
wrote a confident explanation
invented an API that does not exist
mocked the bug away
broke public behavior
mvn verify failed after 28 minutes
BUILD FAILURE · exit 1 $
marc nuri|red hat
Valencia JUG · 2026
[ scene · chaotic ops room · sludge ]
~/dev/valenciajug-2026 · PoorlyArchitectedProject.java act 03 · bad-amplifier · 07
Huge PRs, no baby steps
No tests, behavioral / black-box
No spec docs — nobody knows what it does
Vague, unrefined issues & tickets
No user / customer / PM feedback loop
Problems hidden for months… exposed in hours
// the takeaway
Poor project hygiene becomes worse when AI speeds everything up.
marc nuri|red hat
Valencia JUG · 2026
[ scene · clean ops room · sail catches the tailwind ]
~/dev/valenciajug-2026 · AgentReadyProject.java act 03 · good-amplifier · 08
Small, atomic PRs
Strong test coverage
Clear specs
Well-defined, broken-down issues
Short feedback loop
Fast CI/CD pipeline
// the takeaway
AI doesn't replace engineering discipline — it amplifies it.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · framing/Legacy.java act 03 · legacy-is-unsafe-change · 09
// reframing

Legacy is not age.
Legacy is unsafe change.

[ stable ]

Long-lived
Java codebase

Fabric8 Kubernetes Client Java 11
Helm Java Java 8
  • Stable contracts
  • Behavioral tests
  • Known boundaries
  • Fast feedback
AI can accelerate it.
[ risky ]

AI-assisted codebase
without guardrails

  • Rapid throughput
  • Weak tests
  • Unclear contracts
  • Hidden coupling
  • Review debt
Slop becomes legacy fast.

Age is not the problem. Unsafe change is.

// AI makes unsafe change compound sooner.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · readiness/Flywheel.java act 03 · flywheel · 10
Compounding
AI leverage
1
Bounded readiness work
tests · docs · cleanup · CI
2
Project gets clearer
knowledge · boundaries · specs
3
Agents become safer & faster
better PRs · fewer wrong turns
4
Developer gains capacity
review · scope · architecture
// bridge to act IV

AI-readiness
is a flywheel.

“Start with bounded readiness work that makes the repository easier to change.”

// what's next Map·Roads·Guardrails
// takeaway
Every improvement lowers the cost of the next change.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-04-teach-agents/pom.xml act 04 / 06
$ mvn -pl :valenciajug-act-04-teach-agents -am verify
[INFO]Scanning for projects...
[INFO]──────< com.marcnuri.valenciajug:valenciajug-act-04-teach-agents >──────
[INFO] Building valenciajug-act-04-teach-agents 1.0.0-SNAPSHOT [4/6]
Act IV · @Act(4) · @Delivers(Map, Roads) · public class TeachAgents

Teach agents what the project means.

[INFO]Reactor Build Order:
[INFO] 
[INFO] valenciajug-act-01-context ....... SUCCESS [04:12]
[INFO] valenciajug-act-02-unit-of-productivity .... SUCCESS [05:48]
[INFO] valenciajug-act-03-the-catch ..... SUCCESS [06:21]
[INFO] valenciajug-act-04-teach-agents .... BUILDING ...
[INFO] · valenciajug-act-05-codebase-feedback .... PENDING
[INFO] · valenciajug-act-06-close ......... PENDING
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-04-teach-agents/AGENTS.md act 04 · agent map · 11
// act 04 · agent map

Give the agent a project map.

README.md tells a person how to use it.
AGENTS.md tells an agent how to change it.

README.md tells a person how to use it.
AGENTS.md tells an agent how to change it.
one file at the repo root, whatever your agent calls it:
AGENTS.md · fabric8 / kubernetes-client
# Build & validation
Prefer `make` targets over raw Maven commands.
`make generate-model` is the singular model regen path.
# Mock-mode tests
Default to plain HTTP for new mock-mode tests.
The first HTTPS call in each surefire fork pays
a ~7-8s TLS / Vert.x cold-start cost.
# CI reproduction
Reproduce 2-core CI locally:
  docker run --cpus=2 --memory=4g …
  1. [ 01 ] Golden path what should I run?
  2. [ 02 ] Known trap what will waste time?
  3. [ 03 ] Local checkpoint how do I know before opening a PR?
// takeaway
Not generated. Project-specific.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · fabric8io/kubernetes-client/pom.xml act 04 · boundaries · 12
// act 04 · boundaries

Make Java boundaries obvious.

The agent shouldn't have to reason about the whole
repo to make a local change.

your-java-project no module boundaries
acme-platform/
└ src/main/java/com/acme/app/
VertxConnector.java
OkHttpConnector.java
ClusterClient.java
ModelTypes.java
CrdSupport.java
Extensions.java
… + ~600 more files, one module
// imagine a project like this
No module boundaries.
The agent reasons about the whole repo, and changes too much.
// context & economics the whole repo in the window: more tokens, slower loops, more wrong turns.
fabric8io / kubernetes-client the change has a home
kubernetes-client-api/public API · compiler-checked
kubernetes-client/implementation
httpclient-{jdk, okhttp, jetty}/other backends
httpclient-{vertx, vertx-5}/the change
kubernetes-model-generator/generated · don't hand-edit
extensions/knative · tekton · istio
openshift-client/openshift support
fabric8io / kubernetes-client · #7812 Merged

test: use ephemeral ports in httpclient-vertx{,-5} SSL/body-stream tests

manusamerged · 2026-05-18
±4 files+20 −12
2 moduleshttpclient-vertx{,-5}
0 public API· 0 generated
// the change had a home.
// takeaway
Can't tell where a change belongs? The agent changes everything. The safer the boundary, the smaller the reasoning radius.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · manusa/helm-java/issues/351 act 04 · leverage · 13

Shape the work upstream.

Reason once, where it's expensive.
Implement many times, where it's cheap.

$$$think once strongest model, upstream
  • understand the change
  • explore the codebase
  • surface edge cases
  • define acceptance criteria
  • propose the tests
produces a detailed spec: the road others follow
¢× N then cheap execution
  • a smaller, faster model
  • an external contributor
  • future-you
manusa / helm-java · #351 Open

feat: implement helm rollback command

enhancement help wanted good first issue
Contract
Helm.rollback("r")
    .toRevision(n).call() → Release
Validation
nested k8s tests in HelmKubernetesTest
./mvnw test -pl helm-java
Java 8 · no var, no records reference impl → StatusCommand.java
// takeaway
Anyone can pick this up now.
Do the expensive thinking once, so the rest stays cheap.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · .claude/skills/regen-java-models-from-upstream/SKILL.md act 04 · skill · 14
// act 04 · skill

From prompt to skill.

AGENTS.md was the map.
SKILL.md is the ritual.

~/dev/fabric8-kubernetes-client · claude PROMPT
01> Implement issue #1337.
02> Implement issue #1337 using TDD and project conventions.
03> Implement issue #1337 using TDD and project conventions, then run adversarial review agents and address substantive findings before returning to me. → returns reviewable work
fabric8io / kubernetes-client · #7528 Open

chore(deps): bump cluster-api-provider-metal3 from 1.9.3 to 1.12.2

dependabot[bot] opened 5 days ago
generate-model — cluster-api/v1beta1 not found
check-no-schema-file-modified
build · 47 compile errors
0 reviews · 0 comments · 0 humans assigned · last activity: dependabot · 4h ago
— No conversation yet. —
stale · 5 days upstream reorganized its API. someone has to read Go.
.claude/skills/regen-java-models-from-upstream/SKILL.md SKILL
---
name: regen-java-models-from-upstream
description: bump Go dep · regen Java models · fix · open closing PR
---
# Steps
1. Pick up the failing Dependabot PR.
2. Decode upstream Go API changes← read Go to ship Java
3. Run make generate-model← canonical command (~15 min)
4. Fix Java compile · refToJavaTypeMappings in pom.xml
5. Update CHANGELOG · alphabetical← avoids merge conflicts
6. Commit · push · open the PR that Closes Dependabot.
fabric8io / kubernetes-client · #7544 Merged

chore(deps): bump cluster-api-provider-metal3 from 1.9.3 to 1.12.2

Claude Code opened & merged by manusa · just now
+cmd/reflection.go· Go import path → api/core/v1beta1
+openshift-misc/pom.xml· refToJavaTypeMappings
+Java models moved· clusterapi.v1beta1clusterapi.core.v1beta1
Closes #7528 32 files · 3 cascade bumps · was open 5 days // ran while I slept.
// takeaway
The tenth time, you write the prompt down. That's a skill. // the prompt you got tired of retyping. 5 days. No humans. Still red. // upstream Go reorg · nobody wants to read it. A maintainer's ritual, written down. Once. // ~10 lines of frontmatter · ~300 lines of markdown · executable. Who wants to read Go to ship a Java release? The agent does. // hours of Go reading · slow model-regen cycles · diff-check by hand    unattended.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-05-codebase-feedback/pom.xml act 05 / 06
$ mvn -pl :valenciajug-act-05-codebase-feedback -am verify
[INFO]Scanning for projects...
[INFO]───< com.marcnuri.valenciajug:valenciajug-act-05-codebase-feedback >───
[INFO] Building valenciajug-act-05-codebase-feedback 1.0.0-SNAPSHOT [5/6]
Act V · @Act(5) · @Delivers(Guardrails) · public class CodebaseFeedback

A good codebase tells the agent when it is wrong.

[INFO]Reactor Build Order:
[INFO] 
[INFO] valenciajug-act-01-context ....... SUCCESS [04:12]
[INFO] valenciajug-act-02-unit-of-productivity .... SUCCESS [05:48]
[INFO] valenciajug-act-03-the-catch ..... SUCCESS [06:21]
[INFO] valenciajug-act-04-teach-agents .... SUCCESS [07:02]
[INFO] valenciajug-act-05-codebase-feedback .... BUILDING ...
[INFO] · valenciajug-act-06-close ......... PENDING
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-05-codebase-feedback/SpecsVsTests.java act 05 · pushback · 15
// act 05 · the codebase pushes back

Specs explain intent. Tests enforce it.

But text doesn't push back.

docs/specs/config.md // advisory
# Default API server
When no API server URL is configured,
fall back to https://kubernetes.default.svc/.
// an agent may read this.
// or skip it.
// or break it and still get a green build.
ConfigTest.java // enforced
@Test
@DisplayName("falls back to kubernetes.default.svc")
void fallsBackToDefaultApiServer() {
var config = new ConfigBuilder().build();
assertThat(config.getMasterUrl())
.isEqualTo("https://kubernetes.default.svc/");
}
BUILD FAILED - ConfigTest > falls back to kubernetes.default.svc
expected: "https://kubernetes.default.svc/"
actual: "https://my-cluster.example.com/"
// what the agent sees when it gets it wrong.
// takeaway
In Java, the best spec compiles, runs, and fails loudly. // text waits to be read. tests run on every commit.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-05-codebase-feedback/FailureAsSpec.java act 05 · failure as spec · 16
// act 05 · same failure · richer signal

A failing test should explain the broken behavior.

JUnit gives the agent a behavior breadcrumb

ShoppingCartTest.java @nested · @displayname
@Nested @DisplayName("during checkout")
class DuringCheckout {
@Nested @DisplayName("with a discount code")
class WithDiscount {
@Test @DisplayName("applies the discount before tax")
void appliesDiscountBeforeTax() {
assertThat(cart.total())
.as("discount must apply before tax")
.isEqualTo(money("90.00"));
}
}
}
surefire · ShoppingCartTest // agent broke the contract
$ mvn -Dtest=ShoppingCartTest test
running . . .
BUILD FAILED — ShoppingCartTest
during checkout
└─with a discount code
└─applies the discount before tax
[discount must apply before tax]
expected:$90.00
actual:  $99.00
// takeaway
The failing test reads like a sentence — top to bottom. // @Nested + @DisplayName + descriptive assertions ⇒ this output, for free.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-05-codebase-feedback/BlackBoxTests.java act 05 · unit of behavior · 17

Black-box tests: the unit is behavior.

why these tests survived the AI era

PodServiceTest.java ✓ passing △ AGENT-EDITED
// white-box · couples to internals
@ExtendWith(MockitoExtension.class)
class PodServiceTest {
@Mock(answer = RETURNS_DEEP_STUBS)
KubernetesClient client;
@InjectMocks PodService service;
 
@Test
void deletesPod() {
service.delete("foo");
 
verify(client.pods()
.withName("foo")).delete();
}
}
 @ExtendWith(MockitoExtension.class)
 class PodServiceTest {
  @Mock(answer = RETURNS_DEEP_STUBS)
  KubernetesClient client;
  @InjectMocks PodService service;
 
  @Test
  void deletesPod() {
- service.delete("foo");
+ service.delete(Pod.class, "foo");
 
- verify(client.pods()
+ verify(client.resources(Pod.class)
  .withName("foo")).delete();
  }
 }
asserts who called what agent rewrote it too. nothing pinned the contract.
PodService.java // stable △ refactor applied
// system under test
void delete(String name) {
client.pods()
.withName(name)
.delete();
}
-void delete(String name) {
+<T extends HasMetadata>
+void delete(Class<T> type,
+ String name) {
- client.pods()
+ client.resources(type)
  .withName(name)
  .delete();
 }
internals an agent might refactor pods() → resources(T) · same behavior
PodBehaviorIT.java ✓ passing ✓ UNTOUCHED
// black-box · asserts outcomes, not calls
@QuarkusTest @WithKubernetesTestServer
class PodBehaviorIT {
@Inject KubernetesClient k8s;
 
@Test
@DisplayName("DELETE removes the pod")
void removesPodFromCluster() {
k8s.pods().resource(pod).create();
 
given().when()
.delete("/api/v1/pods/foo")
.then().statusCode(204);
 
assertThat(k8s.pods()
.withName("foo").get()).isNull();
}
}
asserts the pod is gone zero test edits. contract held.
A method is not always a useful unit. A unit of behavior often is.
// survived agent refactors with Fabric8 kube-api-test QuarkusTest Testcontainers YAKD
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-05-codebase-feedback/ProjectStory.md act 05 · project story · 18
// act 05 · one of many recent agent-assisted PRs

Black-box tests unlocked a large change.

tedious maintenance
agent territory

manusa/yakd PR #172 merged
Update Quarkus to 3.30 and Fabric8 to 7
// fixes #146, #149 · maintenance · december 2025
The grunt work // no new feature
  • Quarkus 3.11.3 3.30.4 19 minors
  • Fabric8 6.x 7.x major bump
  • REST resteasy-reactive rest artifact rename
  • −119 legacy API fallback v1beta1 Ingress · CronJob
// I handed all of this to Claude
the compiler caught the renames
strongly typed · no hallucinated API
The safety net // already in the repo
  • REST Assured backend HTTP
  • Selenium IT full UI flow
  • Quarkus infra live WebSocket
real HTTP · a real K8s test API server · not mocks
Zero assertion changes. Same behavior, 19 versions later.
the tests pinned the behavior
// when these pass, the upgrade is done
The contract held.
Put the net up first, then delegate.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-05-codebase-feedback/FeedbackLoops.md act 05 · feedback loops · 19
// act 05 · the loop you remember // act 05 · the loop is the bottleneck

Fast feedback loops — the agent must learn quickly. An agent is only as fast as the feedback loop.

the loop you remember from 2010 is now
the loop your agent is trapped in
measured on my projects:
every lower tier buys confidence at a cost

WebLogic 10.3 · app.ear · 2010 // changing a button label · one cycle ~30 min ☠ legacy
[14:03:11] <Notice> <WebLogicServer> Starting WebLogic Server…
[14:05:42] <JDBC> pool 'AppPool' created, awaiting datasource…
[14:08:19] <Deployer> Deploying application 'app.ear'…
[14:14:07] <EJB> Initializing 312 session beans…
[14:21:55] <JNDI> Re-binding 187 names into context…
[14:28:34] <Warning> Slow startup. Re-deploy after each change.
[14:31:02] Application available at /app 
~30
minutes
one button-label change.
one feedback cycle.

// the agent is trapped in this loop too. shrink it.

seconds
in-process · zero containers
@EnableKubernetesMockClient(https = false)
Fabric8 · KubernetesMockServer · PriorityClassTest
// run on every save · default inner loop
~1
s / class
seconds
real apiserver · no kubelet
@EnableKubeAPIServer
Fabric8 · kube-api-test (envtest) · PaginationIT
// run on every save · many of these
~3
s / class
seconds
your app · booted · in-process
@QuarkusTest @WithKubernetesTestServer
YAKD · Quarkus + RestAssured (+ Selenium) · NamespaceTest
// run on every commit · fewer of these
~5
s / class
minute
kind cluster · final confidence
new KindContainer<>().start()
Helm Java · Testcontainers + KinD · HelmKubernetesTest
// run on every PR · few of these
~50
s / class
minutes
real dev cluster · pre-release
new KubernetesClientBuilder().build()
your real cluster · kubectl apply manifests · smoke tests
// run before release · a handful
~10
min / suite
The faster the loop, the more attempts the agent can afford.
// the rule of thumb many at the top few at the bottom the agent iterates where it's cheap
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-05-codebase-feedback/CIPipeline.md act 05 · ci · 20
// act 05 · the feedback loop you can't fit in a unit test

Tech debt is not optional polish. It caps throughput.

case in point:
Fabric8 CI

4 epics. ~30 flakes. ~40 PRs.
async by an agent · I just reviewed the diff (CI faster, no tests removed)
three wins:  ⏱ faster & stable  ·  $ cheaper  ·  ⚠ hidden bugs found
fabric8io/kubernetes-client · windows-build ✓ stable
// wall-clock per pipeline run · same matrix, same hardware
40m
flaky · retried 2–5×
−45%
22m
stable · first try
module · surefire test phase before → after cut
kubernetes-tests 266s109s -59%
kubernetes-client-api 84s31s -62%
kubernetes-client 153s43s -72%
⚙ async PRs authored by an agent · review: did CI get faster? did any tests get removed?
github.com / settings · billing & plans / usage Billing

CI spend per merged PR

Actions runners bill by the minute. The biggest lever is wall-clock.

−45%
billable minutes per PR
40m → 22m on Windows · similar cuts across the Linux matrix
× every PR · every contributor · every retry that no longer happens
also: cheaper runner SKUs (smaller compound)
generate-model + javadocs: macOS → ARM64 $0.062 → $0.005 / min
Linux build: x64 → ARM64 $0.006 → $0.005 / min
fabric8io / kubernetes-client · issues?q=is:closed+label:flaky label: flaky
31 closed · flaky issues, closed in the last 8 weeks is:closed label:flaky
ExecWebSocketListener.onError drops exit code on peer-close race #7700
flaky production bug

Triggered by a flaky PodTest.testExecWithExitCode. Root cause: cleanUpOnce() synchronously shut down the SerialExecutor, discarding the queued handleExitStatus — so any caller of exec() could lose the parsed exit code.

closed
27 days ago
by manusa
ExecListener.onClose / onFailure lost on close-handshake race #7779
flaky production bug

Surfaced via flake #7756. Any production transport that drops the close handshake under load left callers blocked on a latch forever.

closed
14 days ago
by manusa
SharedProcessor.distribute() NPEs if called after informer.stop() #7716
flaky production bug

Flake report on SharedProcessorTest. Root cause: SerialExecutor.execute() after shutdown — any production code stopping an informer mid-flight could hit the same NPE.

closed
22 days ago
by manusa
5+ of the 31 closed flakes were real production bugs, hidden under low CI contention.
When AI makes everything move fast, tech debt shows up as a throughput cap.
40 minutes and flaky, retried 2–5×.  Now 22 and stable, first try.
Half the billable minutes, every PR. Some jobs got cheaper runners too.
And the bonus: when the retry loop went away, flakes turned into real production bugs.
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-05-codebase-feedback/Reframe.java act 05 · the reframe · 21

Did I just advertise XP?

Extreme Programming est. 1999
  • TDD
  • small steps
  • continuous integration
  • shared knowledge
  • simple design

Discipline. Just renamed.

AI just made the cost of skipping it obvious.

marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-06-close/pom.xml act 06 / 06
$ mvn -pl :valenciajug-act-06-close -am verify
[INFO]Scanning for projects...
[INFO]─────────< com.marcnuri.valenciajug:valenciajug-act-06-close >─────────
[INFO] Building valenciajug-act-06-close 1.0.0-SNAPSHOT [6/6]
Act VI · @Act(6) · public class Close

Close.

[INFO]Reactor Build Order:
[INFO] 
[INFO] valenciajug-act-01-context ....... SUCCESS [04:12]
[INFO] valenciajug-act-02-unit-of-productivity .... SUCCESS [05:48]
[INFO] valenciajug-act-03-the-catch ..... SUCCESS [06:21]
[INFO] valenciajug-act-04-teach-agents .... SUCCESS [07:02]
[INFO] valenciajug-act-05-codebase-feedback .... SUCCESS [05:18]
[INFO] valenciajug-act-06-close ......... BUILDING ...
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · valenciajug-act-06-close/Recap.java act 06 · recap · 22
// act 06 · recap

Map·Roads·Guardrails.

Map

// this week
  • git add AGENTS.md
  • Document one thing every maintainer knows

Roads

// this week
  • Make one module boundary obvious
  • Turn one maintainer routine into a SKILL.md

Guardrails

// this week
  • Fix one flaky test for real
  • Turn one bug report into a failing test

AI takes the repetitive grind.
You keep the judgment.

marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · close/Questions.java act 06 · q-and-a · 23
jshell> audience.ask()

Thank you.
Questions?

Valencia JUG meetup · 18 june 2026
// keep in touch
@manusa
@marcnuri.com
@MarcNuri
/in/marcnuri
blog.marcnuri.com
/MarcNuri
marcnuri.com
marc nuri|red hat
Valencia JUG · 2026
~/dev/valenciajug-2026 · close/JoinUs.java act 06 · join-the-jug · 24
jshell> jug.join()

Your turn
at the front.

Valencia JUG all levels welcome
// propose a talk
Pitch us in #valenciajug, on meetup.com/valenciajug, or grab any organizer.
First talk, lightning talk, half-formed idea. Bring it. We help you get it ready.
// join the community
Valencia's tech Slack: #valenciajug in VLCTechHub.
Slack
QR code to join the VLCTechHub Slack workspace and the valenciajug channel
→ scan to join
jshell> jug.play()

One more thing.
Let's play.

Valencia JUG all levels welcome
// quick quiz · 5 questions · winners take swag
  1. 1 Grab your phone
  2. 2 Open kahoot.it
  3. 3 The PIN appears on screen — type it in
→ no app needed · just your browser
marc nuri|red hat
Valencia JUG · 2026