Version numbers are just a form of documentation, giving the developer's best guess of whether the API broke or not.
In particular, Maven's use of groupId:artifactId:version is inadequate as an identifier, since many artifacts can have this same "identifier", e.g.
- Making changes without updating the version number (bad practice, but easily done)
- Building with different versions of dependencies. Maven allows version ranges, and resolves them based on the contents of third-party servers, so this can change over time even if we're building from the same git commit.
- Building with different property settings, e.g. due to a different profile getting 'activated' due to some ambient aspect of the system (OS, presence/absence of some file path, env vars, etc.).
- Building with different versions of Maven, Java, etc.
- The contents of Maven's cache also affects builds. If a file path exists in the cache, Maven will use it regardless of its contents. Also, undeclared dependencies can sometimes be picked up from the cache, which would otherwise fail if they weren't in the cache.
Maven also uses that scheme as a location for fetching artifacts, which is not only inadequate but also insecure (we can put arbitrary code at a certain path and Maven will happily fetch it). Maven does perform SHA512 verification, but those hashes are fetched from the same location as the artifacts, so subverting one allows subverting both.
Also, in case you weren't aware, git tags are just a convenience. Commit IDs are SHA1 hashes, so they're unforgeable and tamper-proof; whereas git tags can be deleted and recreated to point at anything.
FYI at my workplace we avoid some of these issues by:
- Performing all builds using Nix, which uses a sandbox with versions of Maven, JVM, etc. specified with their SHA256 hashes. This avoids differences due to tooling, profiles, env vars, caches, etc.
- All builds are performed without network access. This prevents Maven resolving version numbers differently.
- The SHA256 of the build environment (including all tooling, env vars, properties, source code, etc.) gets appended to the version number, which distinguishes these different artifacts from each other.
- Our projects specify the expected hash of their Maven dependencies (essentially like a lock file), which avoids having to trust third-parties for the hashes (a trust-on-first-use policy).