When comparing the traditional approach (maven.compiler.source and maven.compiler.target) to the modern approach (maven.compiler.release), you are choosing between basic bytecode generation and true cross-compilation safety. Here is why maven.compiler.release is almost always the better choice for modern Java (like Java 17 or 21):

The Classic Pair (source + target)

  • What they do:
    • source tells the compiler what Java syntax version you are writing (e.g., allow records or pattern matching).
    • target tells the compiler what class-file version format to output (e.g., Java 8 bytecode).
  • The Hidden Trap (API Pollution): If your project is set to target Java 8 (target = 8), but you accidentally use a brand-new method introduced in Java 11 on the String class, it will compile successfully if your IDE/compiler classpath is running on JDK 21. Why? Because the compiler looks at the modern JDK’s standard library during compilation. However, when you run that code on a real Java 8 runtime, it will immediately crash with a NoSuchMethodError.

The Modern Standard (release)

  • What it does: It sets source, target, and swaps out the compiler’s bootstrap classpath to match the exact target version.
  • The Safety Net: If you set <maven.compiler.release>11</maven.compiler.release> while running on JDK 21, the compiler actively restricts itself to only see the APIs that existed in Java 11. If you try to call a Java 17-only method, it will cause a hard compilation error right away, saving you from nasty runtime surprises in production.

Summary

  • Use maven.compiler.release for modern Java projects because it gives you foolproof cross-compilation safety and requires only one line of configuration.
  • Only use the separate source / target properties if you are doing low-level compiler hacking or working with ancient build toolchains that do not support the --release flag.