+1 to Josh's annotation-based approach over "public by default".

Java's public keyword is not an API commitment. Plenty of public methods
exist only because they're called from another package in the same project,
not because they are public APIs intended for external consumers to depend
on them. Treating Java's public keyword as API contract conflates two
different concerns.

Scope annotations (@Public/@Internal/whatever we settle on) aren't just
documentation for consumers, but also a relief to code maintainers. W/o the
annotations, every public method becomes something we can't safely rename,
update or remove, even when it was never meant to be depended on by
external consumers.

Explicit annotations let authors keep refactoring freely on everything not
deliberately promoted as public API, while still giving consumers a clear
surface to build against.

Starting narrow and widening over time takes (significantly) less effort
than defaulting everything to public API and spending years walking it
back.

- Yifan

On Mon, Aug 17, 2026 at 1:34 PM David Capwell <[email protected]> wrote:

> > They're all internal, this is a convenience to improve code sharing.
>
> Once they go into utils they are not “internal” (whatever that means).
> Why I started this effort in the first place is that the test utilities
> have been requested to be used in projects like Cassandra-ecosystem, but
> also non-cassandra projects.  Both users need to know that a hot fix
> doesn’t break their build hence why I propose basic properties you would
> expect: major versions might have breaking changes, minor / patch won’t.
> For the classes anyone has talked about moving here, non of them should
> have issues with this; we are free to add new methods over time, but
> removing causes issues for consumers.
>
> > to improve code sharing.
>
> Even if you think about this only for cassandra ecosystem, if I depend on
> version 0.1.0 and we find a bug so get that fixed and now we depend on
> 0.1.10… I shouldn’t expect to deal with breaking changes.  The mentality of
> “They’re all internal” as a justification to not document these assumptions
> causes me concern as breaking changes explicitly make it harder for parties
> to depend on these classes; breaking changes make is so much harder to
> “share code”.
>
> > We only care about versioning to ensure we don't break any builds
>
> What do you mean by this?  Major versions I am ok with breaking changes;
> minor / patch is what I want to avoid.  We can add new APIs without issue,
> the concern is only on removal.
>
> > and even decide when we upgrade the jar so can spot and fix any
> breakages.
>
> If we are upgrading to enable a new JDK change, I wouldn’t expect users to
> have to rewrite all their tests, redo all their collections, etc…. You
> shouldn’t have to worry about upgrading the version cross minor / patch
> versions, as that should impose a no breaking change rule.
>
>
> > On Aug 14, 2026, at 3:06 PM, Benedict Elliott Smith <[email protected]>
> wrote:
> >
> > Why are we defining this at all? They're all internal, this is a
> convenience to improve code sharing. We only care about versioning to
> ensure we don't break any builds, and we have all the builds, and even
> decide when we upgrade the jar so can spot and fix any breakages.
> >
> > Let's not overcomplicate things, or bind our future selves in red tape
> and regret.
> >
> > On 2026/08/14 17:57:40 Josh McKenzie wrote:
> >> While I read your email I found myself wondering "To what are we
> referring to when we say 'API'"? ;)
> >>
> >> Are you talking about a public interface? Or are you talking about "any
> class that's public and any public method within a public class"?
> >>
> >> If the latter, my primary theoretical concern is that if we pull in
> code that has components that are scoped public for cross-package
> accessibility within-project, promoting those to "public by default"
> immediately calcifies their interface whether we intend for that to be
> consumed or not. Basically, we don't have an interim scope layer between
> "package private" and "public" that corresponds to "project public". In
> theory the post JDK9 modularity kind of provides that but then everyone
> just add-opens bulldozes across things (the joys of legacy code...).
> >>
> >> I think "Any interface that's public is a public API" is pretty obvious
> to maintainers and users and good. I'm also good with "anything public is
> an API unless otherwise documented" and we add a simple annotation like
> @INTERNAL <
> https://github.com/apiguardian-team/apiguardian/blob/main/src/main/java/org/apiguardian/api/API.java#L87>
> from API Guardian to basically say "yeah, this is public, but it's not
> intended for public consumption" (I think we should roll our own to match
> our proposed lifecycles but that's a clear example of the idea).
> >>
> >> I'm worried about the approach of "Any public method in a public class
> is considered public API and you can't change it". I'd prefer we start with
> a more constrained surface area we commit to as being an API and widen it
> later if we find there's a need; it's much harder to go in the other
> direction.
> >>
> >> On Fri, Aug 14, 2026, at 1:24 PM, David Capwell wrote:
> >>> Josh and I talked about this offline and not perfectly in-sync so
> would be good to get other peoples views
> >>>
> >>> My take is that if you are adding the API to this shared repo we need
> to care about backwards compatibility so things should be PUBLIC by default
> (anything that is java public is part of the public interface and breaking
> changes are not allowed without a long enough deprecation window similar to
> Cassandra’s.). I think Josh is in favor of using a annotation to mark that
> a API is public, but without custom tooling I don’t think anyone will
> notice and then we will get to a effectively public state and breaking
> changes will be a nightmare to deal with for the community.  So if you want
> to post the API here, it should be stable and we shouldn’t be putting APIs
> that have not been fleshed out first.
> >>>
> >>>> On Aug 7, 2026, at 8:16 AM, Josh McKenzie <[email protected]>
> wrote:
> >>>>
> >>>> One thing worth immediately calling out that we might want to split
> out: we *could* do this work in Ant.
> >>>>
> >>>> It would be somewhat more imperative and brittle: we'd explicitly
> orchestrate the build ordering in Ant, while relying on the Gradle build
> inside Accord for its cassandra-utils dependency resolution / source
> substitution. That leaves us with a somewhat more complicated mixed
> Ant/Gradle build relationship but it's entirely workable for an interim
> time.
> >>>>
> >>>> So if the Gradle migration is a sticking point, we can break that out
> into a separate discussion and CEP rather than making it part of CEP-65.
> >>>>
> >>>> Doing so would mean retaining and extending some of the complexity in
> our existing build system that a future migration could remove, but it's
> not a crushing amount of additional complexity. The benefit of doing that
> work here with this CEP is that we have one more concrete build need
> providing a reason to move toward a unified build stack, and it fits the
> "clean things up and refactor as you're working on things that could
> benefit from that work" approach many have argued for in the past on the
> project.
> >>>>
> >>>> On Fri, Aug 7, 2026, at 10:58 AM, Josh McKenzie wrote:
> >>>>> As per the previous ML thread: [DISCUSS] Forking Cassandra utilities
> into a separately released library <
> https://lists.apache.org/thread/7kllp45vvonsg7ggzxpz39c5kdcy7r6g>, David
> and I put together a draft of what we discussed and worked through some
> implications and requirements that came up as we tried to nail things down.
> >>>>>
> >>>>> The goal here is to provide a material backstop to continue our
> discussion and version it. As with all CEP DISCUSS threads, this is very
> fluid and none of it should be taken as settled or an implicit mandate.
> Let's see if we can make some progress on this long-standing pain point in
> our ecosystem.
> >>>>>
> >>>>> CEP-65 DRAFT: link <
> https://cwiki.apache.org/confluence/spaces/CASSANDRA/pages/446071230/CEP-65+cassandra-utils+-+A+shared+utility+library+for+the+cassandra+ecosystem+DRAFT
> >
> >>>>>
> >>>>> *Why You Should Read This Draft:*
> >>>>> 1.  Build system impact: we need a parent project and one of its
> submodule to depend on another submodule. We're proposing freezing ant's
> API surface area, maintaining that into perpetuity, and moving to gradle
> going forward.
> >>>>> 2. Branching model: read the draft to see what "One Branch to Rule
> Them All" means.
> >>>>> 3. API Lifecycle: Is @BETA/@STABLE/@DEPRECATED enough? Do we need a
> @PRIVATE?
> >>>>> 4. To Release or Not To Release: we're proposing consumers embed
> this as a submodule initially to minimize friction in moving shared code
> into a shared space.
> >>>>> We have 1 outstanding unanswered question we didn't come up with an
> opinionated proposal for:
> >>>>>
> >>>>> What should unannotated methods and classes in the library be
> considered by potential consumers? @PRIVATE? @PUBLIC? Should we lint and
> fail on any class without a top-level annotation forcing us to make a
> choice on our dev list [DISCUSS] threads whenever we bring in new things?
> >>>>>
> >>>>> There's no timeline on this thread; let's keep turning the crank on
> this until we've hit our pareto-polish frontier. ;)
> >>>>>
> >>>>> ~Josh
> >>>>>
> >>>>>
> >>>>
> >>
>
>

Reply via email to