Interview Prep Zoneby CuriouserLabs

Question 4 of 5 · architect level

The Java Module System (JPMS): what does it give you, and why did most applications skip it?

🎤 Say this first

JPMS gives strong encapsulation (only exported packages are visible — reflection included) and reliable configuration (missing/duplicate module errors at startup instead of runtime CNFE). The JDK itself is modularized — which is why --add-opens haunts upgrades. Most applications skipped module descriptors: classpath + build-tool dependency management already solved their problem, the split-package rule fights legacy jars, and the migration cost outweighs benefits unless you ship a platform/SDK. Its biggest wins are jlink custom runtimes and the JDK's own integrity (integrity-by-default tightening since JDK 24, JEP 498).

The full picture

  • Mechanics: module-info.java declares requires (dependencies), exports (public API packages), opens (deep-reflection permission — what Spring/Hibernate need), provides/uses (ServiceLoader).
  • What it fixes: 'public means everyone' — pre-JPMS, internal packages were public-by-necessity; modules make internals invisible even to reflection unless opened.
  • Why adoption stalled in apps: automatic modules are leaky, split packages across jars are fatal errors, many libraries lacked descriptors for years, and Maven/Gradle already gave 'good enough' boundaries. Gradle/Maven enforcement plugins or ArchUnit deliver architectural boundaries with less friction.
  • Where it matters to you anyway: JDK internals are locked (--add-opens java.base/java.lang=ALL-UNNAMED in every legacy startup script), jlink can cut a 300MB JRE to ~50MB for containers, and library authors should ship at least an Automatic-Module-Name.

🔄 Likely follow-up questions

  • What is an automatic module and its dangers?
  • How does jlink change container image strategy vs jib/distroless?
  • How would you enforce module boundaries without JPMS? (ArchUnit, Gradle configurations)