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

Developer Experience: There's ~280 __experimental APIs. Let's stabilize them! #40316

Closed
adamziel opened this issue Apr 13, 2022 · 53 comments
Closed
Labels
Developer Experience Ideas about improving block and theme developer experience [Type] Code Quality Issues or PRs that relate to code quality

Comments

@adamziel
Copy link
Contributor

adamziel commented Apr 13, 2022

The problem

Rob's script detectes the following number of __experimental APIs:

  • Gutenberg 5.0 has 81 __experimental APIs
  • Gutenberg 6.0 has 113 __experimental APIs
  • Gutenberg 7.0 has 173 __experimental APIs
  • Gutenberg 8.0 has 224 __experimental APIs
  • Gutenberg 9.0 has 300 __experimental APIs
  • Gutenberg 10.0 has 337 __experimental APIs
  • Gutenberg 11.0 has 442 __experimental APIs
  • Gutenberg 12.0 has 562 __experimental APIs
  • Gutenberg 13.0 has 614 __experimental APIs

It is a generous estimate and, as @talldan pointed, the actual number is 280 as of Gutenberg 13.0. Still, the progression above demonstrates the problem.

The Audit Experimental APIs WP 6.0 issue I've created for WordPress 6.0 initially exceeded GitHub's characters limit:

And that's just the __experimental ones! There are also __unstable, __internal, and probably other prefixes..

So what?

Plugin and theme authors are forced to rely on the __experimental features that could get removed or changed in a backwards incompatible way at any time. It is a serious maintenance burden. Every new Gutenberg/WordPress release means potentially breaking changes.

Also, shipping essential features as experimental sends the wrong message to the developers: With all these __experimental APIs around, adding another one surely can't be a big deal. What does "experimental" even mean at this point? What is a valid use case, and what isn't?

The discussion

Let's find a way to harness this growth!

There are two parts of this:

  • Stabilize the existing __experimental APIs
  • Create less new __experimental APIs

Stabilize the existing __experimental APIs

Many are de-facto stable, we just never marked them as such.

One idea to get there would be to:

  • Automatically create one PR per __experimental API to stabilize it
  • Ping the original author and the reviewers
  • Perhaps even publish a call for stabilization on make.wordpress.org
  • Let it simmer for a few weeks

Some PRs would get closed, some ignored, and others merged. Once that happens, we regroup and figure it out from there.

Upsides:

  • Each __experimental API would get feedback (even silence provides information)
  • Many __experimental APIs would likely get promoted

Downsides:

  • A lot of noise
  • Some __experimental APIs might be intertwined and would have to be promoted as a bundle

To me, that's a worthy trade-off. Is it to you? What other ways do you see?

Side note: Plugins relying on these "permanently __experimental" names would break – these names would have to keep working with a deprecation notice.

Create less new __experimental APIs

The number will continue to grow until something slows it down.

It could be a project policy, but policies need enforcement. It is hard, creates extra work, and may feel discouraging on the receiving end.

How about a soft nudge? Perhaps a CI check or a bot that detects new __experimental APIs and tries to educate the PR author? Ideally, it would also propose an alternative.

cc @gziolo @Mamaduka @noisysocks @mtias @youknowriad @andronocean @ciampo @aaronrobertshaw @c4rl0sbr4v0 @jorgefilipecosta @mcfs @paaljoachim @scruffian @draganescu @getdave @talldan @oandregal @ntsekouras @ellatrix @ndiego @priethor @annezazu @dmsnell

@adamziel adamziel added [Type] Code Quality Issues or PRs that relate to code quality Developer Experience Ideas about improving block and theme developer experience labels Apr 13, 2022
@adamziel adamziel changed the title There's 614 __experimental APIs. Let's stabilize them! There's 614 __experimental APIs to worry about. Let's stabilize them! Apr 13, 2022
@adamziel adamziel changed the title There's 614 __experimental APIs to worry about. Let's stabilize them! There's 614 __experimental APIs. Let's stabilize them! Apr 13, 2022
@gziolo gziolo changed the title There's 614 __experimental APIs. Let's stabilize them! Developer Experience: There's 614 __experimental APIs. Let's stabilize them! Apr 13, 2022
@gziolo
Copy link
Member

gziolo commented Apr 13, 2022

Automatically create one PR per __experimental API to stabilize it

Interesting idea. What possibly could go wrong? 😃

I think the biggest concern here would be the documentation which might be missing in many cases. The automated PR could serve as s good way to find out how many undocumented APIs we have. I bet that the lack of good explanation of how to use those APIs is the biggest issue because, at this time, extenders probably learned how to work with experimental prefix 😅

@talldan
Copy link
Contributor

talldan commented Apr 14, 2022

Gutenberg 13.0 has 614 __experimental APIs

The numbers aren't quite right as that script lists many duplicates. It shows any existence of __experimental. It doesn't only list where those experimental APIs are defined, also where they're used.

Doing some quick sorting and removing duplicates gives a number around 280. It might be a little higher because there are probably some APIs that are actually defined in multiple places with the same name.

Still quite high. I think the tricky thing is prioritizing the ones that are actually useful to developers.

The age of an experimental API is also an interesting measurement. If an API is going to sit around for years as experimental, perhaps it'd be better being switched to __internal.

edit: sorry for closing and reopening accidentally.

@talldan talldan closed this as completed Apr 14, 2022
@talldan talldan reopened this Apr 14, 2022
@paaljoachim
Copy link
Contributor

paaljoachim commented Apr 14, 2022

Some brain storming...
It seems this topic needs a wider discussion. If it has not been brought up earlier in the core editor meeting, it could be brought up there. Posting a comment about this to the agenda so people will notice that it will be discussed during the meeting and then spending part of the Open Floor having a wider discussion.

Another thought is to focus on stabilizing experimental APIs for a 6.x version where main focus is stabilization. It would then need to be posted on make core well ahead of time to get developers ready for the shift from experimentation to stabilization.

@dmsnell
Copy link
Contributor

dmsnell commented Apr 18, 2022

when I recently added __unstableBlockSource I did so because there wasn't clear guidance on intentionally-internal APIs. had there been, and had there been documentation, I would have used __internal.

notable we don't list __internal as an official prefix; if we want to draw a distinction there between __unstable and __internal we should consider making that decision and documenting it first.

@mtias
Copy link
Member

mtias commented Apr 18, 2022

Can we approach this in a more gradual manner from oldest to newest? Doesn't make sense to treat APIs introduced in the last couple releases the same as those introduced a couple years ago. APIs that are already part of major releases should be prioritized.

Many are de-facto stable, we just never marked them as such.

Which ones? Identifying those seems like a good start to me.

@noisysocks
Copy link
Member

Getting the age of these tokens would be useful. Could probably hack my script to do this using git log --oneline -S $TOKEN_NAME. I'd say anything that's been in the codebase for more than 12 months should be marked stable or __internal.

@adamziel adamziel changed the title Developer Experience: There's 614 __experimental APIs. Let's stabilize them! Developer Experience: There's ~280 __experimental APIs. Let's stabilize them! Apr 26, 2022
@peterwilsoncc
Copy link
Contributor

To share my thoughts from Slack in a less ephemeral form.

WordPress Core is considered the stable version of the block editor. It has a long standing commitment. to maintaining backward compatibility of public APIs. Any of the __ prefixed items that have been exported from packages in WP Core will need to be maintained.

Discussing how the block editor is used by developers, in order to provide a native feeling experience plugins are required to use the private functions. This is reflected in a rudimentary search of the plugins directory and themes directory.

Maintaining deprecated private APIs has been a long standing practice of WordPress, as reflected in deprecated.php.

Basically, hard deprecating in WordPress Core is a no-go. Instability between versions is already beginning to alienate some of the block-editors biggest external advocates so a concerted effort of deprecation would simply expedite the alienation.

As Gutenberg is considered a bleeding edge product, hard deprecation is acceptable. I'd suggest a soft deprecation of a short period of time (three to six months) before hard deprecation. If it doesn't already include it, adding the hard deprecation version number to soft deprecation console messages would be ideal.

@adamziel
Copy link
Contributor Author

adamziel commented May 4, 2022

All great notes @peterwilsoncc! It's an interesting situation:

  • We have APIs are called __experimental so they can be hard-deprecated
  • Some of them made it to WordPress Core as public APIs so they cannot be hard-deprecated anymore

In other words, releasing an __experimental API as a public API in WordPress Core makes it de-facto not experimental. It's just the codebase doesn't reflect that reality. In this case, I'd like to make sure we either remove __experimental prefixes from new public APIs or turn these public and __experimental APIs into private ones before each WordPress core release.

@adamziel
Copy link
Contributor Author

adamziel commented May 5, 2022

Actually, that doesn't seem to be possible. At least these two kinds of APIs cannot be made private before a major release:

  • React properties
  • experimental APIs exposed by package A to be used in package B

There must be a way to ship them and retain the ability to remove them. If anything that's made public cannot be easily removed, there's no point in maintaining Gutenberg as a separate repository. I now err on the side of keeping the ability to hard-deprecate experimental APIs even if they made it as public to WordPress core.

This is consistent with the following handbook page:

https://developer.wordpress.org/block-editor/contributors/code/coding-guidelines/#experimental-and-unstable-apis

We could, though, make sure that the __experimental prefix keeps working for some time after stabilizing the underlying API (cc @gziolo @dmsnell).

@Clorith
Copy link
Member

Clorith commented May 5, 2022

I feel like @peterwilsoncc is much better at I than articulating here, but it should be noted that the handbook page you are referencing is the contributor guidelines for the Gutenberg plugin, and not the implementor guidelines for developers using what actually lands in core.

If the worry is that experimental features are needed for a feature to be put into core, would it then not stand to reason that that feature is not actually ready for core yet, because it is in part experimental? (I'm being intentionally direct here, I know there are nuances, but would this not improve developer experiences as well if not even core relied on experimental or unstable components?)

@noisysocks
Copy link
Member

I believe that the intention of __experimental and __unstable was to mark an API as subject to breaking without warning regardless of whether that API appears in Core or Gutenberg.

I know Core has historically maintained backwards compatibility for APIs marked private but I'd push back on that precedent a little bit as the majority of this has been for APIs written in PHP. Keeping PHP around for backwards compatibility carries a maintenance burden but not much else. Keeping JavaScript around for backwards compatibility, however, carries a maintenance burden and a user experience burden as it increases the bundle size.

@talldan
Copy link
Contributor

talldan commented May 9, 2022

If the worry is that experimental features are needed for a feature to be put into core, would it then not stand to reason that that feature is not actually ready for core yet, because it is in part experimental?

I think it's worth dividing it up into two things—the user facing feature and the developer API. The user facing feature is usually ready for release, but developer APIs still somewhat open to change. It's often possible to change developer APIs with no effect on the user experience.

On the other hand 'ready' is a relative term. User interfaces do naturally change over time. It can be difficult to do that if the underlying APIs all require an endless commitment to backwards compatibility, and I think that's the main reason to consider something experimental.

@peterwilsoncc
Copy link
Contributor

I believe that the intention of __experimental and __unstable was to mark an API as subject to breaking without warning regardless of whether that API appears in Core or Gutenberg.

I accept that was the intention but once these APIs started shipping in the stable product the promise to maintain backward compatibility was made. These APIs have only ever been documented as removed without notice in the plugin contributor guide.

  • __experimentalGetSettings is used on 11 million plugin installs, the only way to get the sites date format
  • __experimentalLinkControl is used on 11 million plugin installs, as Dave has covered elsewhere. It's the only way to introduce native equivalent UIs.
  • __experimentalNumberControl Six million odd plugin installs,
  • __experimentalRadio 500K plugin installs (excluding gutenberg)
  • __experimentalRadioGroup 500K plugin installs (excluding Gutenberg)

This is before considering themes and custom builds in which use these items too. I am getting DMs since joining this ticket.

Yup, there's a maintenance burden to maintain backward compatibility. That's the cost of working on a project that strives to maintain it.

Yup, shipping these APIs in the stable product has introduced a user experience burden too. Realistically, the APIs should not have been shipped in an unstable/experimental form.

@noisysocks
Copy link
Member

The current policy is that __experimental and __unstable APIs have no backwards compatibility guarantee.

https://github.com/WordPress/gutenberg/blob/336748b39cc1e079806c87c2b8f30ed32d0ebb26/docs/contributors/code/coding-guidelines.md#experimental-and-unstable-apis

We don't really have a way of picking and choosing which code lands from Gutenberg into Core which is a large part of why __experimental and __unstable were introduced.

I would prefer that we improve how we communicate what our policy is for __experimental and __unstable APIs rather than change what that policy is. For example, maybe we can automatically output a console.warn() message if any client code calls an experimental API.

For experimental APIs that are very frequently used (e.g. the ones you list) I think it makes sense to be pragmatic and mark those APIs as deprecated for a few releases. Not guaranteeing backwards compatibility doesn't mean that we can't do it anyway.

But I disagree completely that any __experimental or __unstable API becomes de facto stable once in Core. The team has held the exact opposite assumption for quite some time.

@mtias
Copy link
Member

mtias commented May 9, 2022

I think there can be a stronger commitment to not include __experimental and __unstable APIs in major releases, though as mentioned by @noisysocks that won't always be realistic. We should take those instances as practical occasions to force stabilization as much as possible. In any case, these APIs don't have a commitment to remain as is because they were designed to be changed (at the very least, to drop the prefix at some point) and there's a shared responsibility between the core project and extenders in how they interact with them.

deprecated.php is not really relevant as a comparison since server-side PHP deprecations don't burden users and visitors with degraded performance. It's not realistic for the project to bundle JS functions forever without some form of "present only if needed" mechanism, which is not entirely realistic as of this time. There's probably a need for a wider policy discussion there that balances expectations and pragmatism.

I also see two things at play here: the use and abuse of experimental APIs during the API design (generally to be used and tested in the Gutenberg plugin) and the lack of a diligent process for stabilizing them when they satisfy design criteria. The ones that are to be considered de facto public are those that have existed for many releases in a stable form despite their nomenclature.

@peterwilsoncc
Copy link
Contributor

peterwilsoncc commented May 9, 2022

@m @josephahaden Has WordPress dropped the core philosphy to maintain backward compatibility? Is the project now willing to break millions of plugin installs per my comment above?

@mtias
Copy link
Member

mtias commented May 10, 2022

@peterwilsoncc pardon me, but that seems quite the leap. Where do you see the argument to break millions of plugin installs? There's a nuanced discussion to be had when it comes to how much code we are willing to ship to users on the client in the name of keeping APIs working that's collateral to the discussion of how experimental APIs are introduced, how they are refined, how they are stabilized, and what it means when these end up in major releases. Among those, there are some very concrete things to pursue that's what we should focus effort on.

@mtias
Copy link
Member

mtias commented May 10, 2022

I'll try to summarize what I see as more concrete items.

  • Indefinitely increasing client-side code is at odds with performance budgets, we need to be cognizant of this reality. We might have more room when async loading and native imports can be leveraged.
  • When designing APIs, feedback from real usage is crucial. WordPress beta periods are not long enough or timely enough to gather wide feedback. The bleeding edge Gutenberg plugin is more appropriate for it.
  • Unstable APIs that leak need to be looked at explicitly at the time of major releases for stability as the commitment is orders of magnitude higher.
  • Current experimental APIs bundled in core need to be addressed on a case by case basis with an aim to stabilize, probably from oldest to newest.
  • Mistakes will be made and we need to design for iteration and change while not breaking things. This will always be challenging at heart and need ongoing ingenuity.

@ndiego
Copy link
Member

ndiego commented May 10, 2022

As a plugin author that is currently using many __experimental APIs, I would love to see these stabilized. Most provide crucial functionality but building a product that relies on an __experimental API is always a bit disconcerting. So long as the process is exceedingly transparent, is well publicized, and we provide plugin/theme authors with a guide on how to migrate to stable versions, then I like this initiative.

@dmsnell
Copy link
Contributor

dmsnell commented May 10, 2022

and a user experience burden

in my opinion crashing the editor or a block is a much bigger user experience burden than adding a few extra tens of gzip'd bytes which is what the cost is going to amount to by leaving in deprecation notices on the old function names.

indefinitely increasing client-side code

this also feels like quite the leap @mtias. we're discussing ~280 functions and a rename of those functions.

when we mark code as deprecated we don't really leave clues in the source itself on when to pull them out, but I wonder about this, if we decided a deprecation policy of six months or so and leave some kind of removeAfter property to make it more obvious to someone passing by

export const ButtonBlockerAppender = forwardRef( ( props, ref ) => {
	deprecated( `wp.blockEditor.ButtonBlockerAppender`, {
		alternative: 'wp.blockEditor.ButtonBlockAppender',
		since: '5.9',
		removeAfter: '2022/01/01'
	} );

	return ButtonBlockAppender( props, ref );
} );

it just seems that overall a few months transition window (or one in which we can monitor usage in real plugins) that adds almost no cost to the generated build but prevents the editor from crashing (or a block from crashing) is an obvious choice to make.

maybe we can automatically output a console.warn() message

Just a reminder @noisysocks that console usage in Gutenberg is really frustrating to deal with outside of WordPress. I know we don't have a better solution in the project right now, but I'll at least encourage everyone to avoid using console as much as possible since it doesn't play well with others.

@Clorith
Copy link
Member

Clorith commented May 24, 2022

My intent was not to irritate, but to flip it around; You're saying that these experimental features are needed to making other components in the block editor work, so then we need to look at it from the position of someone trying to extend the editor, and understand that they are also bound by that same need.

So if we did set a hard block on experimental functions for core moving forward, we would then add a bit more work, because related features would also need to be stabilized before a feature lands. I don't see this as a bad thing necessarily, as it would help ensure that what lands in core is something everyone can rely on as an implementation that can be used, extended, or whatever else one might want to do with it.

Do I have a perfect solution, probably not, I'm just trying to spark a discussion and make sure that we move in the right direction :)

@dmsnell
Copy link
Contributor

dmsnell commented May 24, 2022

Adding a note to call for a bit of pragmatism here: we're discussing a lot of ideological values and I get the sense that those risk leading us astray.

In summary of some of the comments above, we don't have to be discussing if developers have lost trust in the API stability because we're specifically talking about a move that does break that trust. In other words, we're talking about removing code that we know many plugins depend on because we can see that in their source code.

Good or bad we expose these APIs and people depend on them and to that matter it's we as the project that haven't adhered to the policy, and not through active intention. It's just how software works that any element that gets exposed becomes part of the public API.

I'm in favor of stabilizing experimental APIs. I'm not in favor of needlessly breaking code that people have come to depend upon. It's not considerate; it's not gaining us anything other than a few bytes that are insignificant in comparison to other changes in bundle sizes.

How long is too long to leave deprecations in place? I don't know, but I'd rather risk leaving them in a little too long while we can still see that a large part of the community is using them. "You shouldn't have done it that way" is never going to be consoling to someone who suddenly starts fielding support calls from people whose content is broken after a WP update.

So for the sake of this issue, I frame it on this question: would you rather suddenly break a lot of code that people have used that we didn't want them to but which we gave them in order to maintain a philosophical purity about a policy we kind of made or would we rather prefer slowing down transitions to communicate to people that we have already slated for certain hooks and functions to be removed and they should use X instead?

Again, there's almost no cost associated to stabilizing without breaking existing code and we win trust and favor. Why are we arguing about intentionally breaking integrations we know are out there and in use? Why would we rather have the editor crash when someone's just trying to write their content than to have an experimental API stick around as a deprecation for a little while longer?

@adamziel
Copy link
Contributor Author

adamziel commented May 25, 2022

My intent was not to irritate, but to flip it around;

👍 thanks for clarifying! You're making some good points, it's just a very hot topic and I found that surfacing and labeling the emotions often makes it easier to talk.

You're saying that these experimental features are needed to making other components in the block editor work, so then we need to look at it from the position of someone trying to extend the editor, and understand that they are also bound by that same need.

@Clorith Are you talking about the stale, long-present __experimental APIs? I do agree. Are you talking about the fresh ones that are actually internal functions that happened to be exposed in public API because of technical limitations of JS? In this case, I disagree. Bear with me, I'll address that in details later in this comment.

That's the culprit I think – we're having many different discussions at the same time here:

  • What was actually agreed upon with regard to __experimental in the past?
  • Does that decision still make sense?
  • What to do with the existing __experimental APIs?
  • How to deal with the future __experimental APIs?
  • How can we all build an extensible editor?
  • Is it okay to internalize the __experimental APIs without giving contributors access to them?
  • ...probably a few more

No wonder it's confusing and conflicting – that's such a huge discussion scope! I didn't really expect it will go in this direction. I'm not sure whether linear discussion in GitHub issues is the best format to untangle all of that – it's hard to address the abundance of issues that got conflated here. I wonder whether we should use the discussions GitHub feature or branch out more issues to spark discussions laser-focused on the specific aspects of that?

To me, the two most actionable problems are:

Dealing with the existing __experimental APIs

We're discussing __experimental APIs with the following reach:

  • APIs used by millions and discoverable via code search
  • APIs used by millions and not discoverable via code search
  • APIs used by few/none (hard to distinguish from ↑)

These APIs fall into one of two buckers:

  • APIs matured and can be stabilized
  • APIs that didn't pan out and that we'd like to ditch

The three options we seem to be debating with regard to __experimental APIs that are currently exposed:

  • Keep them indefinitely, stabilize some today
  • Deprecate them, stabilize some today and remove others after a few releases.
  • Stabilize some today, remove others today

There's 18 different paths across these lists, e.g. popular experimental APIs that already matured and we'd like to keep around indefinitely. That's a lot of ground to cover in a single issue! All the downsides and upsides of each option have been already mentioned and it doesn't seem like reiterating them moves the discussion forward anymore.

Since top-down planning seems hard, I echo @mtias and propose we try a discovery/bottom-up route:

  1. Create some PRs to stabilize specific APIs
  2. Discuss it on a case-by-case basis there. Specific questions to consider:
  • What's the cost of hard-removing that specific API?
  • What's the cost of soft-deprecating that specific API?
  • What's the cost of leaving that specific API indefinitely?

We may quickly find that we're clashing at the strategy level but agree at the tactical level. If so, it would help us find a new alignment on the strategy level.

To @dmsnell's point:

There's almost no cost associated to stabilizing without breaking existing code and we win trust and favor. Why are we arguing about intentionally breaking integrations we know are out there and in use? Why would we rather have the editor crash when someone's just trying to write their content than to have an experimental API stick around as a deprecation for a little while longer?

This applies to APIs that can be stabilized and I'm happy with such resolution. There may be some APIs intended for removal – I don't think we can blanket-cover them in this discussion so I'd like to follow up on a case-by-case basis.

Dealing with the future __experimental APIs

Experimenting without breaking the internet

The arguments in this debate seem to be:

  • Adding and removing public __experimental APIs as needed is necessary to develop Gutenberg effectively
  • Removing public __experimental APIs burdens the extenders and is detrimental to the community

Both arguments hinge on the word "public".

Notice how there aren't many __experimental features on the PHP side, if any. Project philosophy aside, in PHP it's easy to restrict access via private function x(). This is how the WebFonts API got shipped in WordPress 6.0: it's all encapsulated in closures and private functions so that extenders cannot consume it:

https://github.com/WordPress/wordpress-develop/blob/02414638ce37bb4a58fad2491151fb2fcf4a4e73/src/wp-includes/script-loader.php#L3040-L3052

While the support for WebFonts is there, the API remains an implementation detail to clarify over time.

We need this, but for JavaScript. To me, __experimental prefix was an attempt to implement cross-module internal functions using a naming scheme. This discussion tells me it's such an unfortunate fact they've been exposed via window.wp and we need to develop a much stricter limitation. @noisysocks proposed one above and @gziolo started implementing it in #41278. I am happy with it, but if anyone has any other ideas – please, please, please speak out!

Providing the community with well-defined extension points they need

Restrictions aside, @Clorith also said the following:

we need to look at it from the position of someone trying to extend the editor, and understand that they are also bound by that same need.

We all want to build an extensible editor here, I don't think anyone is debating this.

I also do agree there's a ton of missing extension points. Just yesterday I've seen a request for the ability to disable specific editor features or block controls or inspector controls. There's a whole lot of good work to do in this area.

At the same time, I don't view all __experimental APIs as extension points:

  • __experimentalLinkControl certainly is one, let's stabilize it.
  • __experimentalUseQuerySelect is an internal experiment that we luckily didn't have to export – I'd rather keep private for now and retain the remove it on a whim as needed
  • __experimentalElementButtonClassName makes button elements possible to manage using Global Styles. The feature is needed, but the implementation may fluctuate wildly. It's not a well-defined extension point yet, but, in all likelihood, it will become one in the future. I'd like to avoid prematurely exposing the API to the extenders even if the feature makes it to WordPress 6.1. It's the same situation as WebFonts (cc @scruffian @hellofromtonya).

So:

  • Not all __experimental are extension points.
  • Feature releases can precede some or all extension points releases.
  • The future __experimental APIs that are not meant to be extension points can be safely internalized

Also, the community would greatly benefit from intentionally exposing the __experimental APIs once they mature enough to be useful extension points and remain stable over time.

This is at least three different discussions:

  • What's the desired lifecycle of features, extension points, and public APIs in Gutenberg given the technical constraints of JavaScript modules?
  • What highly requested extension points are missing today?
  • How can we build well-defined extension points as we go to provide the developer community with the exact technical features they need AND with the same ease of working with the editor as core developers have today?

I'd say that's beyond scope of this issue which was meant to figure out a way to stabilize the 280 existing experimental APIs. I'm more than happy to take it elsewhere, though. Do we have any existing issues about these?

@dream-encode
Copy link

Hi 👋 I am davidbaumwald on W.org, and I am one of the Core Committers that privately reached out to @peterwilsoncc to echo my concerns about this.

I also agree with @Clorith that the plugin is the place to iterate on the editor, not Core. Regarding this...

There must be a way to ship them and retain the ability to remove them. If anything that's made public cannot be easily removed, there's no point in maintaining Gutenberg as a separate repository. I now err on the side of keeping the ability to hard-deprecate experimental APIs even if they made it as public to WordPress core.

I think this is the opposite as to how I would assess the problem. The plugin exists to iterate and stabilize before merging into Core, no different than any other feature plugin.

I disagree with this sentiment...

I'd even argue that changes to HTML or CSS (in the admin) (regardless if they come from Gutenberg packages or Core) is causing more issues than changes coming from experimental and unstable APIs. I'm sure you'd agree with me that we can't stop making changes to HTML and CSS because that basically means stop making changes full stop.
I don't think anyone is advocating for halting development. The discussion here is that code that makes it into Core is intended to be stable. As committers, we merge fixes to existing CSS/HTML but must consider back-compat.

Finally, I think it's clear that there's a huge disconnect between what is considered policy. My understanding is that any documentation appearing in the Block Editor Handbook applies only to developing with the editor. Plugins and Themes also have their own handbook for development, but their APIs that appear in Core must abide by the Core Handbook.

@akirk
Copy link
Contributor

akirk commented Jun 10, 2022

I have to admit I haven't read all the comments up until now but I wanted to share an idea that I had around this: Could we by default hide experimental APIs behind a getter like in #41278 and add the Gutenberg version when it was introduced as a variable?

Then we could give experimental APIs a certain number of Gutenberg versions to mature (say 5) and after that it would start to console.log() TODO messages. Then the decision could be made to promote them to a non-experimental API and through the getter not break plugins that use it, or give it another version to mature.

I know this might ignore some of the previous discussion, I just wanted to share this idea as a possible path.

@adamziel
Copy link
Contributor Author

adamziel commented Aug 2, 2022

After some more discussions, I believe this solution comes close to everyone’s goals:

Remove the __experimental prefix before merging new APIs into WordPress core

WordPress core needs stability and backwards compatibility. Gutenberg needs agility and freedom to remove any __experimental API. We won't find a reasonable middle-ground without compromising either of these goals, but we can change the integration process.

Let's only merge those Gutenberg features into Core that would get accepted as a standalone wordpress-develop PR. No special treatment.

This way:

You all came up with this idea, not me. I'm just facilitating the discussion as we somehow never reached a consensus.

What if a stable feature depends on an experimental feature?
Then it isn’t actually stable. Let’s stabilize the dependencies first.

What about the existing __experimental APIs?
Most of those already merged to core would get a stable alias. It would preserve BC and shouldn't noticeably affect the bundle size. Some will need a different treatment, let's discuss that case-by-case.

What if we really need to remove an existing __experimental API that's already in core?
Do we? If so, let's consider that on a case-by-case basis. There are established Core practices like contacting plugin authors, writing make core posts, preferring soft deprecations and so on. I don’t expect to see many instances of this.

What if we really need to remove a future stable Gutenberg API after it's released in core?
Yes, it will happen. Let's acknowledge and embrace it. Some good reasons were already mentioned in this discussion and the future will surprise use with many new ones. Again, let's follow the established core practices. For example, deprecated.php shows that removing a function body is sometimes okay as long as the name keeps working.

How to make this happen? There's no process for that today.
Block-library already excludes the experimental blocks from the core build and other packages could do the same. We'd also need a simple safeguard such as a pre-commit hook or a CI check to confirm there are no new references to __experimental APIs.

There's also a relevant discussion about opening wordpress-develop PRs in parallel with Gutenberg PRs, at least for PHP changes. Right now Gutenberg PHP code gets blanket-backported to core right before a release, which is far from perfect.

What are the downsides?

I see two:

  • Some Gutenberg features will get merged into the Core later that they currently would be. The total amount of the development work won't change but the merging timeline will.
  • Refactoring Gutenberg APIs will be difficult once they get shipped with core. In reality, that’s already the case.

Is there any other downside that I missed?

Alternatives

Keep not acting on this issue

In this scenario everyone loses:

  • Deprecating Gutenberg APIs will lead to tensions and debates.
  • Some deprecations will fall through the cracks and get merged, meaning core won't deliver on its Backwards Compatibility promise.
  • Both sides of this debate will feel unheard and adversarial.

Align WordPress and Gutenberg Backwards Compatibility policies

It would make both projects worse-off. The needs are just too different. Personally, I think of Gutenberg as of a WordPress Early Access release channel. Aligning Early Access and Stable wouldn't help either.

Find a way to ship the __experimental APIs with core as "internal" and unavailable to plugin authors

@gziolo explored that in #41278 and it didn't get much traction. There were also some technical challenges. I can't think of any alternative approaches, so it seems like a dead-end.

Do you agree?

If yes, please react with 👍 to this comment. If not, please elaborate.

@talldan
Copy link
Contributor

talldan commented Aug 3, 2022

Let's only merge those Gutenberg features into Core that would get accepted as a standalone wordpress-develop PR

@adamziel Something wasn't clear to me, so it'd be good to clarify. Is the idea to selectively ship Gutenberg features to core? Or are you proposing that every API is stabilised when a WordPress release approaches?

@adamziel
Copy link
Contributor Author

adamziel commented Aug 3, 2022

@talldan The idea is to ensure no new __experimental API makes it to core. We'd stabilize those that matured and punt those that didn't. You could call it selective shipping, but that phrase makes me think of messy, manual merges that I'd like to avoid. How about we follow the pattern established in block-library where all experimental blocks are hidden behind a feature flag and thus not included in the core build?

export const __experimentalRegisterExperimentalCoreBlocks = process.env
.IS_GUTENBERG_PLUGIN
? ( { enableFSEBlocks } = {} ) => {

Perhaps this could be automated further with a webpack plugin to automatically remove the __experimental exports from core builds.

@talldan
Copy link
Contributor

talldan commented Aug 3, 2022

How about we follow the pattern established in block-library where all experimental blocks are hidden behind a feature flag and thus not included in the core build?

I think something like that would be needed, but perhaps a bit more evolved. A proper configurable feature flagging system.

To be honest, I think it may be a good idea to consider feature flagging a lot of new work anyway. Whether we like it or not, the Gutenberg plugin has widespread usage in production, and the number of bugs and regressions that are shipped currently is very uncomfortable (at least to me). I don't think the risk of using the plugin is adequately conveyed.

Feature flagging doesn't necessarily mean that all experimental features are disabled by default, but perhaps it could work like this:

  • Experimental features are active by default in development builds of Gutenberg, but can be toggled off by the user.
  • Experimental features are inactive by default in production builds of Gutenberg, but can be toggled on by the user.
  • Features can be promoted or relegated between development or production without re-writing code.
  • Experimental features are excluded completely from builds for WordPress core until the code is re-written to remove the feature flag - signifying stability.

@peterwilsoncc
Copy link
Contributor

To be honest, I think it may be a good idea to consider feature flagging a lot of new work anyway. Whether we like it or not, the Gutenberg plugin has widespread usage in production, and the number of bugs and regressions that are shipped currently is very uncomfortable (at least to me). I don't think the risk of using the plugin is adequately conveyed.

True the plugin is used in production. I think this is becoming less common as features get shipped in Core based on anecdotes I am hearing from agencies.

I agree with @adamziel's comment that Gutenberg is a WordPress Early Access release channel and needs a more relaxed attitude to backward compatibility that WordPress Core. Possibly the Early Access section in the readme needs to be earlier and be less ambiguous about not being production ready.

As indicated by my 👍 vote, I support Adam's proposal and think it an excellent way to move forward.

@Clorith
Copy link
Member

Clorith commented Aug 4, 2022

I'd like to echo the sentiment from @peterwilsoncc (and also, huge thanks to @adamziel for summarizing this discussion really well, to what I believe is the right path forward); Let's make it more obvious that the Gutenberg plugin is a alpha/beta-channel for upcoming changes to certain areas of core, instead of hiding things behind feature flags, which some may not even know are there. I use the plugin for experimenting, but I honestly never remember to visit the experiments page to turn on things, so if they're off at any stage, they're not as discoverable as we would like (or that's my perception at least).

And as an agency, I can confirm, we've moved completely away from using the plugin in production. it was a necessity for a while, to get your hands on features that would not be ready for a few core releases, but we've found that we're past that hurdle, the editor as it stands has most of the flexibility an agency needs at least by now.

@tradesouthwest
Copy link

Gutenberg should not be anything core, let alone "__experimental." Gutenberg should not ship with core and should stand on its own; like WooCommerce or any Page builder plugin. Leave "__experimental" to developers, not to the mercy of the community at-large who are having enough trouble trying to understand why the APIs are using a third and a fourth programming language to parse any data of a block; instead of using the PHP and flat javascript of what WP developers already know and trust.

Basically, anything new, conceptually (experimental) or progressive enough as a page builder or ecommerce attachment should stay away from core 101%.

So my vote is to not promote use of anything interim or temporary into core. Core is for stable. Plugins are for experimental.

adamziel added a commit that referenced this issue Sep 26, 2022
Make the __experimental APIs private by introducing a dealer mechanism that only grants access to core WordPress packages.

It solves the problem of leaking private experimental APIs to extenders in public stable releases. See #40316 for more details.

Usage example:

```js
// in @wordpress/data
import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/experiments';
const experiments = __dangerousOptInToUnstableAPIsOnlyForCoreModules(
	'I know using unstable features means my plugin or theme will inevitably break on the next WordPress release.',
	'@wordpress/data'
);

export const __experiments = experiments.register({
	__experimentalFunction: () => { /* ... */ },
});
```

```js
// In @wordpress/core-data:
import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/experiments';
import { __experiments as __dataExperiments } from '@wordpress/data';

const experiments = __dangerousOptInToUnstableAPIsOnlyForCoreModules(
	'I know using unstable features means my plugin or theme will inevitably break on the next WordPress release.',
	'@wordpress/core-data'
);

// Get the experimental APIs registered by @wordpress/data
const { __experimentalFunction } = experiments.unlock( __dataExperiments );

__experimentalFunction();
```
@peterwilsoncc
Copy link
Contributor

peterwilsoncc commented Sep 27, 2022

With the introduction of the @wordpress/experiments package to make it very, very difficult for theme and plugin developers to use new experimental developer APIs, I think there now exists a robust solution for allowing function signatures that will change.

Thanks @adamziel for figuring out a solution that allows for an early-access channel to co-exist with the stable product.

@adamziel
Copy link
Contributor Author

adamziel commented Sep 29, 2022

Yay indeed @peterwilsoncc! Now that there is a way forward, the next steps would be:

Tame new experimental APIs

  1. Update the handbook with specific guidelines for adding experimental APIs
  2. Enable different use-cases via @wordpress/experimental
  3. Make the unpublished experimental APIs private
  4. Add build warnings when new experimental APIs are exported

Related to number 2 above, here's a PR to enable experimental selectors. Other than that there are:

  • Private actions – quite similar to the selectors
  • Private function arguments – it could take two separate signatures
  • Private React component properties
  • Private functions and constants – already supported by @wordpress/experiments
  • ...anything else?

Stabilize the existing experimental APIs

Work through the existing __experimental APIs to add deprecations and provide stable versions when applicable.

@adamziel
Copy link
Contributor Author

adamziel commented Mar 3, 2023

Update: Private APIs are now separate from experimental APIs 🎉

Private APIs

Private APIs are now shared internally inside the Gutenberg codebase using the new lock and unlock utilities. They can't be imported from the outside. The contributors started using lock and unlock rapidly and @ntsekouras coordinated the effort to convert any new __experimental exports into private ones for WordPress 6.2.

I reviewed the wp/6.2 Gutenberg branch and found only two new __experimental exports: __experimentalUseSlotFills and __experimentalPopoverPositionToPlacement. This is the lowest number I've seen by a wide margin. There were WordPress releases with 40 new experimental exports, 20 new exports, and even 14, but not less. Two is phenomenal 🎉

Experimental APIs

Experimental APIs are like early access feature previews that require community feedback to mature. New experimental APIs should not be merged into WordPress core, but they will continue to be released in the Gutenberg plugin.

Next steps

This issue had two goals:

  • Tame new experimental APIs
  • Stabilize the existing experimental APIs

The first goal is now largely achieved. The remaining follow-up work is tracked separately in #47786.

As for the second goal, let's track it separately in #48743. It will require a case-by-case assessment of a few hundred __experimental APIs that are in WordPress core today. WordPress 6.2 dealt with a few such exports, and the rest of them will likely be gradually addressed in each future release. Some of them will get stabilized, others will get deprecated, and others may stay indefinitely. The upcoming community summit may yield some answers, as @peterwilsoncc proposed the problem of deprecating JavaScript APIs as a discussion topic.

I really appreciate the discussion we've had here and what it achieved – thank you, everyone!

@jeffpaul
Copy link
Member

Great work here folks, thanks for all your efforts!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Developer Experience Ideas about improving block and theme developer experience [Type] Code Quality Issues or PRs that relate to code quality
Projects
None yet
Development

No branches or pull requests