gnodet commented on PR #12745:
URL: https://github.com/apache/maven/pull/12745#issuecomment-5436611779
Thanks for the effort on this, @Hiteshsai007. The `api`/`implementation`
split is a feature the community has been asking for, and it's worth getting
the design right.
Here are a few concerns and an alternative design proposal:
### Model version
Changing the semantics of `compile` scope cannot land in model 4.1.0 — it
would need to be model `>= 4.2.0`. The 4.1.0 model is already defined and
changing existing scope behavior within it would be a breaking change to an
established contract.
### Design concern: flipping `compile` semantics
Making `compile` non-transitive and adding `api` as the transitive
replacement inverts 20+ years of meaning. This creates a serious migration
hazard: bumping `modelVersion` to 4.2.0 without migrating scopes would silently
break runtime classpaths — everything compiles fine, then fails at runtime with
`ClassNotFoundException`. The current consumer POM builder strips
non-transitive scopes, so downstream consumers would lose those dependencies
entirely.
### Alternative proposal: additive, not rename
Instead of changing what `compile` means, introduce two **new** scopes
alongside the existing ones:
| Scope | Build paths | Transitive | Gradle equivalent |
|---|---|---|---|
| `compile` (unchanged) | compile + runtime + test | Yes | `api` |
| `compile-only` (unchanged) | compile only | No | `compileOnly` |
| **`api`** (new) | compile + runtime + test | Yes | `api` |
| **`implementation`** (new) | compile + runtime + test | No |
`implementation` |
`api` is semantically identical to `compile` — it exists to express intent
("this is part of my public API"). `implementation` is the new scope that
provides the non-transitive behavior the PR is after. `compile` keeps its
current meaning across all model versions — including as the default scope when
`<scope>` is omitted.
This is purely additive — no existing behavior changes, no silent breakage
on model version bump, and no change to the default scope. Projects adopt
`api`/`implementation` at their own pace.
### Phased rollout
A concern with the additive approach is adoption — if the default doesn't
change, there's no forcing function to get projects to use the new scopes. A
phased rollout addresses this:
**Model 4.2.0** — introduces `api` and `implementation` as new scopes. A
model-level flag (e.g. `<defaultScope>`) controls the default scope when
`<scope>` is omitted. The flag defaults to `api` (preserving today's transitive
behavior). Early adopters can set it to `implementation` to get non-transitive
defaults immediately.
**Model 4.3.0** — the flag defaults to `implementation`. The default scope
becomes non-transitive for everyone.
The effective model builder already resolves undefined scope to `compile` —
it would just need to check the flag and resolve to `api` or `implementation`
accordingly.
This gives projects three levels of adoption speed:
1. **Conservative**: bump to 4.2.0, don't touch the flag — nothing changes
2. **Gradual**: bump to 4.2.0, start using `api`/`implementation` explicitly
on some deps
3. **Aggressive**: bump to 4.2.0, flip the flag — get 4.3.0 behavior
immediately
### Consumer POM compatibility
The consumer POM needs to remain consumable by Maven 3.x. The scope mapping
when downgrading to model 4.0.0 would be:
- `api` → `compile` — exact semantic match ✅
- `implementation` → `runtime` — consumers can't compile against it
(correct), it's present at runtime (correct), slightly wider transitivity than
ideal but safe ✅
This means `api`/`implementation` should **not** force the consumer POM
model version up — they have clean 4.0.0 equivalents. The
`DefaultConsumerPomBuilder` would need a scope mapping table instead of the
current `isTransitive()` filter, but no changes to Maven 3.x are needed.
### Migration via `mvnup`
- **4.1.0 → 4.2.0**: no-op — new scopes are available, default behavior
unchanged
- **4.2.0 → 4.3.0**: for every dependency without an explicit `<scope>`,
inject `<scope>api</scope>` to preserve transitive behavior. Developers can
then tighten to `implementation` where appropriate. Projects that already set
the flag or adopted `api`/`implementation` explicitly need no changes.
This mirrors the migration path Gradle users went through when `compile` was
deprecated in favor of `api`/`implementation`.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]