Page MenuHomePhabricator

RFC: Amendment to the Stable interface policy (November 2020)
Closed, ResolvedPublic

Description

Motivation

The proposed amendment of the Stable Interface Policy aims to do the following:

  • remove ambiguity and clarify wording
  • provide guidance for cases previously not covered
  • streamline the deprecation process to allow for deprecated code to be removed more quickly
  • clarify responsibilities during the deprecation period to provide more clarity and better support for third party extension authors.

Requirements

  1. Define guarantees and annotations for PHP ''traits''. This was previously missing.
  2. Define guarantees and a deprecation process for member fields. This was previously missing.
  3. Define a process for deprecating methods that are stable to override. This was lacking a mechanism for triggering deprecation warnings when the method is overridden.
  4. Define which extensions are to be considered to what extent when deciding whether code can be hard-deprecated or removed.
  5. Define who is responsible for removing usages of deprecated code.

Exploration

Notable changes to the stable interface definition:

  • Provide a definition of "MediaWiki ecosystem" (see also T268328) and "Wikimedia maintained code".
  • Declare that traits are not safe to use unless marked as @stable to use. In traits that are safe to use, all methods, including private methods, are stable to call.
  • Declare that fields are safe to read, but not safe to write. Define a process for deprecating fields.
  • Consider the use of interfaces in the new hook system.
  • State that when deprecating methods that are stable to override, if the method is overridden by a subclass, a deprecation warning must be triggered, but the method must still be called. (helper code for this is pending, see T267080)

Notable changes to the deprecation process:

  • Clarify that the goal of the deprecation process is to remove deprecated code as soon as possible without breaking things in critical ways.
  • Define a hierarchy of support for different groups of extensions: those used and maintained by WMF have the highest priority, popular open source 3rd party extensions should receive active support, other 3rd party extensions should receive reasonable warning about changes.
  • Clarify that individuals or teams that deprecate code are also responsible for following up and removing usages of that code, and support authors of affected extensions.
  • Clarify the timeline for the deprecation process:
    • ideally, hard deprecation follows soft deprecation within the same release
    • removal may occur after hard deprecation in at least one release and at least three months on the development branch.
  • Remove the section defining exceptions allowing removal without deprecation. Instead:
    • Allow hard deprecation by public announcement in cases where it is not possible to emit deprecation warnings.
    • Allow removal of code that was never used elsewhere, or never released.
  • Clarify that deprecation and removal should not be backported to release candidates, and should ideally be performed soon after a release, rather than shortly before a release.

Notable changes to the scope:

  • Extensions are no longer expected to provide any stable interface per default - the scope had been extended to cover extensions in March (T193613), which now seems like a mistake: MediaWiki is designed as an extensible framework, but most extensions are not.
  • The MediaWiki "ecosystem" is defined to include actively maintained extensions hosted by Wikimedia and extensions listed by the MediaWiki Stakeholder Group.

Resourcing and Stewardship

No work beyond writing the draft is needed. Stewardship of the policy remains as before.

See also

Event Timeline

daniel updated the task description. (Show Details)
daniel updated the task description. (Show Details)
daniel updated the task description. (Show Details)
daniel updated the task description. (Show Details)

Tim noted that we can't guarantee that we'll emit deprecation warnings before removing any kind of behavior or concept, see T267080#6639286. Perhaps that should be mentioned.

Can we also clarify if extensions' public code qualifies as a stable interface?

The draft says in the "Scope" section:

*Extensions maintained by Wikimedia only if they offer extension points such as hooks, or explicitly opt into this policy. Per default, extensions are themselves not considered extensible, and do not offer a stable interface.
*Code in repository in the MediaWiki ecosystem only if and only of it explicitly opts into this policy.

This mans that public methods in extensions are no longer "stable" per default. They had been added to the scope only recently, and in retrospect, that was probably not a good idea, since most extensions are not intended to be used as frameworks, while core is.

daniel updated the task description. (Show Details)

In traits that are safe to use, all methods, including private methods, are stable to call.

Can we clarify that these methods are stable to call by default, but can still be marked as @internal/@unstable/@deprecated as with other code?

Additionally, how can we deprecate using a trait entirely? All of the methods should be deprecated and then removed following the normal deprecation process, but how exactly is it possible to emit deprecation warnings for useing a trait?

[...]
Additionally, how can we deprecate using a trait entirely? All of the methods should be deprecated and then removed following the normal deprecation process, but how exactly is it possible to emit deprecation warnings for useing a trait?

Hmm, if a trait is use'ed, but not actually used (ie. no method is called), removing the use statement shouldn't hurt anyone. If the methods _are_ called, they would be deprecated under the current deprecation policy, and emit hard deprecation warnings for some time. I believe we can just treat that as an "include", and remove trait as long as its methods aren't used anywhere.

Krinkle renamed this task from RFC: Amendment to the Stable interface policy, November 2020 to RFC: Amendment to the Stable interface policy (November 2020).Dec 1 2020, 11:34 PM

[...]
Additionally, how can we deprecate using a trait entirely? All of the methods should be deprecated and then removed following the normal deprecation process, but how exactly is it possible to emit deprecation warnings for useing a trait?

Hmm, if a trait is use'ed, but not actually used (ie. no method is called), removing the use statement shouldn't hurt anyone. If the methods _are_ called, they would be deprecated under the current deprecation policy, and emit hard deprecation warnings for some time. I believe we can just treat that as an "include", and remove trait as long as its methods aren't used anywhere.

I mean

Core trait
trait EmptyCoreTrait {
	// Nothing here, all of the methods have already been deprecated and removed
}
Extension not in codesearch
class RandomClass {
	use EmptyCoreTrait;
	...
}

If the core trait is simply removed, won't the extension break because it tries to use a trait that doesn't exist? If so, how can we emit deprecation warnings before the removal, if no methods are used?

In traits that are safe to use, all methods, including private methods, are stable to call.

Can we clarify that these methods are stable to call by default, but can still be marked as @internal/@unstable/@deprecated as with other code?

The draft says:
All methods of usable traits are automatically stable to call, unless they are marked @deprecated, @internal or @unstable

Additionally, how can we deprecate using a trait entirely? All of the methods should be deprecated and then removed following the normal deprecation process, but how exactly is it possible to emit deprecation warnings for useing a trait?

It's not possible to emit deprecation warnings when a deprecated trait is used. It's generally not possible in all cases to emit deprecation warnings before removing obsolete concepts or behavior. Global variables and interfaces are other examples.

For this reason, the Removal section now states:
If it is not reasonably possible for obsolete code to emit deprecation warnings, it MAY be removed if the removal has been announced on wikitech-l at least three month and at least one major release prior. The announcement must explain why deprecation is not possible or not reasonable, and provide an opportunity for affected parties to raise concerns and propose alternatives.

I'm thinking about further clarifying this in the definition of "hard deprecation".

it MAY be removed if the removal has been announced on wikitech-l at least three month and at least one major release prior

3 months is awfully arbitrary, especially when you have a non-arbitrary criterion right next to it.

it MAY be removed if the removal has been announced on wikitech-l at least three month and at least one major release prior

3 months is awfully arbitrary, especially when you have a non-arbitrary criterion right next to it.

It indeed is arbitrary. Why is that a bad thing, and what is the alternative?

We could say "two released", but that can mean up to a year . We could say "one release", but that could mean that the code gets deprecated just before the release branch is cut, and removed right after. You could go from soft deprecation to removal within a week. This is fine for people using snapshot releases of core and extensions, but it may break extension's development branch and CI. The three months are intended to give developers some warning before their code becomes incompatible with MediaWiki's development (master) branch.

Do you think this is unnecessary? Or do you have a better idea than requiring at least 3 months before removal?

I agree we should discourage the discruptive pattern of deprecating and removing around the branch date. I'm guilty of this myself, but support discouring this by policy going forward. This means what we want is a minimum amount of actual time for something to be hard-deprecated in master branches. While any amount we come up with is arbitrary, I think it makes sense to encode it as an amount of time and not something else, because that is in fact the need we are addressing.

We do not have a need currently in this task where someone wants two releases to exist. That would also be arbitrary to me, and also confusing, since that induces a rather long time amount (6-12 months, depending on where we are in the cycle so that two branch cuts have occurred) which is also not based on something we have a current ask or need for from anyone.

Whatever number of months we want to require, I propose we encode it as that, a hard-deprecated method may be removed after 1 release cut has ocurred and a minimum number of months have passed since the hard-deprecation.

I agree we should discourage the discruptive pattern of deprecating and removing around the branch date. I'm guilty of this myself, but support discouring this by policy going forward. This means what we want is a minimum amount of actual time for something to be hard-deprecated in master branches. While any amount we come up with is arbitrary, I think it makes sense to encode it as an amount of time and not something else, because that is in fact the need we are addressing.

We do not have a need currently in this task where someone wants two releases to exist. That would also be arbitrary to me, and also confusing, since that induces a rather long time amount (6-12 months, depending on where we are in the cycle so that two branch cuts have occurred) which is also not based on something we have a current ask or need for from anyone.

Whatever number of months we want to require, I propose we encode it as that, a hard-deprecated method may be removed after 1 release cut has ocurred and a minimum number of months have passed since the hard-deprecation.

I'd like to object to this as it applies to deprecating/removing right before the branch cut - we shouldn't need to maintain things for an extra version so that it is hard-deprecated for longer. But, as it applies to deprecating/removing right after the branch cut, that makes a bit more sense, because it doesn't delay by a version, just by a few months.

I'd like to object to this as it applies to deprecating/removing right before the branch cut - we shouldn't need to maintain things for an extra version so that it is hard-deprecated for longer. But, as it applies to deprecating/removing right after the branch cut, that makes a bit more sense, because it doesn't delay by a version, just by a few months.

Hard deprecation and removal may generate fallout - if extension authors do not follow up in time, broken code (or failing tests) may get into a snapshot release. Avoiding removal an deprecation right before a release gives extension authors a better chance to spot issues.

To clarify this, I have put this under "further guidance":

  • Deprecations and removals SHOULD NOT be performed shortly before a release branch or between release candidates, giving extension authors time to fix any issues that may arise, and avoid broken snapshots of extensions.

This does mean that we may have to maintain code for longer, even for an entire release. I don't really see a way to avoid this. But we can already fix all callers in advance, and we can turn the deprecated method into a stub. So there is not much to maintain.

Per Wednesday's TechCom meeting, this RFC is entering the Last Call period. If no concerns remain unaddressed by January 20, the policy will be amended as proposed.

Per Wednesday's TechCom meeting, this RFC is entering the Last Call period. If no concerns remain unaddressed by January 20, the policy will be amended as proposed.

Moving on workboard accordingly