Netflix Java at scale 2026: JDK 25, ZGC everywhere, Claude Code migrations
- https://www.youtube.com/watch?v=HhPs2sKlQYg
- Original title: Wait, I thought Java was dead?
Recap of Paul Bakker's (Java Champion, Netflix staff engineer) "How Netflix Uses Java" talk at JavaOne 2026, the sequel to last year's edition (~50% new material). Netflix now runs 3,000–4,000 Java applications (growing, not shrinking), has finished its two-year Spring Boot 3 migration, made generational ZGC the default garbage collector for every service, and is retrying virtual threads on JDK 25. The big new twist is AI: the Spring Boot 4 migration is driven entirely by Claude Code running headlessly as batch jobs with per-upgrade subagents, and production microservices embed Spring AI agentic workflows — with the Netflix platform team concluding AI agents handle migration corner cases better and cheaper than deterministic tools like OpenRewrite.
Architecture refresher
- Two buckets: streaming (user-facing, millions of users, huge RPS, multi-region, aggressive timeouts, non-relational data) and studio apps (low traffic, relational DBs, much lower tolerance for failure — you can't lose a film schedule to a retry). Same architecture for both.
- GraphQL request hits a federated gateway that fans out to microservices called DGSes (Domain Graph Services); the DGS framework (GraphQL for Java on Spring Boot) is open source and is ~80% Kotlin. Service-to-service is gRPC plus federated GraphQL: one big schema implemented by many services, the gateway stitches responses — how a monolithic-looking API hides thousands of microservices.
- Open Connect: ~18,000 servers (racks of SSDs) shipped to ISPs; video streams from your ISP's rack, not AWS. Saves Netflix and the ISP money and is faster for users. Management software: Java + Spring Boot.
- Why Java: "the best trade-off between runtime performance, developer productivity, and maintainability." For long-running server workloads the JVM's JIT does runtime optimization AOT-compiled languages can't; services running for days or weeks often catch up to or beat C++. "Java" includes Kotlin — Netflix has plenty (sponsor segment: Kotlin is ~40% less code, null safety, coroutines, Koog framework for JVM AI agents).
- Testing: they use the plain
@SpringBootTest(not slice tests) to test one functionality through all layers, GraphQL to database; since that's slow with thousands of beans, they built custom test slices like@EnableDgsTestbootstrapping only needed framework pieces.
Spring Boot 3 done, Spring Boot 4 via Claude Code
- Spring Boot 2→3 (javax→jakarta rename, caused by Oracle keeping the Java trademark when Java EE moved to Eclipse) was solved with a now open-sourced Gradle plugin doing bytecode rewriting at artifact resolution time (works because APIs are identical, only package names differ), plus OpenRewrite recipes and Gradle Lint. After two years, the migration is complete.
- For Spring Boot 4 they ditched OpenRewrite entirely: the migration is driven through Claude Code, run headlessly as a batch job via a workflow engine they built on top of it. Every upgrade is a separate prompt on a separate subagent with its own context window; scoped work composes into a fully migrated app and can run in parallel.
- When a run fails, a human can inspect the exact state of both Claude and the source code where it failed — recreating the reasoning trail a deterministic tool gives you ("error on line X").
- Rationale straight from Paul: OpenRewrite rules are really hard to write to cover all corner cases; agents are more flexible and cheaper to write. One of the world's most sophisticated Java platform teams evaluated deterministic tooling vs AI and picked AI.
- But it's not "let AI go willy-nilly": they find where Claude is inefficient or wrong and write deterministic scripts to collect data or do specific tasks, plugged back into Claude. Engineering is still required.
- Why upgrade at all if SB3 works: the cost of not upgrading compounds faster than the pain of upgrading (aimed at shops still on Java 8).
JVM performance: GC and virtual threads
- JDK 8 → 17 gave Netflix 20% less CPU spent on garbage collection just from G1 improvements.
- JDK 21 brought generational ZGC (near-zero stop-the-world pauses, concurrent work, young/old separation — big win for services with long-lived caches). Netflix made ZGC the default for all services, not just high-RPS ones, because with aggressive IPC timeouts every GC pause meant failures, retries, extra load, and cascading cluster problems. With ZGC pause times dropped to zero.
- Virtual threads: last year's rollout deadlocked — synchronized blocks pinned virtual threads to platform threads until every platform thread was held by a virtual thread waiting on a lock that could never run. JDK 25 fixes pinning, so they're rolling virtual threads out again.
The silent structured-concurrency bug
StructuredTaskScope.open()+scope.fork()creates new virtual threads — but ThreadLocal values do not copy to new threads, and nearly all implicit context in Java server apps lives in ThreadLocal: Spring Security's current user, Micrometer tracing context, connection-pool transactions.- Result: forked tasks run with empty thread locals — REST calls fail with no security context, and you can't debug it because there are no traces. Code compiles, passes basic tests, silently breaks in production.
- Scoped values (finalized in JDK 25) were supposed to fix this, but Paul sees no practical migration path since every framework uses ThreadLocal.
- Workaround: pass a custom ThreadFactory to
StructuredTaskScope.openand use the Micrometer Context Propagation library to copy thread locals into new virtual threads. Less efficient than scoped values, but lets you use structured concurrency with existing frameworks today.
Spring AI agentic workflows in production
- Paul's taxonomy: level 1 — simple LLM call (summarize/classify; where most companies stop); level 2 — agentic workflow: multiple LLM calls chained/routed/parallel, but you define the steps and the LLMs are tools inside them; level 3 — autonomous agent (Claude Code / Codex figures out the steps itself).
- Netflix uses level 3 for migrations, but level 2 for features inside Java services, because they want predictable step order with LLMs filling in the smart parts.
- Example: their Spring startup-time profiler showed which beans are slow, but findings weren't actionable when the slow bean was in a library you don't own. New agentic workflow on top: (1) feed profiling data to an LLM to rank the worst offenders, (2) fetch the library's source code, (3) feed the source to another LLM to diagnose, (4) tie it into a concrete recommendation ("this class in this library loads data at init, consider async"). Easy to build with Spring Boot + Spring AI.
- Java AI ecosystem is mature: Spring AI built in, LangChain4j widely used — the "AI means Python" assumption is outdated.
Takeaway
- Java is very much alive: Netflix's Java footprint is growing, the JVM keeps getting faster, and cutting-edge AI-driven engineering (headless Claude Code migration engines, production agentic workflows) is happening in Java and Spring Boot, not just TypeScript. And please upgrade from Java 8.