Hi Dmitri,

Following up here since it connects with PR5052, which just merged.
CatalogConfigEndpointContributor landed in runtime/service for now. On that
PR I argued core is too broad and runtime is too Quarkus-specific for it,
and suggested punting the real "where does this go long-term" question to a
separate issue rather than blocking the merge on it. You agreed, merged as
is. I don't see that issue filed yet, so figured this thread works as well
as any.

A few things weren't fully clear to me in the writeup:

   1. The API/SPI split hinges on "when multiple alternatives exist", is
   that about how many alternatives exist today, or design intent to allow
   more later? A plugin point with exactly one default implementation,
   built that way on purpose, still reads like an SPI to me even with a single
   implementer.
   2. Same question on the placement rule, you allow the SPI to live inside
   X when X is already guaranteed to be a dependency of its own
   implementations, curious what governs it when that's true for some
   implementers and not others.
   3. And on point 4, the compile-error safety net assumes downstream
   rebuilds against new releases before it matters, not every consumer does.
   Probably its own thread though, I'm thinking of more on the client and
   server side (schema versioning on the wire, not something to hash out here).

Separately, curious how general the placement rule is meant to be.
Polaris's own dependency graph, every extensions/* implementation already
has to depend on polaris-core, lands on keeping SPIs like PolarisAuthorizer
and PolarisMetaStoreManager inside core rather than split out.

Is "ideally X-spi per module" the default shape with the in-X case being
rare, or are both equally "ideal" depending on which way the dependency
graph points, no inherent preference either way?
And on GenericTableEndpoints, the thing I raised on the same PR, it's not
an SPI either, nobody implements a wire-format constant. Does your model
have a read on where that kind of thing goes, or is it genuinely outside
what you're describing here?

One more thing, more analogy than a literal claim, not saying the
REST-serving layer is an SPI. But if you squint at it that way, Quarkus
being today's default impl, a hypothetical Spring binding being another, it
points at a real in-between layer: framework-agnostic REST-serving logic
that isn't quite core and isn't quite runtime either, the same shape as the
GenericTableEndpoints question, just one layer up. Your examples are all
in-process, CDI-selected at runtime, this would be a build-time,
different-deployable choice, a different axis than what you described. Not
asking you to settle whether it's an SPI, more whether that in-between
layer is something your placement thinking has a slot for.

-ej

On Mon, Jul 13, 2026 at 5:42 PM Dmitri Bourlatchkov <[email protected]>
wrote:

> Hi All,
>
> I'm sending this email hoping to confirm alignment of the Polaris community
> members on SPI development principles in this project. I do not think we
> need strong and formal rules / guidelines as long as the community has
> consensus on the general approach.
>
> Recap: SPI = Service Provider Interface
>
> * What is the difference between API and SPI?
>
> API is meant to be invoked by a client on a library (or service) in order
> to cause the library (service) to perform a certain action.
>
> SPI is meant to be implemented by a library / service plugin in order to
> provide a method for performing certain actions when multiple alternatives
> exist.
>
> Example from Polaris:
>
> IcebergRestCatalogApiService is an API that represents the IRC spec in
> terms of java classes.
>
> Authenticator is an SPI that defines what Polaris code requires an
> authorization framework to implement. The default impl.
> is DefaultAuthenticator, OPA and Ranger implementation and alternatives
> that can be plugged in via CDI + configuration.
>
> * Where should SPI classes be located normally?
>
> Without considering the legacy code in Polaris, I believe SPI classes
> should be located such that they require minimal depdency leaks into the
> module implementing the SPI. Ideally all SPI classes used by module X
> should be located in a module "X-spi" to convey that they are associated
> with X.
>
> This is not to say that existing code must be refactored immediately to fit
> these patterns. This is a proposal for future evolution.
>
> If module X can be expected to be a dependency of its SPI implementations,
> the SPI classes can be placed inside X itself.
>
> * Should SPI classe be in polaris-core?
>
> If polaris-core requires defines a plugin point, the SPI for that can
> certainly be in polaris-core.
>
> BasePersistence is an example of a core SPI.
>
> * Can SPI interfaces have breaking changes from the java language
> perspective?
>
> Yes. Unline API, SPI interfaces define what plugins must implement. not
> what clients rely on.
>
> For example, adding a parameter to an SPI method will cause a compilation
> error downstream, but it's ok. This is a signal to the downstream SPI
> implementation that it is expected to process the new parameter. It is a
> good breakage in the sense that it will inform the downstram implementation
> and prevent invalid behaviour that might occur as a result of ignoring the
> new parameter.
>
> In many cases this is not a hard failure downstream. Normally, the
> downstream project will get those compilation errors inside a PR that
> updates its Polaris dependencies. It will not be a deployment time error
> (which is common with service API changes).
>
> Thoughts?
>
> Thanks,
> Dmitri.
>

Reply via email to