WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.51k stars 4.2k forks source link

Components: Document our deprecation procedures #61099

Open mirka opened 6 months ago

mirka commented 6 months ago

What problem does this address?

In the past, there has been a lot of ambiguity in our back compat policy. We've been working to bring more clarity around these issues, through efforts like Private APIs and removing __experimental prefixes (#59418).

Here, I'd like to increase clarity around component deprecations. The goal of this is twofold:

What is your proposed solution?

A "deprecation", at the very basic level, means that we don't encourage new usages of the component.

There are two reasons we may feel this way:

If there is no big harm in keeping existing usages, we will be as unobtrusive as possible about the deprecation ("passive deprecation"). On the other hand, if there are strong enough reasons to discourage continued usage, we will also log a deprecation warning in the console and write a Dev Note ("soft deprecation"). Additionally, if the bundle size cost justifies the eventual removal of the code, the deprecation warning will include an end version ("hard deprecation").

If circumstances change, a component may later progress into a stronger level of deprecation.

Deprecation levels

Passive deprecation

Soft deprecation

Hard deprecation

flowchart TD
  1{{"`Do we encourage _new_ usages of this component?`"}}
  1 -- Yes --> 2[Yay!]
  1 -- No --> 3{{"`Are there strong reasons to discontinue _existing_ usage?`"}}
  3 -- Yes --> 4{{"`Is the bundle size cost big enough to justify completely deleting the component?`"}}
  3 -- No --> pdep
  4 -- Yes --> hdep
  4 -- No --> sdep

  subgraph pdep ["Passive Deprecation"]
    pdep1[TypeScript @deprecate]
    -.- pdep2[Update docs]
  end

  subgraph sdep ["Soft Deprecation"]
    sdep1[TypeScript @deprecate]
    -.- sdep2[Update docs]
    -.- sdep3[Dev note]
    -.- sdep4["deprecated() with no end version"]
  end

  subgraph hdep ["Hard Deprecation"]
    hdep1[TypeScript @deprecate]
    -.- hdep2[Update docs]
    -.- hdep3[Dev note]
    -.- hdep4["deprecated() with end version"]
    -.- hdep5[Delete from package when end version arrives]
  end
mirka commented 6 months ago

It is not easy to strike a balance between all the concerns involved (consumer disruption, discoverability, maintenance cost, bundle size, etc).

With that in mind though, please chime in with any feedback that might be relevant. (Including but not limited to: @WordPress/outreach @WordPress/gutenberg-components)

youknowriad commented 6 months ago

Great initiative, I think we should not limit this to components. I think the same constraints apply to any API.

youknowriad commented 6 months ago

Delete from package when end version arrives

The only change I'd make the flow chart above, is that before actually doing this. I'd check existing usage using wp-directory.net. If the impact of removing an API is still big, I'd do make an outreach campaign (post, contact plugin authors) and potentially delay the removal by one or two versions.

annezazu commented 6 months ago

+1 to what Riad said! I've been a part of those efforts more than a few times and I think it goes a long way.

t-hamano commented 6 months ago

I found out that there is already documentation regarding backwards compatibility policy:

Backward Compatibility – Block Editor Handbook | Developer.WordPress.org

We may also find the discussion in the core ticket below helpful.

#49957 (Adopt a policy to remove deprecated functions) – WordPress Trac

mirka commented 6 months ago

I think we should not limit this to components. I think the same constraints apply to any API.

Probably. If this works out for the components case, I think it can be adapted into the general API case, with some tweaks in procedure and language.

I found out that there is already documentation regarding backwards compatibility policy

Yes, the intention is not to replace that, but to supplement a missing part of it. We were frequently running into repetitive discussions about the deprecation procedures, because we didn't have a common framework that acknowledged how not all deprecations are equal.

peterwilsoncc commented 6 months ago

I think WordPress needs a consistent deprecation policy that covers both JavaScript and PHP. It's too confusing for implementers if one part of the code base has one policy, another part of the code base a different policy. From an implementation perspective, it's all the same.

To that end, I suggest a proposal be written up on make/core so all participants can be aware of it.

mcsf commented 6 months ago

This makes sense, with the provisions already mentioned by Riad, Peter, etc.

My only note is that the messaging seems to stress "bundle size cost" as the only meaningful reason for a hard deprecation. In the narrow context of the Components package, I understand why (a deprecated component or prop is relatively easy to isolate, thereby mitigating the cost in code complexity), but I think it's sensible to explain that there may be other costs to be weighed empirically and not a priori.

mirka commented 6 months ago

I think WordPress needs a consistent deprecation policy that covers both JavaScript and PHP. It's too confusing for implementers if one part of the code base has one policy, another part of the code base a different policy. 

The only concern I have with making a universal policy covering both JS and PHP is that, I personally have no practical experience maintaining the non-components JS packages, much less the PHP APIs. This proposal is based on an intimate understanding of the components package, and all the specific aspects (including TypeScript and Storybook documentation) that need to be balanced there.

If someone with more familiarity with the non-components APIs would like to extract a more generalized deprecation policy out of this draft, that would be great. (I think we would still want to keep this level of specificity when making decisions for the components package, though.)

the messaging seems to stress "bundle size cost" as the only meaningful reason for a hard deprecation. In the narrow context of the Components package, I understand why (a deprecated component or prop is relatively easy to isolate, thereby mitigating the cost in code complexity), but I think it's sensible to explain that there may be other costs to be weighed empirically and not a priori.

I completely agree. This is one of the things that should be re-written in a more generalized way if we want to extract a universal deprecation policy out of this.

youknowriad commented 6 months ago

I think we would still want to keep this level of specificity when making decisions for the components package, though.

Personally, while I can see some differences between PHP and JS when it comes to decision making (mainly because the impact is not similar), I have a hard time seeing anything specific to the components package compared to any other JS API. So I'd love to understand why you think there are specificities here?

tyxla commented 6 months ago

Thanks for opening it up @mirka and for the discussion so far everyone!

The cost of shipping deprecated code in PHP has historically been cheaper than doing it in JS since in JS it may impact bundle size. But that doesn't mean that the sole reason for hard deprecation is bundle size impact - there might be other reasons, like abandoning a poorly designed API in favor of a better one, or just the fact that we have a better alternative that everyone should be using in order to get a better user experience and optimal performance.

That being said, I agree with the gist of @mirka's proposal and I also agree with @youknowriad that it might be a good idea to generalize the suggested deprecation procedures for any other JS API in Gutenberg. For many components and APIs, it might actually be simpler, because of fewer usages across the plugin directory, and because they're not so thoroughly covered by storybook stories and tests.

However, I think that there might be parts of the policy that don't necessarily make sense for PHP APIs - historically, we've kept close to full backward compatibility on the backend side - if a PHP API worked in 2005, it's likely to be still working. If we keep that, we might never hard-deprecate anything in our JS packages, and with their experimental and swift-moving nature, I think that would be a downside.

If we agree that we're looking at crafting a universal deprecation procedure for JS APIs, in addition to thoroughly documenting it and publicizing it, there are three things I'd propose as corrections to the existing plan:

  1. Considering that the policy applies not only to components but any JS APIs in general (props, hooks, utils, etc.).
  2. Stressing on the fact that it applies to JS APIs and components only, and doesn't necessarily affect PHP APIs.
  3. Reconsidering the hard vs soft deprecation decision flow to account for other important factors, like known existing usage being low or none, better alternatives being available, and existing good reasons not to use (like perf implications, etc).
peterwilsoncc commented 6 months ago

There's a few reasons I think WordPress needs a universal policy for PHP and JS proposed on make/core:

  1. It's the only way you'll get buy in from all contributors.
  2. The affect on end users is near identical: a white screen of death at worst, an error message at best. A users doesn't know or care if it's caused client or server side.
  3. The performance impact of deprecated APIs in PHP can be equally terrible, for example get_usermeta() has much poorer performance than get_user_meta().