Stable interface policy

This stable interface policy for PHP code of MediaWiki defines what parts of the software can be considered stable and safe for use by extensions and other code outside of core. More generally, it is intended to provide best practices for using PHP code across module boundaries in general.

All code considered part of the "stable interface" is subject to the Deprecation Process defined below. Any code not part of this stable interface may change without notice.

Motivation
The motivation for this policy is two-fold: On the one hand, it offers guarantees to extension developers, providing guidance on what aspects of MediaWiki core they can safely rely upon. On the other hand, it provides 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.

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

This proposed policy explicitly defines the stable interfaces for use by extensions, to accompany the best practices for extensions. The new policy applies from MediaWiki 1.35 onward. The policy for MediaWiki 1.34 and earlier is given in the section Stable Interfaces up to MediaWiki 1.34 below.

Terminology
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Scope
This proposed policy applies to the PHP API of the MediaWiki core (mediawiki/core.git) codebase. It explicitly does not apply to the api.php API, client-side JavaScript, HTML output, or database schemas. Those should (and do) have their own policies and practices for maintaining stable interfaces, which MUST be followed where applicable. As with all policies, developers should apply their best judgement when following it, and use common sense as necessary.

While this policy is written to define a contract between MediaWiki core and MediaWiki extensions, it can by extension be applied to the relationship between core and libraries used by core. In this case, it is the libraries that expose parts of their code as a stable interface, and MediaWiki core that bind to that stable interface, following the rules set out by this policy.

A "library" in this context would be any separately released code that is used by MediaWiki core, as well as any directory under the  path in the core repository.

The same rules also govern dependencies between libraries, or between extensions, or between extensions and libraries.

Stable Interfaces up to MediaWiki 1.34
''The following definition of stable interfaces, as previously given on the deprecation policy page, is applicable to MediaWiki up to version 1.34. With the release of MediaWiki 1.35, this section becomes obsolete and should be removed.''

The stable part of the PHP API is comprised of all code in MediaWiki core that is explicitly marked public, and has been included in at least one stable release. In addition, some classes expect to be subclassed in extensions; in those cases protected functions also are included in the API. These classes should have a note in their documentation comment that they expect subclassing. If no note is present, it SHOULD be assumed that the class is not expected to be subclassed. Hooks are considered part of the PHP API.

Note: Typically PHP code would use private or final, however those are not supported by PHPUnit's mocking features, and as a result not really used in MediaWiki code.

Classes and/or functions with public visibility MAY also have,   or   annotations to indicate they are not stable interfaces that SHOULD NOT be depended upon.

Some legacy code may not have any visibility modifiers, in which case it is not considered to be part of the stable, public interface. Developers SHOULD add visibility modifiers as soon as possible, and use judgement when making this code protected or private. Where known users exist, developers SHOULD still consider following the full deprecation process. New code MUST have explicit visibility set on all properties and functions.

Definition of the Stable Interface
The signatures and contracts of following parts of the codebase are considered part of the stable interface:


 * Global functions with the "wf" prefix (unless declared otherwise through annotations).
 * Public methods and fields (unless declared otherwise). Legacy functions that are implicitly public due to not having visibility modifiers are not considered public by this policy.
 * Documented hooks (unless declared otherwise).
 * Any other code explicitly opting in to the stable interface, using Stability Annotations.

In contrast, the following things are not part of the stable interface, and are therefore subject to change without notice:


 * Protected methods and fields are not stable, unless otherwise annotated.
 * Method signatures are not safe for overriding, unless otherwise annotated.
 * Constructor signatures are not stable, unless otherwise annotated.
 * Interface signatures are not safe for implementation, unless otherwise annotated. This means that extensions SHOULD not directly implement interfaces from core. Instead, a base class (marked as stable for subclassing)  SHOULD be provided for supported extension use cases.
 * Global variables are not stable (including those with the "wg" prefix). MediaWikiServices SHOULD be used as an entry point that provides access to service objects and configuration instead.
 * Implicitly public methods and fields (those lacking explicit visibility modifiers) are not stable.
 * Any code annotated with ,  ,  , or   is not stable.

Stability Annotations
As described above, the following defaults apply:


 * Public methods of classes and interfaces are considered stable unless marked with,  , or  . They are however considered not fixed, meaning they are not safe to override.
 * Protected methods are considered unstable unless marked with  or.
 * Constructor signatures are considered unstable unless marked with  or.
 * Signatures of interfaces are considered not fixed unless the interface is marked with . Methods and optional arguments may be added without notice.
 * Similarly, the set of required abstract methods is considered unstable unless the class is marked with  (  does not suffice). Methods and optional arguments may be added without notice.

The following annotations can be used to depart from the defaults above,

to remove guarantees:


 *  : do not use outside the module, may be removed following the procedure defined by the deprecation policy. SHOULD always be accompanied by instructions of what to use instead.
 *  : do not use outside the module, subject to change without notice. Same as, except that internal typically stays internal.
 *  : do not use outside the module, subject to change without notice. Same as, except that unstable things are intended to become stable in the future.
 *  : same as.

to add guarantees:


 *  : Backwards compatibility will be maintained for code calling the method or function; changes that impact callers are subject to the deprecation policy. This applies to the function's or method's contract as well as its signature. This does not mean the method can safely be overridden.
 * On protected methods: safe to call for subclasses outside the module. Only useful if the class is.
 * On public methods, fields, and functions: discouraged since redundant. Public methods are considered stable by default, unless the entire class or interface is marked as  or.
 * On constructors: safe to call directly. SHOULD only be used on classes marked as   or  . Technically redundant for those cases, but encouraged for clarity. Constructors not marked as stable for calling are subject to change without notice.
 * On classes and interfaces: Discouraged, no defined meaning.
 *  : Backwards compatibility will be maintained for code overriding the method (or implementing an abstract method) in another module. Only very limited changes to the method's signature and contract are possible. All such changes are subject to the deprecation policy. Technically redundant on abstract methods of classes marked as stable for subclassing, but still encouraged for clarity.
 *  : Backwards compatibility will be maintained for subclassing; changes affecting subclasses will be done in a backwards compatible way. This implies that the constructor is stable for calling, and no abstract methods can be added. Note that methods can only be safely overridden if specifically marked as @stable for overriding, and protected methods are safe to call only if declared @stable for calling.
 *  : The interface is safe to be implemented by classes in another module. Interfaces not marked as such are not safe to implement directly, a base class SHOULD be used instead. On (abstract) methods, this means the same as @stable for overriding.
 *  : The class is safe to be instantiated by code in another module using the  operator. This implies that the constructor is stable for calling. Generally, only plain value objects  SHOULD be newable, since any other kind of object may require dependency injection.

Implications and Recommendations
This section provides an overview of the practical implications this policy has for authors of extensions as well as authors of core code. By following the guidance of the principles given here, developers on both sides of the module boundary can benefit from the guarantees provided by a stable interface.

Authors of code outside of MediaWiki core, in particular of extensions, should consider the following principles and recommendations to avoid incompatibility with future versions of MediaWiki:


 * It's generally safe for extensions to call public methods and access public fields in classes defined by MediaWiki core, unless these methods are documented to be unsafe (e.g. annotated with  or   or  ).
 * It's generally unsafe for extensions to extend (subclass) a class or implement an interface defined by MediaWiki core, unless that class or interface was designated to be safe for that purpose. In particular, the constructor signature may change, and abstract methods may be added. Thus, extensions SHOULD not implement interfaces, extend classes, or override methods, unless explicitly marked as stable for that the purpose.
 * It's generally unsafe for extensions to directly instantiate (using new) a class defined by MediaWiki core, unless that class is designated to be safe for this purpose. Thus, extensions SHOULD not directly create instances of classes that do not have the   annotation.
 * It's generally unsafe for extensions to rely on global variables. Static methods such as MediaWikiServices::getInstance SHOULD be used instead.

Authors of code in MediaWiki core, in particular code that defines extension points, SHOULD consider the following principles and recommendations, in order to provide extensions with a stable interface without removing flexibility from the code in core:


 * MediaWiki extension points SHOULD use base classes marked as @stable for subclassing. Any methods to be implemented or overwritten  SHOULD be explicitly marked as @stable for overriding to indicate that their signature will not change in an incompatible way.
 * Base classes are preferred over interfaces as extension points. MediaWiki extension points may also use PHP interfaces if these are annotated as @stable for implementation. This however means that no method can ever be added to them, since that would break any extension implementing the interface.
 * Pure value objects defined by MediaWiki SHOULD be marked with the   annotation if extensions would benefit from being able to construct such objects directly. The constructor of a @newable class  SHOULD be marked as @stable for calling.
 * For objects that extensions may need to instantiate, but which for which a stable constructor cannot be guaranteed, a factory service SHOULD be provided . This is especially true for objects that may in the future need to have a service instance injected.

Deprecation Process
All code that falls within the scope of this policy and defines a stable interface is subject to the following deprecation process:

Prior to deprecation
Deprecation is typically considered when code needs to be refactored in order to add new functionality, improve general architecture, or fix bugs. Developers SHOULD consider the impact of their proposed changes by doing basic greps on extensions or using tools like MediaWiki code search.

Extension developers are encouraged to mirror their code into Wikimedia's Gerrit/Phabricator/Github to make it easier for core developers to identify usage patterns. Extensions that are open source will be given more consideration than those that core developers cannot see.

Deprecations MUST first take place on the master branch. It is NOT RECOMMENDED to backport deprecations to stable branches.

Developers SHOULD consider deprecating similar parts of code together so affected code can be updated all at once.

Deprecation
There are two steps to deprecation: a soft deprecation, and then a hard deprecation.

A soft deprecation occurs when a developer adds a  annotation to the documentation comment for a method, function, or class.
 * 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.
 * If code is only soft deprecated, it SHOULD function the same as prior to deprecation. (This is only a SHOULD not MUST as sometimes it is only possible to provide similar functionality, which is enough for practical purposes, but technically not equal functionality.)
 * Any relevant documentation in the git repository or on mediawiki.org MUST be updated once the change is approved.
 * The deprecation 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. Deprecation of hooks MUST be mentioned in docs/hooks.txt.
 * Developers SHOULD update MediaWiki core to no longer use the deprecated functionality.
 * Developers SHOULD update any extension or skin bundled with the MediaWiki tarball when soft deprecating, and MAY update popular extensions (WikiApiary.com and ExtensionDistributor can be used as indicators of extension popularity).
 * If they don't submit patches, developers MUST file bugs about bundled extensions/skins using deprecated functions so their maintainers can work on updating them.

A hard deprecation occurs, when a  call is added to the function or method in question, or by supplying the $deprecatedVersion parameter to Hooks::run. This emits deprecation warnings and causes things like unit tests to fail.
 * Code that is hard deprecated MUST also be soft deprecated.
 * The version number in the  call MUST match the one in the @deprecated annotation, even if the hard deprecation occurs in a different release.
 * Often code is soft deprecated first, and hard deprecated at a later date, but they MAY occur at the same time.
 * Hard deprecated code MAY act as no-ops instead of actually functioning, though this is not recommended.
 * MediaWiki core code that triggers hard deprecation warnings MUST NOT be reachable from non-deprecated core code using non-deprecated configuration settings
 * Extensions and skins bundled with the MediaWiki tarball MUST NOT trigger hard deprecation warnings and MUST be updated to use the new code.


 * Developers MAY email wikitech-l or mediawiki-l about the soft or hard deprecation depending about severity.
 * When a bug report or a task is related to a deprecation, it is RECOMMENDED to tag it specifically in the bug tracker; for instance with the "Technical Debt" tag, column "Deprecate / Remove" in Phabricator.

Removal

 * Code MUST emit hard deprecation notices for at least one major MediaWiki version before being removed. It is RECOMMENDED to emit hard deprecation notices for at least two major MediaWiki versions. EXCEPTIONS to this are listed in the section "Removal without deprecation" below.
 * 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 statistics 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.

Caveat: As one of the principles of MediaWiki, developers should ensure any removals will not cause issues in the Wikimedia setup and extensions deployed there. If they do, developers should expect to be reverted by Wikimedia system administrators.

Removal without deprecation
In some cases, it is necessary to remove code without deprecating it in a major MediaWiki version beforehand. Some past examples include: In any case:
 * During the authentication rewrite in MediaWiki 1.27, serious architectural changes necessitated either complete removal of some code, or replacement of code in some functions with exception throws.
 * Developers MUST email wikitech-l ahead of time, explaining why deprecation is not possible or not reasonable.
 * All steps about documenting deprecations and removals MUST still be followed, as applicable.