Best practices for extensions

This page aims to lists best practices for to follow. Each item has a rating of how important it is. Ideally every extension should strive for the "gold standard".

This list was originally started at the 2017 Wikimania Hackathon. This document aligns to the language defined by RFC 2119. Here is what each word means in context:
 * MUST – Meeting these criteria likely means your extension will work, but it may not be sustainable or maintainable.
 * SHOULD – Meeting these criteria likely means your extension works well and will continue to work well in the future.
 * RECOMMENDED – Meeting these criteria means your extension is the ideal "gold standard" to which we all aspire, and should be used as an example to other developers.

Meta

 * MUST: Use the normal MediaWiki bug tracker / code review systems.
 * MUST: For deployment on Wikimedia wikis – performance & security review.
 * SHOULD: The extension should not be Wikimedia or organization specific.
 * SHOULD: Have co-maintainers! There should be two maintainers minimum. (Doesn't have to be Wikimedia Foundation staff.)
 * SHOULD: Create a MediaWiki-Vagrant role for your extension.

Architecture

 * SHOULD: Provides the same functionality over API and web (index.php).
 * RECOMMENDED: The functionality should be implemented in a way without duplication (e.g. shared backend service class with light API and UI bindings.)
 * SHOULD: Avoid global state (TODO: expand)
 * SHOULD: Use dependency injection, avoid static calls for other than utility methods or entry points.
 * SHOULD: Prefer service wiring over singletons.
 * SHOULD: Be easy to.
 * SHOULD: Use, with meaningful levels.
 * SHOULD: Throw exceptions only for situations it should not handle and need to bubble up.
 * SHOULD: Don't hardcode wikicode / templates or stuff, especially in a way that's not configurable for other websites.
 * SHOULD: Code should be readable by someone who is familiar in that area.
 * SHOULD: Don't call out to static functions in an unrelated class because it wasn't refactored or thought out well.
 * SHOULD: Has a clear separation of concerns between what it actually does, and how its presented to the user.
 * SHOULD: Think twice before adding new wikitext syntax functionality or something that will have a stable API for a very long time.
 * SHOULD: Skin and extension functionality should not be tightly integrated.
 * SHOULD: Don't add new user preferences, unless you really have a good reason to do so.
 * RECOMMENDED: Expose JavaScript methods for use by user scripts and gadgets, and to enable easy debugging from the console.

Documentation

 * MUST: Have an Extension:… page on mediawiki.org.
 * MUST: Add it to the appropriate extension categories.
 * MUST: Quick explanation of what it does.
 * MUST: Document available hooks under Extension:ExtensionName/Hooks/HookName using the "ExtensionHook" template (if any).
 * MUST: Complete list of any dependencies and how to install them, configure, and uninstall* (TODO: clarify uninstall)
 * SHOULD: Declare the compatibility policy of your extension.
 * SHOULD: All configuration settings described in one place, from most-used to most-obscure.
 * RECOMMENDED: Compare and contrast with similar extensions.
 * RECOMMENDED: Document used in the extension infobox, it's a nice method of exposing examples so that other developers can learn (they get automagically categorised).
 * RECOMMENDED: Instructions how to/information on limitations regarding uninstallation.
 * MUST: Having a Help:Extension:… page on mediawiki.org (if the extension has web-facing user interfaces).
 * RECOMMENDED: Documentation should have screenshots in multiple languages, and ideally include one in a right-to-left language.
 * RECOMMENDED: Documentation should discuss some edge cases that were tested – to prove due diligence of not just testing on simple/generic articles.
 * SHOULD: Consistently use, , , in documentation.
 * RECOMMENDED: Add/update the extension's page on WikiApiary (if needed).

TODO: Update Documentation/Style guide.

File structure
Overall, the extension's file layout should be organized: consistent naming, directory structure that is logical and not messy.
 * MUST: Using the following standard directory layout:
 * (when using PSR4 namespaced, preferred) or : Contains all (and only) PHP classes.
 * i18n files for specialpage alias, magic words or namespaces located in root folder.
 * SHOULD: Use PSR-4 structure for classes and files (note that as of MediaWiki 1.31 you can use in extension.json instead of listing all classes).
 * SHOULD: Classes in  namespace.   is permissible if the extension name is a sufficiently  unique word and not something generic (e.g. not a verb or noun).
 * SHOULD: One class per file.
 * (or ): Contains JavaScript and CSS for ResourceLoader.
 * command-line maintenance scripts
 * : Contains localized messages in JSON files.
 * : SQL files for database modifications (e.g. called by )
 * : Contains parser test files.
 * : Contains test cases.
 * : Contains test cases.
 * : Contains Selenium browser test files.
 * or : Contains full copy of the relevant license the code is released under.
 * SHALL NOT: Shall not have many files in the root level directory.
 * SHOULD: Avoid having dozens of nested directories that all only contain one or two things.
 * SHOULD: Avoid having very large files, or very many tiny files (but keep following one class per file pattern – many tiny classes may be a sign of something else going wrong).
 * SHOULD: A README file that summarizes the docs and gives detailed installation direction.
 * SHOULD: A README file that summarizes the docs and gives detailed installation direction.

Database

 * MUST: If adding database tables, it should use the hook to ensure update.php works.
 * MUST: Database access uses the Wikimedia-Rdbms library (database abstraction layer).
 * Doing so avoids most SQL injection attack vectors, and takes care of general performance and transaction best practices.
 * SHOULD: It works well in a distributed environment (concurrency, multiple databases, clustering).
 * SHOULD: Uninstallation maintenance script (dropping tables, removing added columns, deleting log entries, deleting page properties). (TODO: ???)
 * SHOULD: If it needs persistence, it creates nice SQL (primary keys, indexes where needed) and uses some caching mechanism where/if necessary.
 * SHOULD: Never add fields to the core tables or modify it in any way.
 * SHOULD: Create its own table for the fields with the same primary key as the core table. This makes it easier to remove a extension.

Coding conventions
Overall, follow the for, , , and any other languages that are in-use and have applicable code conventions.
 * SHOULD: Run MediaWiki-CodeSniffer to enforce PHP conventions (check CI Entry points).
 * SHOULD: Run Phan for PHP static analysis (check CI Entry points).
 * SHOULD: Run ESLint for JavaScript conventions and static analysis (check CI Entry points).
 * SHOULD: Run stylelint for CSS conventions.
 * SHOULD: Avoid writing all code into one large function (in JavaScript especially).
 * RECOMMENDED: Use code comments generally to document why the code exists, not what the code does. In long blocks of code, adding comments stating what each paragraph does is nice for easy parsing, but generally, comments should focus on the questions that can't be answered by just reading the code.

Testing

 * SHOULD: Have and run and/or  tests.
 * RECOMMENDED: Split out integration and unit tests (see T87781).
 * SHOULD: If there are parser functions or tags, have and run parser tests.
 * RECOMMENDED: Have and run browser tests.
 * RECOMMENDED: Test against right-to-left (RTL) languages! (how to verify?).
 * RECOMMENDED: Test against language converter languages! (how to verify?).

i18n & Accessibility
Overall, your extension should be fully usable and compatible with non-English and non-left-to-right languages.
 * SHALL NOT: Don't have hardcoded non-translatable strings in your code, use the proper localisation functions.
 * MUST: Using i18n and Translatewiki.net, and have a clear prefix to ease search of messages to translate.
 * MUST: Use HTML elements for their intended purpose. Aka use a and not a  with a click handler for accessibility.
 * SHOULD: Id's should only be for things you can navigate to. Everything else should be a class. Even if you want to only use it once, just use a unique class.
 * SHOULD: I18n escape as close to output as possible. Document whether functions take/except wikitext vs. HTML.
 * RECOMMENDED: Add qqq message strings for all messages that exist in en.json, and verify them using grunt-banana-checker.

Security

 * SHALL NOT: Don't touch HTML after it has been sanitized (common pattern is to use regex, but that's bad)
 * MUST: Shelling out should escape arguments.
 * MUST: All write actions must be protected against cross-site request forgery (CSRF).
 * SHOULD: Use the normal MediaWiki CSRF token system.
 * SHOULD: Don't load external resources for privacy and performance.
 * MUST: Make sure privacy related issues (checkuser, oversight) are still covered when refactoring or writing new code.

Don't reinvent / abuse MediaWiki
Overall, don't re-implement functionality or code that MediaWiki provides.
 * MUST: Use MediaWiki functionality/wrappers for things like vs. , etc.
 * MUST: Use where possible as opposed to workarounds or novel ways of modifying, injecting, or extending functionality.
 * MUST Use MediaWiki's validation/sanitization methods e.g. those in the and  classes.
 * MUST: Don't disable parser cache unless you have a really good reason.
 * MUST: Use Composer for 3rd party PHP library management.
 * SHOULD: Don't reimplement the wheel. Prefer stable and well-maintained libraries when they exist.
 * SHOULD: Don't disable.
 * SHOULD: If an abstraction exists (e.g. ), use that instead of hooks.
 * SHOULD: Don't make things harder for yourself – use standard functionality like extension.json's tests/PHPUnit auto-discovery stuff.
 * SHOULD: Use global MediaWiki configuration such as read-only mode.

Uncategorized

 * Can I read the code with reasonable effort? Is it convoluted / unnecessarily complex?
 * Know when you should use ParserOutput methods vs. similar methods on OutputPage.
 * It's okay to create duplicate bug reports (it's better than not reporting it at all!)
 * Should extensions be adding user groups in their default configuration?
 * Adding user rights is easy in code, but can be politically controversial and should be discussed with the community, especially who can grant the rights, and who has the rights by default.

TODO: Check on Extension:BoilerPlate – T97105 ("BoilerPlate extension should show latest coding practices")