User:DKinzler (WMF)/Stable interface policy

The stable interface policy for MediaWiki PHP code defines what parts of the software are considered stable and safe for use by other components. Code that is considered part of this "stable interface" is subject to the deprecation process.

Using code

 * It is generally stable to call public methods on a class instance.
 * It is generally not stable to construct a class (instantiate).
 * It is generally not stable to extend a class (subclass) and not stable to implement an interface.

Writing code
When changing existing code:


 * Keep public methods and hook signatures compatible for callers. Follow the deprecation process for breaking changes.
 * Keep constructor signatures compatible, if it is marked.
 * Keep method signatures compatible for subclasses, if the method is marked.

When creating new code:


 * When defining hooks, keep the signature minimal. Expose narrow interfaces, ideally only pure value objects, as parameters.
 * Avoid using interfaces as extension points. It is recommended to use an abstract base class instead. See Stable to extend.

Terminology

 * Authors: You are working on something that others will use. For example, a class in MediaWiki core that extensions can use.
 * Users: You are working on something that uses a stable interface. For example, a class in an extension that interacts with MediaWiki core.
 * Wikimedia maintained code is defined as as any code running on Wikimedia sites or officially published by Wikimedia for use by others.
 * The MediaWiki ecosystem includes code in extensions that are published as free software and actively maintained in a public repository linked from mediawiki.org.

Stable to call
Stable to call can apply to methods and functions. It means they stay backwards-compatibility between releases. This stability applies to both the behavior (its contract), and the signature. Breaking changes that would impact callers must follow the deprecation policy.

Note that methods are not stable to override by default.

Included:


 * Global functions of which the name starts with the "wf" prefix.
 * Public methods on any class instance.
 * Protected methods of a class that is stable to extend.
 * All methods in traits that are stable to use.
 * Constructor methods that are marked . This means their class will be considered "newable" and thus may be instantiated using the   operator in any code.
 * Constructor methods of classes marked.

Not included:

For authors:
 * Any constructor method, unless marked.
 * Any method or function marked,   or.
 * Legacy class methods that do not have an explicit visibility modifier. These are technically public, but considered unstable.


 * It is recommended to only mark constructors as stable to call if they are for value objects or for extendable classes.
 * When making a constructor method, consider marking the class it belongs to as  . This technically provides the stability guruantee, and is used to  in discoverability of the stable constructor, and as self-documenting way to encourage a usage pattern through the   operator. It is at the author's discretion to decide whether or not to mark a class with a stable constructor as  . For example, if the class is generally only constructed through an intermediary utility method or subclass, then it may benefit users to not draw attention to the constructor.
 * For complex classes that may involve dependency injection, you should avoid making the constructor stable to call, as this means adding or changing dependencies would constitute a breaking change that requires following the deprecation policy.

Stable to type
Stable to type can apply to interfaces and classes. It means the type will continue to exist between releases and provide at least the same public methods that are stable to call. You can type against these interfaces and classes from various contexts; such as argument type declarations ("type hints"), return types,  statements, and   assertions.

Remember that by default interfaces are not stable to implement, and thus methods may be widened or added without notice. As PHP requires implementations to define all methods and use the same or narrower signatures, these would normally be breaking changes, but are backwards-compatible for the purpose of typehints and calling methods. For the same reason, an interface may become a class, and a class may become an interface without notice, unless it provides additional guarantees such as,  , or.

Included:

Not included:
 * All classes and interfaces.


 * Any class or interface marked,   or.

For authors:


 * Avoid using interfaces as extension points. It is recommended to use an abstract base class instead. See Stable to extend.
 * When you do create interfaces, it is recommended that you explicitly mark them as . This is intended to aid the discovery of limited guarantees around interfaces.

Stable to extend
Stable to extend can apply to classes. It means the class and its methods will stay backward-compatible between releases and may be subclassed anywhere. Changes that affect subclasses will follow the deprecation policy. Protected (and public) methods of extendable classes are automatically stable to call, unless they are marked,   or. Remember that by default methods remain not stable to override, unless they are abstract.

Included:


 * Only classes that are marked.

For authors:


 * Constructor methods of extendable classes must be marked.


 * When allowing extensions to create additional classes of a certain type, it is recommended you provide an abstract base class (marked stable to extend) instead of an interface. This is because is not possible to use deprecation in an interface. If you mark an interface as stable to implement, you commit to never changing its method signatures, and never adding new methods – unless the interface as a whole is deprecated first.

Stable to use
Stable to use can apply to traits. It means all methods defined in the trait will stay backward-compatible between releases. Changes that affect classes using the trait will follow the deprecation policy. All methods of usable traits are automatically stable to call, unless they are marked,   or. Remember that by default methods remain not stable to override, unless they are abstract.

Included:


 * Only traits that are marked.

Stable to access
Stable to access applies to fields of most classes. It means that the field will not be removed, and its behavior will not change, without going through the deprecation process. It however does not mean that they will keep being read, so there is no guarantee that writing to them will have the desired effect in the future, unless such a guarantee is explicitly given in the documentation of the field.

Included:


 * Public fields
 * Protected fields of classes that are stable to extend

Not included:
 * write access
 * any field marked as,   or

For authors:
 * Public fields should generally be avoided in favor of getters and setters.
 * Base classes should generally not expect subclasses to modify protected fields directly. If this is desired, it must be explicitly documented.
 * When hard deprecating a field that is stable to access, PHP's magic __get and __set methods SHOULD be used to trigger a deprecation warning.

Stable to implement
Stable to implement can apply to interfaces. It means they will stay backward-compatible between releases and may be implemented anywhere. Changes that affect implementations will follow the deprecation policy.

Included:


 * Only interfaces that are marked.

For authors:


 * Do not add methods to interfaces marked as.
 * Do not break method signatures in interfaces that are.
 * Any hook interface that is documented should be marked.
 * Avoid using interfaces as extension points other than hook interfaces. It is recommended to use an abstract base class instead. See Stable to extend.

Stable to override
Stable to override can apply to class methods and hooks. It means the method signature will remain compatible for overriding, and the method or callback will continue to be called in relevant circumstances. Changes to that contract must follow the deprecation policy.

Included:


 * Any hook that is documented. For the sake of this policy, hook callbacks are treated as implementations of abstract methods. Hook interfaces follow the normal rules for interfaces. Note that since MediaWiki release 1.35, it is preferred for extensions to implement hook interfaces, rather than registering hook callbacks.


 * Methods that are declared as  in classes that are stable to extend.
 * Any method marked.

Not included:

For authors:
 * Any method marked,   or.

When hard deprecating code that is stable to override,


 * a deprecation warning SHOULD be triggered in case the method is overridden by a subclass.


 * the method MUST still be called if it is overridden.
 * TBD: mention helper (T267080)

Global variables
Global variables are not stable, not even those with the "wg" prefix.

For users:


 * To access site configuration, use  instead.
 * To access service objects, use  methods instead.

For authors:


 * When access to global state cannot be avoided, static methods SHOULD be used.

Add guarantees

 * : See Stable to call.
 * : See Stable to type.
 * : See Stable to extend.
 * : See Stable to implement
 * : See Stable to override.
 * : See Stable to call.

The  annotations can be followed by a   segment to indicate that a particular use of the class or method is only supported since a specific version. For example: The  annotations can be followed by a   segment to indicate that a particular use of the class or method is currently deprecated. This can be used to indicate that extensions should no longer subclass, but may still call public methods. This guruantee may then be removed in the next release. Note that there is currently no hard-deprecation for the removal of stability guarantees.

Remove guarantees

 * : Do not use outside the original module. It may change without notice.
 * : It may change without notice. Similar to, except that unstable things are aimed at external use and intended to become stable in the future.
 * : This means something should not be used as this may be removed in a future release, per the deprecation process. This must include a  segment, and must include instructions for what to use instead (or state that there is no alternative). For example:

Deprecation process
Deprecation becomes necessary when the public interface of code needs to be changed in order to add new functionality or improve architecture. All code that falls within the scope of this policy and defines a stable interface is subject to the deprecation process defined here.

The deprecation process generally consists of the following steps, described in more detail below:


 * 1) soft deprecation, immediately followed by update of calling code
 * 2) hard deprecation, as soon as the deprecated code appears to be unused
 * 3) removal of the deprecated code, after the next release branch has been cut. (In particularly problematic cases, removal MAY be delayed until after a second release).

The purpose of the deprecation process is to update code that uses deprecated functionality, so that the eventual removal of the deprecated code does not break the callers. Usage of deprecated code is considered more or less relevant depending on where it occurs: the most relevant usages are within the same repository, followed by Wikimedia maintained code, further followed by code in the MediaWiki ecosystem. Usages beyond that are considered the least relevant.

Wikimedia maintained code MUST receive special attention in the deprecation process. Extensions in the MediaWiki ecosystem tagged as "stable" or "beta" on their page on mediawiki.org SHOULD be given consideration and support during the deprecation process, according to their popularity (WikiApiary.com and ExtensionDistributor can be used as indicators). Extension developers are encouraged to make their source code public and describe their extensions in the Extension namespace on mediawiki.org using the Extension template.

Individuals, teams and organizations that deprecate code MUST commit to follow through with the deprecation process until the obsolete code has been removed, and they SHOULD be proactive about supporting maintainers of affected code in the MediaWiki ecosystem. Assuming two releases of MediaWiki per year, this process SHOULD take no longer than six months, from soft deprecation to the removal of the deprecated code (or twelve month in particularly complex cases, when removal has to wait until after a second release).

Soft deprecation
Soft deprecation occurs when a developer adds a  annotation to the documentation comment of a method, function, class, or interface.

The following rules apply to soft deprecation:
 * The documentation comment MUST mention what the alternative method or migration path is. If there is no alternative, it should state that.
 * The documentation comment MUST state what MediaWiki core version the deprecation occurred in.
 * As long as is only soft deprecated, it SHOULD function the same as prior to deprecation. If not possible, a best effort SHOULD be made to provide similar functionality that covers typical use cases.
 * Any relevant documentation in the Git repository and on mediawiki.org MUST be updated once the change is approved.
 * The deprecation MUST be mentioned in the relevant RELEASE-NOTES file, and MAY also be mentioned in the "Upgrade notices for MediaWiki administrators" section of the wiki release page depending upon severity. Deprecation of hooks MUST be documented on the Manual:Hooks page.
 * Developers or teams deprecating code SHOULD remove usages in Wikimedia maintained code as soon as possible.
 * Developers or teams deprecating code SHOULD actively support removal of usages in code in the MediaWiki ecosystem, especially in popular extensions. This includes making maintainers aware of the deprecation as well as creating or reviewing patches removing usages.
 * Soft deprecated code SHOULD be unused in Wikimedia maintained code and hard deprecated no more than one major release after soft deprecation.
 * If the deprecated code is unused in Wikimedia maintained code at the time of deprecation, it SHOULD be hard deprecated immediately.

Hard deprecation
Hard deprecation occurs when the code starts emitting a deprecation warning when used, typically by calling. Deprecation warnings cause unit tests to fail.

The following rules apply to hard deprecation:
 * Code that is hard deprecated MUST also be soft deprecated.
 * The version number in the  call MUST match the one in the   annotation, even if the hard deprecation occurs in a different release.
 * Any soft deprecated code SHOULD be hard deprecated as soon as it is no longer used in any Wikimedia maintained code.
 * Hard deprecated code MAY act as no-ops instead of actually functioning, though this is not recommended.
 * Hard deprecation MUST NOT be applied to code still used in Wikimedia maintained code. Such usage MUST be removed first.


 * Deprecation with far-reaching impact SHOULD by announced by email to wikitech-l or mediawiki-l.

Removal
The following rules apply to the removal of code:
 * Code MAY be removed without deprecation only under the conditions listed in the section "Changes without deprecation" below.
 * Callable functions and methods in scope of this process MUST emit deprecation warnings for at least one major MediaWiki version before being removed.
 * Deprecated behavior SHOULD trigger deprecation warnings for at least one major MediaWiki version before being removed. This may however not always be possible (e.g. for the removal of global variables).
 * Deprecated code SHOULD be removed after it has been emitting deprecation warnings in one major release. In case of particularly impactful deprecations, removal MAY be delayed until the code has been emitting deprecation warnings in two major releases.
 * Developers SHOULD consider how difficult it is to support and maintain the deprecated code when determining how urgent removal is. In addition, developers SHOULD consider usage patterns in extensions.
 * Developers MAY consider the LTS cycle in removing deprecated code (removals may be accelerated to avoid deprecated code being included in LTS versions, requiring an extended support period).
 * The removal MUST also be mentioned in the relevant RELEASE-NOTES file, and MAY also be mentioned in the "Upgrade notices for MediaWiki administrators" section of the wiki release page depending upon severity.
 * As one of the principles of MediaWiki, developers MUST ensure any removals will not cause issues an Wikimedia sites. Any removals that cause issues on the live site will be reverted by Wikimedia system administrators.

Changes without deprecation
The deprecation process MAY be bypassed for code that appears to have never been used within the Wikimedia maintained code and the MediaWiki ecosystem (except in the repo that defines it), and seems unlikely to be used elsewhere.

Additionally, in some rare cases, it may be necessary to make breaking changes without deprecation in a major MediaWiki version beforehand, because the old behavior cannot reasonably be emulated. In such a case, developers MUST email wikitech-l ahead of time, explaining why deprecation is not possible or not reasonable, and providing an opportunity for affected parties to raise concerns and propose alternatives.

In any case, all steps about documenting deprecations and removals MUST still be followed, as applicable.

Further guidance
And finally: As with all policies, developers should apply their best judgement when applying it.
 * Usages in code that is itself deprecated, or can only be activated by deprecated configuration settings, SHOULD be ignored for the purpose of this process.
 * Developers SHOULD consider the impact of their proposed changes by searching for existing usage in extensions using tools such Codesearch.
 * Deprecations MUST first take place on the master branch. It is NOT RECOMMENDED to backport deprecations to stable branches.
 * Developers SHOULD deprecate related parts of code together so affected code can be updated all at once.
 * When a task is related to a deprecation, it is RECOMMENDED to tag it specifically in the bug tracker; for instance with the "Deprecation process" tag in Phabricator.

Motivation
The motivation for this policy is two-fold:


 * Offer guarantees to extension developers, providing guidance on what aspects of MediaWiki core they can safely rely upon.
 * Provide guarantees to developers working on MediaWiki core, telling them what aspects of the code they can safely change without having to worry about breaking extensions.

This policy is designed to make extensions more robust against changes in MediaWiki core, and provide more freedom for MediaWiki core code to evolve.

Scope
This policy is mainly written to define a contract between MediaWiki core and MediaWiki extensions, but it also applies to the relationship between MediaWiki and libraries it uses, as well as dependencies between extensions. It applies to applies to the following:

This policy does not apply to the following:
 * PHP code of MediaWiki core (mediawiki/core.git) as published in official releases.
 * Libraries maintained by Wikimedia, inside the core repository or in separate repositories, as published in official releases.
 * 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.


 * Any unreleased code, in particular code as it is on the master or development branch of the report.
 * Web APIs such as api.php or rest.php.
 * client-side JavaScript
 * The structure of HTML output from index.php and other endpoints
 * The structure of dumps or exports
 * The database schema as exposed on Wikimedia Labs.

Those may have their own policies and practices for maintaining stable interfaces.

History

 * This policy was established in January 2017 with RFC T146965 (effective since MediaWiki 1.29), and superseded the guideline archived at Deprecation policy/Until 2017.
 * This policy was amended in March 2020 with RFC T193613, and in June 2020 with RFC T255803 (effective since MediaWiki 1.35 ).
 * The policy for MediaWiki 1.34 and earlier can be found at Stable interfaces up to MediaWiki 1.34.