Interview Prep Zoneby CuriouserLabs

Question 5 of 5 · architect level

You own 40 services on Java 8/11. Give me a migration strategy to 21, and the features that justify it.

🎤 Say this first

Treat it as a fleet program, not 40 ad-hoc upgrades: build a paved road (base images, build-tool upgrades, CI matrix running tests on both JDKs), fix the common blockers once (removed EE modules, --add-opens, agent/library versions), pilot on low-risk services, then batch the long tail; LTS-hop 8→11/17→21 rather than crawling every version. The payoff pitch: virtual threads for I/O-bound services, G1/ZGC improvements (often 10–20% infra savings just from the JDK), records/sealed/switch for code health, and staying inside the security-patch window.

The full picture

  1. Inventory & blockers: scan for internal-API usage (jdeps --jdk-internals), removed pieces (JAXB/JEE modules, Nashorn, SecurityManager deprecation, CMS), reflection-heavy libs needing upgrades (older Lombok/Mockito/ASM breaks on new class-file versions).
  2. Paved road first: one shared parent/BOM, base Docker images, toolchains in Gradle/Maven so the JDK is code, CI running the test matrix on current + target JDK — surface breakage continuously, not on migration day.
  3. Behavioral risk spots: GC default changes (8's Parallel → G1), locale/Unicode/CLDR differences, Thread.stop/finalization deprecations, TLS defaults, and JIT warm-up profile changes — canary + perf baselines per service.
  4. Rollout: pilot 2–3 representative services → template the fixes → batch by team with a deadline and a 'migration is a feature' budget line. Measure: JDK version histogram dashboard; the program is done when it hits zero-on-old.
  5. Sell the wins: fewer pauses (G1 improvements are free), virtual threads unlock simpler code + higher density, records/sealed shrink boilerplate, and CVE posture — auditors care even when engineers don't.
HopHighlights you actually use
8 → 11var, HttpClient, single-file run, G1 default, jlink; lose JEE modules (add explicit deps)
11 → 17records, sealed, switch expressions, text blocks, NPE messages, stronger encapsulation of internals
17 → 21virtual threads, pattern matching for switch, record patterns, SequencedCollection, generational ZGC
21 → 25unnamed variables (_), scoped values, structured concurrency (preview→final track), FFM API replacing JNI use-cases

🔄 Likely follow-up questions

  • How do you keep libraries (agents, APM, Lombok) from blocking the fleet?
  • What would make you choose 17 over 21 as the target?
  • How do you quantify the business case for a JDK upgrade?