Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a mechanism to expose unstable APIs to plugins #66197

Open
jsnajdr opened this issue Oct 17, 2024 · 34 comments
Open

Create a mechanism to expose unstable APIs to plugins #66197

jsnajdr opened this issue Oct 17, 2024 · 34 comments
Labels
[Feature] Extensibility The ability to extend blocks or the editing experience [Package] Private APIs /packages/private-apis [Type] Enhancement A suggestion for improvement.

Comments

@jsnajdr
Copy link
Member

jsnajdr commented Oct 17, 2024

The current Gutenberg policy, aligned with WordPress backward compatibility policy, is to not expose any private APIs to plugins at all. When a Gutenberg package exposes a private API, it's supposed to be used only by another Gutenberg package. For example, the Site Editor can use a private API from Block Editor or Components.

The packages try very hard to prevent plugins from using private API, there is the lock/unlock API and also @peterwilsoncc is hardening it by regularly changing the consent string in PRs like #55182.

However, this is a situation that treats the unstable APIs as "static" -- they already are here, and sometimes we review them and promote them to stable APIs. But I think we're not paying enough attention to the "dynamic" aspect, how new APIs are created and evolved before they are stabilized.

Currently, the only way how a new API can be reasonably created and evolved is when some Gutenberg app needs it. Because the Gutenberg packages can be broadly divided into "libraries" and "apps": libraries provide components, frameworks and utilities, and the apps (Post Editor, Site Editor, the 100+ core blocks) use them. If one of the apps needs something new from the libraries, we create a private API for that. That API can be modified as we learn more about the use cases and add new ones. Sometimes we stabilize an API and make it public, available to plugins.

The downside of this is that a plugin author never has a chance to participate in the development! They can use only something that is already finished. There's no way how a plugin author can experiment with private APIs and provide feedback, and participate in their evolution. Only the Gutenberg apps and blocks can be part of this process of trial and error.

This is now becoming a problem as we're working on new projects that go beyond Gutenberg, trying to rethink and refresh the entire wp-admin experience. The WooCommerce project would like to be part of the team, to use the new components like DataView or DataForm and rebuild the admin experience with them. Their use cases are more complex and rich than what is in Core and Gutenberg, we need them to verify that we're doing the Core APIs right.

So, can we find a way to allow plugins to participate in the evolution of new APIs, while at the same time keeping the WordPress backward compatibility contract?

One source of inspiration could be the JavaScript language and the TC39 process. After a new feature is added to the language standard, it needs to be finished and it's going to be there forever. But how is the feature developed and evolved?

Their answer is that potential new APIs go through several stages, stage 0 to stage 4. The stage signals how stable the feature is and how much you can rely on it. Developers can enable the unstable features in their build tools (the Babel transpiler etc.) or use polyfills. Browsers implement unstable APIs behind feature flags, and expose them unflagged in development versions (Google Canary, Firefox Nightly). That provides opportunities for testing, for developers to try them out and provide feedback.

Could WordPress have something similar?

@jsnajdr jsnajdr added the [Type] Enhancement A suggestion for improvement. label Oct 17, 2024
@colorful-tones
Copy link
Member

This sounds like the means for a great Make proposal.

@tyxla
Copy link
Member

tyxla commented Oct 17, 2024

This might be worth feedback from a broader audience: @WordPress/gutenberg-core

@peterwilsoncc peterwilsoncc added the [Package] Private APIs /packages/private-apis label Oct 17, 2024
@peterwilsoncc
Copy link
Contributor

The background for the introduction of the private APIs in place of __unstable and __experimental APIs was due to their use in themes and plugins rendering them as faux-stable. Changing them would break many sites so WordPress became stuck with the initial API design, even though it was later discovered to be sub-optimal.

My concern with allowing plugin to opt-in to the private APIs is that WordPress would end up in a similar situation again. I worry that we'd end up in a situation in which WordPress couldn't unlock an API for risk of breaking sites (@adamziel worked on the code, so this may not be an issue -- I suggested it & try to track string changes each release).

__dangerousOptInToUnstableAPIsOnlyForCoreModules() does allow for plugins to op-in to private APIs while running the plugin, although the string needs to be maintained by plugins doing so.

// The safety measure is meant for WordPress core where IS_WORDPRESS_CORE is set to true.
const allowReRegistration = globalThis.IS_WORDPRESS_CORE ? false : true;

@jsnajdr is your request to allow opt-in to be easier only when running the Gutenberg plugin, or do you wish to be able to opt in for sites running WordPress Core?

@youknowriad
Copy link
Contributor

I feel this need everyday when developing on Gutenberg. For a long term and sustainable project, a way to have a feedback loop for APIs, just like we do for features is needed. I know it's something that WordPress never had (aside from a small period where we had the experimental APIs in Gutenberg). I don't have a solution, but it's definitely a problem that is worth solving if we want to be shipping the right stable APIs.

@draganescu
Copy link
Contributor

But isn't the consent string a way to opt in?

@youknowriad
Copy link
Contributor

@draganescu Right now, the consent string can only be used by Core packages, not third-party packages. At least, that's the theory, they can pretend that they're a core package and use it but that's a hack really and can break anytime if the core package in question start loading in the page.

@draganescu
Copy link
Contributor

Then the 1st thing that I can think of is to properly define the difference between opting into an API to participate into its formation vs relying on an unstable API.

@talldan
Copy link
Contributor

talldan commented Oct 18, 2024

A way to statically anyalyze the usage of the private APIs would also be good. For most of the experimental APIs we've resorted to using https://www.wpdirectory.net/ to search whether plugins are using an API, but it's not perfect, it can overmatch or be hard to find exact matches.

If there were a manifest or something that plugins had to use to say which private APIs they're using it'd be much easier to search for usage and even notify them when we're removing a private API.

Not sure how this would work, part of the problem is that an API can be so many things (function, prop, parameter, property etc ...) and there are often different techniques used for each.

@mtias mtias added the [Feature] Extensibility The ability to extend blocks or the editing experience label Oct 18, 2024
@ralucaStan
Copy link
Contributor

ralucaStan commented Oct 18, 2024

Introducing stage in the lifecycle of a new/experimental API would be good for consumers to understand its maturity.
Between the 2 stages, private and stable it’s hard to know where an API is currently positioned ( as in closer to which stage).

This change could also encourage community feedback, which is ultimately the enabler for stabilizing something.
It could also enable the maintainers to track better progress on APIs by observing how long it's been in a certain stage. This could prompt faster decision-making.

When it comes to usage of experimental APIs, it's clear that the risks are different depending on where the API is in its lifecycle. Using an API that is closer to becoming stable should in theory be less risky. What is a risk? Breaking changes or full removal of API.

I think this information about API maturity could speak more to developers about the type of involvement they could have and the risks/limits that come with it.

I agree coming up with these stages could be tricky, and there might be downsides to this approach, but it would allow for more flexibility and predictability then now.

Good changes from the status quo are to:

  • understand when an API is closer to becoming public; open it for early adopters and guarantee it's not going to be removed. Breaking changes can still happen (or not)
  • highlight the stages where the probability of the API removal is high
  • highlight when people can start experimenting and submitting feedback (I'm thinking Bits)
  • give another parameter outside of the API age and dissolve any confusion around an API's current state
  • reflect the team's progress/opinion in terms of API maturity;

@peterwilsoncc
Copy link
Contributor

I feel this need everyday when developing on Gutenberg. For a long term and sustainable project, a way to have a feedback loop for APIs, just like we do for features is needed. I know it's something that WordPress never had (aside from a small period where we had the experimental APIs in Gutenberg). I don't have a solution, but it's definitely a problem that is worth solving if we want to be shipping the right stable APIs.

@youknowriad I haven't been able to think of anything for WordPress Core but for sites running the Gutenberg plugin, it might be worth making the opt-in string static so site's using the APIs can safely do so while testing with the plugin.

I opt in for sites running the Gutenberg plugin and acknowledge these APIs may change without notice and are unavailable in WordPress Core

Extenders making use of it would need to do a little error catching during the transition period but it's a solvable problem.

@ramonjd
Copy link
Member

ramonjd commented Oct 20, 2024

Warning: left field idea incoming...

Could the experimental page be built out as a place to document/track/solicit feedback for experimental APIs?

It might be as feature-rich as the plugins page, with update/alert badges. Automation would be ideal, e.g., a script could scrape the JS code base for relevant data, which the page could publish, and some other flag in experimental PHP code (under /lib/experiments) or something.

🤷🏻

@jsnajdr
Copy link
Member Author

jsnajdr commented Oct 22, 2024

is your request to allow opt-in to be easier only when running the Gutenberg plugin, or do you wish to be able to opt in for sites running WordPress Core?

@peterwilsoncc I didn't realize that we could distinguish between running and not running the Gutenberg plugin when I was writing down this issue. And yes, it could be a very good solution.

If a plugin wants to use unstable APIs, it's typically for an experimental feature that can be turned on/off in plugin settings. For example, WooCommerce has a Settings/Advanced/Features screen where you can enable a beta product editor:

Image

A new additional constraint would be that this beta editor can be active only when the Gutenberg plugin is installed and active. Today the purpose of the Gutenberg plugin is to provide a bi-weekly "technology preview" of what is coming to Core in the next release. And now it could also provide a "beta environment" to plugins that also want to offer their own "technology preview" features. It all fits together very well.

If you want to ship a feature to the general public, it won't be able to use the experimental APIs. The APIs need to be stabilized in Core before they can be used. That's certainly a limitation, but not a bad one.

How would the API for opting into the private APIs look like? Currently Core modules can do this:

__dangerousOptInToUnstableAPIsOnlyForCoreModules( 'I acknowledge...', '@wordpress/blocks' );

where there is a consent string that serves as a sort of password, and the module also needs to specify its name. Every module name can be used only once, and there is an allowlist of them. That partially prevents non-Core modules from impersonating as a Core module.

For experimental API access from plugins we could have a second function:

__dangerousOptInToUnstableAPIs( 'I confirm...' );

This function implementation would check globalThis.IS_GUTENBERG_PLUGIN. It would throw an error if the constant is false, and check the consent string otherwise.

How does that sound? @ralucaStan @gigitux @lysyjan would it satisfy the requirements for Woo and MailPoet plugins?

It would be also nice to have the private APIs documented and sorted into various stages. That could be a well-maintained private-apis.md file in the repo which would document each API and describe how stable it is.

@youknowriad
Copy link
Contributor

@jsnajdr Just noting that we actually already do that. We already use globalThis.IS_GUTENBERG_PLUGIN to avoid shipping some experimental APIs to Core. An example that comes to my mind is the wp.editor.registerEntityAction API.

So I had assumed the discussion was more general, about Core as well.

Also, I do think there's value in improving our communication around the different stages of the different APIs.

@gigitux
Copy link
Contributor

gigitux commented Oct 22, 2024

If a plugin wants to use unstable APIs, it's typically for an experimental feature that can be turned on/off in plugin settings. For example, WooCommerce has a Settings/Advanced/Features screen where you can enable a beta product editor:

This is not always true. For example, currently, we're using some private APIs for the Customize Your Store project that it is in production and belongs to the Woo onboarding flow. I shared some private APIs that we're using:

From a quick search, it looks like that we're using 20 times unlock():

Image

We can do this because these packages aren’t imported directly from WordPress Core; instead, we use the npm version.

My concern with allowing plugin to opt-in to the private APIs is that WordPress would end up in a similar situation again. I worry that we'd end up in a situation in which WordPress couldn't unlock an API for risk of breaking sites (@adamziel worked on the code, so this may not be an issue -- I suggested it & try to track string changes each release).

While I fully agree with our commitment to maintaining stable APIs, I’m not sure why we should take responsibility for not breaking plugins that use private APIs. By definition, these APIs are experimental, and it is the responsibility of plugin developers to ensure their plugins remain compatible with new versions of WordPress. As a platform, we should make this “contract” clear, and we should certainly communicate the status of private APIs more effectively and work towards stabilizing those that have been in use for several years and are relied upon by multiple plugins.

@jsnajdr
Copy link
Member Author

jsnajdr commented Oct 22, 2024

we're using some private APIs for the Customize Your Store project that it is in production and belongs to the Woo onboarding flow.

If a production project needs private APIs, the first question is why these APIs are private and if it's still justified. There are many that could be stabilized right away.

Your first example uses the useGlobalSetting hook. This one was added 3 years ago in #35264 and it hasn't changed since. It has three parameters (path, block, source) and returns a state-like [ value, setValue] array. In Jan 2023 #47098 moved it from edit-site to block-editor, no other change ever happened. This hook and many other Global Styles APIs are a clear candidate for stabilization.

The second example uses the @wordpress/router package. What is special here is that:

  • the entire package is in fact private, it exports only private APIs
  • the Customize Your Store app could fully bundle it without losing anything, it doesn't need to be an externalized WP script
  • the app could also completely avoid using it: the @wordpress/router package is a thin wrapper around a public history package
  • the @wordpress/router package will probably soon disappear completely, after Gutenberg migrates Site Editor to one of the standard open source routers.

While I fully agree with our commitment to maintaining stable APIs, I’m not sure why we should take responsibility for not breaking plugins that use private APIs.

In my view the API stability policy is there also to protect the user from us engineers 🙂 If a plugin tries to use some API that's no longer there or is different, the user's site will break and the user will be harmed. Then various groups of engineers can argue with each other about whose fault it is and who is responsible, but that doesn't help the user much -- their site is broken.

@jsnajdr
Copy link
Member Author

jsnajdr commented Oct 23, 2024

We already use globalThis.IS_GUTENBERG_PLUGIN to avoid shipping some experimental APIs to Core. An example that comes to my mind is the wp.editor.registerEntityAction API.

Yes, maybe we don't really need much more than that. In its current form, wp.editor.registerEntityAction is a public function that behaves as a noop outside the Gutenberg plugin. Technically, we made a commitment to keep the function there so that a wp.editor.registerEntityAction() call doesn't crash.

If I make an analogy with public/protected/private modifiers in PHP or C++, we want to introduce "protected" APIs that can be used more widely than "private" ones, namely by plugins. Then registerEntityAction would no longer be public, but it would be a protected API that a plugin explicitly needs to opt-in into.

So I had assumed the discussion was more general, about Core as well.

We can discuss also Core and access to private APIs without the Gutenberg plugin active. It's just that we don't have any nice solution for that yet, and also it's not clear who needs such a Core access and what the requirements are.

@youknowriad
Copy link
Contributor

We can discuss also Core and access to private APIs without the Gutenberg plugin active. It's just that we don't have any nice solution for that yet, and also it's not clear who needs such a Core access and what the requirements are.

For me the need is that we need a better feedback loop with extenders and Extenders can give us that feedback unless they ship their plugins to real users without the need for an additional Gutenberg plugin forced onto their users.

It's true that we've managed so far to have something "working" without it but it's far from ideal.

@joshuatf
Copy link
Contributor

It seems like we're discussing two things in this issue:

  1. How we can safely expose private APIs to third party plugins.
  2. How we can get feedback on those APIs so that they can eventually become stable.

On the 1st point, as I understand it Gutenberg could remove or introduce a breaking change to a private API at any point in time (please correct me if I'm wrong here). This makes it unsafe for plugins like WooCommerce to consume these APIs and subsequently difficult to accomplish point 2 for Gutenberg to get feedback on these APIs.

It might also be dangerous relying on the Gutenberg plugin being active as a means to expose private APIs. This seems like it's introducing a dependency that can't be fully controlled by the consuming plugin. For example, one plugin might require version X and the other requires version Y, with version X having the private API that plugin A needs, while breaking changes are made in version Y.

Using NPM would be a simple solution that would allow plugins to pin versions with the private APIs without introducing breaking changes in upcoming versions. However, this eliminates the benefits of exposing packages on the wp global and is costly in terms of performance if multiple plugins are all pinning different versions of packages in order to guarantee that a certain API is in place.

I wonder if there's an opportunity to create a minimal package that houses the private APIs and could be pinned by consumers. However, in practice I think there might be too many interdependencies for those private APIs, creating the same performance issues we were trying to avoid.

@adamziel
Copy link
Contributor

For me the need is that we need a better feedback loop with extenders and Extenders can give us that feedback unless they ship their plugins to real users without the need for an additional Gutenberg plugin forced onto their users.

I wanted to echo my comment from another thread. The consent string seems like an obvious thing to look at, but I think it's just a symptom of a deeper challenge with the dependency graph.

Here's my understanding of the situation:

  • @wordpress/dataviews are meant for Core once the API matures
  • Today it's shipped with the Gutenberg plugin and as an npm package
  • Part of maturing the API is getting feedback from the developers using dataviews in their projects
  • Expecting every dataviews-reliant plugin to depend on a specific version of Gutenberg is a big ask
  • Therefore, developers consume dataviews through npm, and with it they bring in all the npm dependencies, such as @wordpress/data
  • These plugins are built with @wordpress/scripts, which replaces the npm version of @wordpress/data with wp.data, but it cannot do the same for dataviews because there's no core version of dataviews

Does that sound right? If yes, that last steps effectively replaces the dependency graph used by @wordpress/dataviews. The assumption seems to be "I'm sure window.wp is compatible with all possible versions of this dependency". Is that assumption correct? Aside of the private APIs, that is.

One solution would be for all the dataviews-reliant plugins to require the Gutenberg plugin after all. Then, instead of bundling the package we could perform the substitution to window.wp.dataViews and keep a coherent dependency graph.

@mtias
Copy link
Member

mtias commented Oct 24, 2024

The idea of using the presence of the Gutenberg plugin to expose unstable APIs so plugins can get ahead, try, provide feedback, etc, is how we've been thinking about it already. Gutenberg has become, as a matter of fact, a channel for early testing of both user and developer features. Plugins that have a dependency on Gutenberg would be well aware of it and should help promote APIs that become solid to be part of Core releases.

I also agree with @youknowriad that it'd be good to formalize the state of some of these, whether that is through "stages" or something else.

@jsnajdr
Copy link
Member Author

jsnajdr commented Oct 24, 2024

@adamziel The @wordpress/dataviews package is special because it's not a part of the WordPress platform. There is no wp-dataviews script you can enqueue, no wp.dataviews global that needs to have a reliable contract with consumer. It's just another NPM package you can use and bundle in your project. It has no WordPress compatibility constraints.

The only problem with @wordpress/dataviews is that it uses private APIs from @wordpress/components, which is a part of the platform. That creates its own set of issues as discussed in #63657.

This discussion is mainly about packages that i) are part of the platform, and ii) expose private experimental APIs that often change. If plugins could use these private APIs freely, an upgrade from WordPress 6.6 to 6.7 would likely break many sites.

@joshuatf
Copy link
Contributor

@youknowriad @mtias I have 2 concerns with requiring the Gutenberg plugin to be active to use these newer APIs:

  1. It requires another plugin install (Gutenberg) for some features that are currently shipped as enabled with WooCommerce core.

I think this point might signal that the underlying API should be marked as stable, but I'm not sure that a single plugin (Woo) can make that decision. Even once that decision is made, it means that we need to always install Gutenberg with at least the next 2 WP versions.

  1. If the private APIs can be removed or changed at any time, what happens when one plugin is using the APIs from Gutenberg version A while another plugin is using APIs from version B? Or what happens when plugins get auto-updated and the API is no longer available?

This seems like it might wreak havoc on consuming plugins relying on an API being present in a specific version of Gutenberg.

@adamziel
Copy link
Contributor

adamziel commented Oct 24, 2024

I think this:

The only problem with @wordpress/dataviews is that it uses private APIs from @wordpress/components, which is a part of the platform.

contradicts this:

It has no WordPress compatibility constraints.

@jsnajdr isn't that an implicit dependency on a specific WordPress / Gutenberg version?

@peterwilsoncc
Copy link
Contributor

The idea of using the presence of the Gutenberg plugin to expose unstable APIs so plugins can get ahead, try, provide feedback, etc, is how we've been thinking about it already. Gutenberg has become, as a matter of fact, a channel for early testing of both user and developer features. Plugins that have a dependency on Gutenberg would be well aware of it and should help promote APIs that become solid to be part of Core releases.

I couldn't agree more with this. Making it easier to opt-in when running Gutenberg (via a fixed string or some other means) would help make this feedback loop easier.

I have 2 concerns with requiring the Gutenberg plugin to be active to use these newer APIs:

1. It requires another plugin install (Gutenberg) for some features that are currently shipped as enabled with WooCommerce core.

I think this point might signal that the underlying API should be marked as stable, but I'm not sure that a single plugin (Woo) can make that decision. Even once that decision is made, it means that we need to always install Gutenberg with at least the next 2 WP versions.

2. If the private APIs can be removed or changed at any time, what happens when one plugin is using the APIs from Gutenberg version A while another plugin is using APIs from version B?  Or what happens when plugins get auto-updated and the API is no longer available?

This seems like it might wreak havoc on consuming plugins relying on an API being present in a specific version of Gutenberg.

The private APIs are, by definition, not ready for use in production. If a theme or plugin chooses to __dangerousOptInToUnstableAPIsOnlyForCoreModules() for production code, then it's up to the developer of the third-party code to manage the fact that that private API can be removed or changed at any time.

I do accept that sometime we (being WordPress Core developers) could sometime mark APIs stable more quickly but it needs to be done through an issue to allow for folks to discuss whether the API is, in fact, in good shape to be marked stable.

I DO want to make it easier for third party developers to assist with the stabalization of the APIs and making it easier for devs to use the APIs in non-production code will help open the feedback loop. What I don't want to do is end up back were we started of experimental APIs been used so widely in production code that WordPress Core gets stuck with the first or second draft of the API by making them available without running the Gutenberg plugin.

@joshuatf
Copy link
Contributor

The private APIs are, by definition, not ready for use in production. If a theme or plugin chooses to __dangerousOptInToUnstableAPIsOnlyForCoreModules() for production code, then it's up to the developer of the third-party code to manage the fact that that private API can be removed or changed at any time.

@peterwilsoncc I understand the conundrum and why we're moving this direction. However, if we're wanting to get early feedback on these APIs, I think this method might present some challenges to plugins.

As a 3PD, needing to install an additional plugin for a feature to work is less than ideal, but not a deal breaker. Needing to install a plugin for a feature to work AND not knowing if the (latest) installed version will even house the private APIs that allow said feature to work is a dealbreaker.

Unless I'm misunderstanding something, without a way to pin a specific version of the plugin (i.e., with the required APIs), there's not a way to guarantee that a feature is supported at any given time. I know that WooCommerce will not be able to reliably use or provide feedback on any of these APIs if that's the case.

@joshuatf
Copy link
Contributor

One more question I have if we opt for the plugin approach is how we safe guard against changes in a consumer plugin.

Do we need to have strict version checks to make sure the functions work as expected in the event a function is removed or signature changes?

if ( GUTENBERG_VERSION === '19.3.0' ) {
    doPrivateApiThing();
}

@jsnajdr
Copy link
Member Author

jsnajdr commented Oct 29, 2024

isn't that an implicit dependency on a specific WordPress / Gutenberg version?

@adamziel The contradiction can be resolved by distinguishing between:

  • the API @wordpress/dataviews exposes to its clients. This API is not exposed in WordPress in any way, it's the API of an NPM package, and is it governed only by semver rules. The WordPress backward compatibility policy doesn't apply here.
  • what APIs of the WordPress platform the @wordpress/dataviews package uses under the hood. If it's using a private component exported from @wordpress/components, that places a hard constraint on dataviews consumers: if the current policy proposal goes through, the Gutenberg plugin needs to be active for any dataviews-using plugin.

@jsnajdr
Copy link
Member Author

jsnajdr commented Oct 29, 2024

@joshuatf I can offer two arguments that could address your concerns:

The private APIs wouldn't change or disappear abruptly and randomly. The changes can be discussed, prepared for, you should be able to detect whether you're dealing with the old or new version. The point is that for stable APIs, we guarantee that plugin authors don't need to do any extra work. Once a stable API is there, WordPress guarantees that it will continue to work exactly the same way, practically forever. But unstable API is not the exact opposite of that (random chaos) but rather that the plugin developer is expected to keep up with the latest changes and update their code.

Second, in the future we should have only very few permanently private APIs, if any. They should get stable in a reasonable amount of time. If a plugin succesfully implements a production-ready feature using a private API, that's a strong stabilizing force that creates pressure on Core to stabilize that API, so that plugins can ship a stable version of the feature to general public. That's one thing I hope that this proposal achieves: that the private status will always be only temporary, because we'll have a good process how to get new APIs widely used, get feedback, and improve them quickly.

@jsnajdr
Copy link
Member Author

jsnajdr commented Oct 29, 2024

It would be also nice to have the private APIs documented and sorted into various stages. That could be a well-maintained private-apis.md file in the repo which would document each API and describe how stable it is.

I started working on the private API docs in #66558.

@psealock
Copy link
Contributor

psealock commented Oct 29, 2024

Requiring the presence of Gutenberg plugin to expose Private APIs is a good solution that doesn't work for the business demands of a plugin like WooCommerce whose features are often shipped under deadlines imposed either internally or by contractual obligations.

The ideal scenario would be to rely on semver via NPM, but as mentioned above by @joshuatf, this is costly on performance. What if there is a solution that offers the security of semver but forces developers to be respectful of changing APIs?

What if the opt-in string was per package and required pinning to a major version of that package?

__dangerousOptInToUnstableAPIsOnlyForCoreModules(
	'I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.',
	'@wordpress/router',
	'1.10.0^',
	'woocommerce'
);
  • Gutenberg private packages can change APIs and bump major versions without fear of breaking plugins.
  • Plugin developers are aware that a new version of WP and its packages must be opted into. This forces awareness of potential changes and requires development effort to keep using APIs.
  • Using private APIs in production is now possible, but incurs an inherent cost that prohibits "set and forget" type development, encouraging devs to participate in stabilizing APIs.
  • Unmatched versions in the opt-in string could default to the requirement of Gutenberg plugin.

@jsnajdr
Copy link
Member Author

jsnajdr commented Oct 31, 2024

doesn't work for the business demands of a plugin like WooCommerce whose features are often shipped under deadlines imposed either internally or by contractual obligations.

This seems to be a project management issue about priorities. If you have a contractual obligation to deliver something by a certain date, then obviously you want to implement that with stable APIs, and you cannot spend time trying out experimental APIs.

For stabilizing Core APIs, the overwhelming constraint is "quality": we need to be sure that the API is right and that we can commit to it long-term. Deadlines play a very small role in that process. If another project's major constraint is "time", that leads to problems. It's a classic project management "triangle" where a project is constrained by resources, time, scope and quality and you can improve one metric only by compromising on others.

What if the opt-in string was per package and required pinning to a major version of that package?

If I understand it correctly, this means that the opt-in call can fail. It can return an error saying either that the Gutenberg plugin is required and you don't have it, or that you're requesting a version of the package that is not available on this particular WordPress installation.

Then you'd have to implement some fallback using stable APIs. Having two versions of a feature, one stable and conservative, and the other is experimental and optional.

@psealock
Copy link
Contributor

psealock commented Nov 8, 2024

Thanks @jsnajdr

Deadlines play a very small role in that process.

Thats a good point. For the sake of the discussion best to avoid that perspective.

If I understand it correctly, this means that the opt-in call can fail.

Yes, thats exactly right. The failure is by design what would keep extension developers honest about using an unstable API. WooCommerce, for example, has an L-1 support policy so that only the current latest WP and the previous versions are supported. In this scenario lets say a Private API gets a major version bump, requiring all opted-in consumers to update their consent strings otherwise fatal errors will occur. WooCommerce is now greatly incentivised to proactively address the update, determine what code changes are required for the updated API, and finally handle the situation to avoid breakage. This would be a regular part of the development process and provide enough friction to dissuade lazy usage of private APIs.

We'd need to gracefully handle failed consent in the UI by showing a message to update WooCommerce. Unit testing with pre-release versions of Core WP can offer enough time to address issues. Also, new WooCommerce features based on unstable APIs are often hidden behind feature flags where merchants can opt in to the new experience. This allows for early feedback, which affects usage of unstable APIs and ultimately provides feedback to those APIs and enhancing stability.

@costasovo
Copy link
Contributor

@jsnajdr I'm very grateful for this discussion. Thank you for starting it! The idea of stages is great, and having information about how close to stabilization an API is would help us avoid touching fresh or temporarily unstable APIs and encourage us to experiment with the APIs that are close to becoming stable.

In MailPoet, we are working on the email editor, and we use a couple of private APIs. The editor is currently behind a feature flag, but we are working towards preparing it for a release. There is no hard deadline, but we would like to release it in the next couple of months. So, there is a motivation to help stabilize the APIs we use.

That's one thing I hope that this proposal achieves: that the private status will always be only temporary because we'll have a good process for how to get new APIs widely used, get feedback, and improve them quickly.

The stages will give us a clue about the stability of an API, but what about the process for stabilization? As I mentioned, we use private APIs, and we have the motivation to help stabilize them. What are the steps we should take to provide feedback and ask for stabilization? For example, we use ExperimantalBlockCanvas because we need one prop that is not available on the stable BlockCanvas. Shall we open an issue where we describe our use-case and ask for stabilization? Or do you imagine a different process for sharing feedback about the usage of an unstable API?

As for private APIs, they are available only when Gutenberg is active. I'm thinking about a situation when a private API becomes stable, and it may take a couple of months until it makes it to WP Core. I think it is a bit of a shame because if we know the API is stable and the private version in WP Core is the same, I think we could release the feature to users earlier.

@jsnajdr
Copy link
Member Author

jsnajdr commented Nov 13, 2024

we use ExperimantalBlockCanvas because we need one prop that is not available on the stable BlockCanvas. Shall we open an issue where we describe our use-case and ask for stabilization?

Yes, in this case open an issue or even a PR that adds the prop to BlockCanvas. I guess you need contentRef? That should be easy to stabilize, it provides a ref to the content element which anyone can get anyway using the .editor-styles-wrapper selector. There are also hooks like useFlashEditableBlocks that are private and could be either made public, or called inside BlockCanvas without having to expose them. This was also discussed in #57672, namely in #57672 (comment) where @youknowriad says that he would like to make contentRef and other props not needed at all. Let's discuss how to get there. The #57672 issue is about the Isolated Block Editor, I guess the MailPoet editor has a lot in common with that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Extensibility The ability to extend blocks or the editing experience [Package] Private APIs /packages/private-apis [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

No branches or pull requests