CINXE.COM

Best practices for extensions - MediaWiki

<!DOCTYPE html> <html class="client-nojs vector-feature-language-in-header-disabled vector-feature-language-in-main-page-header-disabled vector-feature-sticky-header-disabled vector-feature-page-tools-pinned-disabled vector-feature-toc-pinned-clientpref-1 vector-feature-main-menu-pinned-disabled vector-feature-limited-width-clientpref-1 vector-feature-limited-width-content-enabled vector-feature-custom-font-size-clientpref-1 vector-feature-appearance-pinned-clientpref-1 vector-feature-night-mode-disabled skin-theme-clientpref-day vector-toc-available" lang="en" dir="ltr"> <head> <meta charset="UTF-8"> <title>Best practices for extensions - MediaWiki</title> <script>(function(){var className="client-js vector-feature-language-in-header-disabled vector-feature-language-in-main-page-header-disabled vector-feature-sticky-header-disabled vector-feature-page-tools-pinned-disabled vector-feature-toc-pinned-clientpref-1 vector-feature-main-menu-pinned-disabled vector-feature-limited-width-clientpref-1 vector-feature-limited-width-content-enabled vector-feature-custom-font-size-clientpref-1 vector-feature-appearance-pinned-clientpref-1 vector-feature-night-mode-disabled skin-theme-clientpref-day vector-toc-available";var cookie=document.cookie.match(/(?:^|; )mediawikiwikimwclientpreferences=([^;]+)/);if(cookie){cookie[1].split('%2C').forEach(function(pref){className=className.replace(new RegExp('(^| )'+pref.replace(/-clientpref-\w+$|[^\w-]+/g,'')+'-clientpref-\\w+( |$)'),'$1'+pref+'$2');});}document.documentElement.className=className;}());RLCONF={"wgBreakFrames":false,"wgSeparatorTransformTable":["",""],"wgDigitTransformTable":["",""], "wgDefaultDateFormat":"dmy","wgMonthNames":["","January","February","March","April","May","June","July","August","September","October","November","December"],"wgRequestId":"0b0be8af-f2b8-48b7-b851-d43584626e2d","wgCanonicalNamespace":"","wgCanonicalSpecialPageName":false,"wgNamespaceNumber":0,"wgPageName":"Best_practices_for_extensions","wgTitle":"Best practices for extensions","wgCurRevisionId":6471904,"wgRevisionId":6471904,"wgArticleId":708355,"wgIsArticle":true,"wgIsRedirect":false,"wgAction":"view","wgUserName":null,"wgUserGroups":["*"],"wgCategories":["Development guidelines"],"wgPageViewLanguage":"en","wgPageContentLanguage":"en","wgPageContentModel":"wikitext","wgRelevantPageName":"Best_practices_for_extensions","wgRelevantArticleId":708355,"wgIsProbablyEditable":true,"wgRelevantPageIsProbablyEditable":true,"wgRestrictionEdit":[],"wgRestrictionMove":[],"wgNoticeProject":"mediawiki","wgCiteReferencePreviewsActive":true,"wgMediaViewerOnClick":true,"wgMediaViewerEnabledByDefault": true,"wgVisualEditor":{"pageLanguageCode":"en","pageLanguageDir":"ltr","pageVariantFallbacks":"en"},"wgMFDisplayWikibaseDescriptions":{"search":true,"watchlist":true,"tagline":false,"nearby":true},"wgWMESchemaEditAttemptStepOversample":false,"wgWMEPageLength":20000,"wgInternalRedirectTargetUrl":"/wiki/Best_practices_for_extensions","wgTranslatePageTranslation":"source","wgCentralAuthMobileDomain":false,"wgEditSubmitButtonLabelPublish":true,"wgDiscussionToolsFeaturesEnabled":{"replytool":true,"newtopictool":true,"sourcemodetoolbar":true,"topicsubscription":false,"autotopicsub":false,"visualenhancements":false,"visualenhancements_reply":false,"visualenhancements_pageframe":false},"wgDiscussionToolsFallbackEditMode":"visual","wgULSPosition":"personal","wgULSisCompactLinksEnabled":true,"wgVector2022LanguageInHeader":false,"wgULSisLanguageSelectorEmpty":false,"wgCheckUserClientHintsHeadersJsApi":["brands","architecture","bitness","fullVersionList","mobile","model","platform", "platformVersion"]};RLSTATE={"ext.globalCssJs.user.styles":"ready","site.styles":"ready","user.styles":"ready","ext.globalCssJs.user":"ready","user":"ready","user.options":"loading","ext.translate.tag.languages":"ready","ext.discussionTools.init.styles":"ready","oojs-ui-core.styles":"ready","oojs-ui.styles.indicators":"ready","mediawiki.widgets.styles":"ready","oojs-ui-core.icons":"ready","skins.vector.search.codex.styles":"ready","skins.vector.styles":"ready","skins.vector.icons":"ready","ext.translate.edit.documentation.styles":"ready","ext.translate":"ready","ext.wikimediamessages.styles":"ready","ext.visualEditor.desktopArticleTarget.noscript":"ready","ext.uls.pt":"ready","wikibase.client.init":"ready","ext.wikimediaBadges":"ready"};RLPAGEMODULES=["mediawiki.action.view.redirect","site","mediawiki.page.ready","mediawiki.toc","skins.vector.js","ext.centralNotice.geoIP","ext.centralNotice.startUp","ext.translate.pagetranslation.uls","ext.urlShortener.toolbar", "ext.centralauth.centralautologin","mmv.bootstrap","ext.visualEditor.desktopArticleTarget.init","ext.visualEditor.targetLoader","ext.echo.centralauth","ext.discussionTools.init","ext.eventLogging","ext.wikimediaEvents","ext.navigationTiming","ext.uls.compactlinks","ext.uls.interface","ext.checkUser.clientHints"];</script> <script>(RLQ=window.RLQ||[]).push(function(){mw.loader.impl(function(){return["user.options@12s5i",function($,jQuery,require,module){mw.user.tokens.set({"patrolToken":"+\\","watchToken":"+\\","csrfToken":"+\\"}); }];});});</script> <link rel="stylesheet" href="/w/load.php?lang=en&amp;modules=ext.discussionTools.init.styles%7Cext.translate%2CwikimediaBadges%7Cext.translate.edit.documentation.styles%7Cext.translate.tag.languages%7Cext.uls.pt%7Cext.visualEditor.desktopArticleTarget.noscript%7Cext.wikimediamessages.styles%7Cmediawiki.widgets.styles%7Coojs-ui-core.icons%2Cstyles%7Coojs-ui.styles.indicators%7Cskins.vector.icons%2Cstyles%7Cskins.vector.search.codex.styles%7Cwikibase.client.init&amp;only=styles&amp;skin=vector-2022"> <script async="" src="/w/load.php?lang=en&amp;modules=startup&amp;only=scripts&amp;raw=1&amp;skin=vector-2022"></script> <meta name="ResourceLoaderDynamicStyles" content=""> <link rel="stylesheet" href="/w/load.php?lang=en&amp;modules=site.styles&amp;only=styles&amp;skin=vector-2022"> <meta name="generator" content="MediaWiki 1.44.0-wmf.4"> <meta name="referrer" content="origin"> <meta name="referrer" content="origin-when-cross-origin"> <meta name="robots" content="max-image-preview:standard"> <meta name="format-detection" content="telephone=no"> <meta name="viewport" content="width=1120"> <meta property="og:site_name" content="MediaWiki"> <meta property="og:title" content="Best practices for extensions - MediaWiki"> <meta property="og:type" content="website"> <link rel="preconnect" href="//upload.wikimedia.org"> <link rel="alternate" media="only screen and (max-width: 640px)" href="//m.mediawiki.org/wiki/Best_practices_for_extensions"> <link rel="alternate" type="application/x-wiki" title="Edit" href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit"> <link rel="apple-touch-icon" href="/static/apple-touch/mediawiki.png"> <link rel="icon" href="/static/favicon/mediawiki.ico"> <link rel="search" type="application/opensearchdescription+xml" href="/w/rest.php/v1/search" title="MediaWiki (en)"> <link rel="EditURI" type="application/rsd+xml" href="//www.mediawiki.org/w/api.php?action=rsd"> <link rel="canonical" href="https://www.mediawiki.org/wiki/Best_practices_for_extensions"> <link rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/"> <link rel="alternate" type="application/atom+xml" title="MediaWiki Atom feed" href="/w/index.php?title=Special:RecentChanges&amp;feed=atom"> <link rel="dns-prefetch" href="//meta.wikimedia.org" /> <link rel="dns-prefetch" href="//login.wikimedia.org"> </head> <body class="ext-discussiontools-replytool-enabled ext-discussiontools-newtopictool-enabled ext-discussiontools-sourcemodetoolbar-enabled skin--responsive skin-vector skin-vector-search-vue mediawiki ltr sitedir-ltr mw-hide-empty-elt ns-0 ns-subject mw-editable page-Best_practices_for_extensions rootpage-Best_practices_for_extensions skin-vector-2022 action-view"><a class="mw-jump-link" href="#bodyContent">Jump to content</a> <div class="vector-header-container"> <header class="vector-header mw-header"> <div class="vector-header-start"> <nav class="vector-main-menu-landmark" aria-label="Site"> <div id="vector-main-menu-dropdown" class="vector-dropdown vector-main-menu-dropdown vector-button-flush-left vector-button-flush-right" > <input type="checkbox" id="vector-main-menu-dropdown-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-main-menu-dropdown" class="vector-dropdown-checkbox " aria-label="Main menu" > <label id="vector-main-menu-dropdown-label" for="vector-main-menu-dropdown-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet cdx-button--icon-only " aria-hidden="true" ><span class="vector-icon mw-ui-icon-menu mw-ui-icon-wikimedia-menu"></span> <span class="vector-dropdown-label-text">Main menu</span> </label> <div class="vector-dropdown-content"> <div id="vector-main-menu-unpinned-container" class="vector-unpinned-container"> <div id="vector-main-menu" class="vector-main-menu vector-pinnable-element"> <div class="vector-pinnable-header vector-main-menu-pinnable-header vector-pinnable-header-unpinned" data-feature-name="main-menu-pinned" data-pinnable-element-id="vector-main-menu" data-pinned-container-id="vector-main-menu-pinned-container" data-unpinned-container-id="vector-main-menu-unpinned-container" > <div class="vector-pinnable-header-label">Main menu</div> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-pin-button" data-event-name="pinnable-header.vector-main-menu.pin">move to sidebar</button> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-unpin-button" data-event-name="pinnable-header.vector-main-menu.unpin">hide</button> </div> <div id="p-navigation" class="vector-menu mw-portlet mw-portlet-navigation" > <div class="vector-menu-heading"> Navigation </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="n-mainpage-description" class="mw-list-item"><a href="/wiki/MediaWiki" title="Visit the main page [z]" accesskey="z"><span>Main page</span></a></li><li id="n-mw-download" class="mw-list-item"><a href="/wiki/Download"><span>Get MediaWiki</span></a></li><li id="n-mw-extensions" class="mw-list-item"><a href="/wiki/Special:MyLanguage/Category:Extensions"><span>Get extensions</span></a></li><li id="n-blog-text" class="mw-list-item"><a href="https://techblog.wikimedia.org/"><span>Tech blog</span></a></li><li id="n-mw-contribute" class="mw-list-item"><a href="/wiki/Special:MyLanguage/How_to_contribute"><span>Contribute</span></a></li> </ul> </div> </div> <div id="p-support" class="vector-menu mw-portlet mw-portlet-support" > <div class="vector-menu-heading"> Support </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="n-help" class="mw-list-item"><a href="/wiki/Special:MyLanguage/Help:Contents" title="The place to find out"><span>User help</span></a></li><li id="n-mw-faq" class="mw-list-item"><a href="/wiki/Special:MyLanguage/Manual:FAQ"><span>FAQ</span></a></li><li id="n-mw-manual" class="mw-list-item"><a href="/wiki/Special:MyLanguage/Manual:Contents"><span>Technical manual</span></a></li><li id="n-mw-supportdesk" class="mw-list-item"><a href="/wiki/Project:Support_desk"><span>Support desk</span></a></li><li id="n-mw-communication" class="mw-list-item"><a href="/wiki/Special:MyLanguage/Communication"><span>Communication</span></a></li> </ul> </div> </div> <div id="p-development" class="vector-menu mw-portlet mw-portlet-development" > <div class="vector-menu-heading"> Development </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="n-mw-developerportal" class="mw-list-item"><a href="https://developer.wikimedia.org/"><span>Developer portal</span></a></li><li id="n-svn-statistics" class="mw-list-item"><a href="/wiki/Development_statistics"><span>Code statistics</span></a></li> </ul> </div> </div> <div id="p-mediawiki.org" class="vector-menu mw-portlet mw-portlet-mediawiki_org" > <div class="vector-menu-heading"> mediawiki.org </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="n-portal" class="mw-list-item"><a href="/wiki/Project:Help" title="About the project, what you can do, where to find things"><span>Community portal</span></a></li><li id="n-recentchanges" class="mw-list-item"><a href="/wiki/Special:RecentChanges" title="A list of recent changes in the wiki [r]" accesskey="r"><span>Recent changes</span></a></li><li id="n-mw-translate" class="mw-list-item"><a href="/wiki/Special:LanguageStats"><span>Translate content</span></a></li><li id="n-randompage" class="mw-list-item"><a href="/wiki/Special:Random" title="Load a random page [x]" accesskey="x"><span>Random page</span></a></li><li id="n-mw-discussion" class="mw-list-item"><a href="/wiki/Project:Village_Pump"><span>Village pump</span></a></li><li id="n-Sandboxlink-portlet-label" class="mw-list-item"><a href="/wiki/Project:Sandbox"><span>Sandbox</span></a></li> </ul> </div> </div> <div id="p-lang" class="vector-menu mw-portlet mw-portlet-lang" > <div class="vector-menu-heading"> In other languages </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> </ul> <div class="after-portlet after-portlet-lang"><span class="wb-langlinks-add wb-langlinks-link"><a href="https://www.wikidata.org/wiki/Special:NewItem?site=mediawikiwiki&amp;page=Best+practices+for+extensions" title="Add interlanguage links" class="wbc-editpage">Add links</a></span></div> </div> </div> </div> </div> </div> </div> </nav> <a href="/wiki/MediaWiki" class="mw-logo"> <img class="mw-logo-icon" src="/static/images/icons/mediawikiwiki.svg" alt="" aria-hidden="true" height="50" width="50"> <span class="mw-logo-container skin-invert"> <img class="mw-logo-wordmark" alt="MediaWiki" src="/static/images/mobile/copyright/mediawikiwiki-wordmark.svg" style="width: 7.5em; height: 1.125em;"> </span> </a> </div> <div class="vector-header-end"> <div id="p-search" role="search" class="vector-search-box-vue vector-search-box-collapses vector-search-box-show-thumbnail vector-search-box-auto-expand-width vector-search-box"> <a href="/wiki/Special:Search" class="cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet cdx-button--icon-only search-toggle" title="Search MediaWiki [f]" accesskey="f"><span class="vector-icon mw-ui-icon-search mw-ui-icon-wikimedia-search"></span> <span>Search</span> </a> <div class="vector-typeahead-search-container"> <div class="cdx-typeahead-search cdx-typeahead-search--show-thumbnail cdx-typeahead-search--auto-expand-width"> <form action="/w/index.php" id="searchform" class="cdx-search-input cdx-search-input--has-end-button"> <div id="simpleSearch" class="cdx-search-input__input-wrapper" data-search-loc="header-moved"> <div class="cdx-text-input cdx-text-input--has-start-icon"> <input class="cdx-text-input__input" type="search" name="search" placeholder="Search MediaWiki" aria-label="Search MediaWiki" autocapitalize="sentences" title="Search MediaWiki [f]" accesskey="f" id="searchInput" > <span class="cdx-text-input__icon cdx-text-input__start-icon"></span> </div> <input type="hidden" name="title" value="Special:Search"> </div> <button class="cdx-button cdx-search-input__end-button">Search</button> </form> </div> </div> </div> <nav class="vector-user-links vector-user-links-wide" aria-label="Personal tools"> <div class="vector-user-links-main"> <div id="p-vector-user-menu-preferences" class="vector-menu mw-portlet" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="ca-uls" class="mw-list-item active user-links-collapsible-item"><a data-mw="interface" href="#" class="uls-trigger cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet"><span class="vector-icon mw-ui-icon-wikimedia-language mw-ui-icon-wikimedia-wikimedia-language"></span> <span>English</span></a> </li> </ul> </div> </div> <div id="p-vector-user-menu-userpage" class="vector-menu mw-portlet emptyPortlet" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> </ul> </div> </div> <nav class="vector-appearance-landmark" aria-label="Appearance"> <div id="vector-appearance-dropdown" class="vector-dropdown " title="Change the appearance of the page&#039;s font size, width, and color" > <input type="checkbox" id="vector-appearance-dropdown-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-appearance-dropdown" class="vector-dropdown-checkbox " aria-label="Appearance" > <label id="vector-appearance-dropdown-label" for="vector-appearance-dropdown-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet cdx-button--icon-only " aria-hidden="true" ><span class="vector-icon mw-ui-icon-appearance mw-ui-icon-wikimedia-appearance"></span> <span class="vector-dropdown-label-text">Appearance</span> </label> <div class="vector-dropdown-content"> <div id="vector-appearance-unpinned-container" class="vector-unpinned-container"> </div> </div> </div> </nav> <div id="p-vector-user-menu-notifications" class="vector-menu mw-portlet emptyPortlet" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> </ul> </div> </div> <div id="p-vector-user-menu-overflow" class="vector-menu mw-portlet" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="pt-sitesupport-2" class="user-links-collapsible-item mw-list-item user-links-collapsible-item"><a data-mw="interface" href="https://donate.wikimedia.org/?utm_source=donate&amp;utm_medium=sidebar&amp;utm_campaign=spontaneous&amp;uselang=en" class=""><span>Donate</span></a> </li> <li id="pt-createaccount-2" class="user-links-collapsible-item mw-list-item user-links-collapsible-item"><a data-mw="interface" href="/w/index.php?title=Special:CreateAccount&amp;returnto=Best+practices+for+extensions" title="You are encouraged to create an account and log in; however, it is not mandatory" class=""><span>Create account</span></a> </li> <li id="pt-login-2" class="user-links-collapsible-item mw-list-item user-links-collapsible-item"><a data-mw="interface" href="/w/index.php?title=Special:UserLogin&amp;returnto=Best+practices+for+extensions" title="You are encouraged to log in; however, it is not mandatory [o]" accesskey="o" class=""><span>Log in</span></a> </li> </ul> </div> </div> </div> <div id="vector-user-links-dropdown" class="vector-dropdown vector-user-menu vector-button-flush-right vector-user-menu-logged-out" title="More options" > <input type="checkbox" id="vector-user-links-dropdown-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-user-links-dropdown" class="vector-dropdown-checkbox " aria-label="Personal tools" > <label id="vector-user-links-dropdown-label" for="vector-user-links-dropdown-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet cdx-button--icon-only " aria-hidden="true" ><span class="vector-icon mw-ui-icon-ellipsis mw-ui-icon-wikimedia-ellipsis"></span> <span class="vector-dropdown-label-text">Personal tools</span> </label> <div class="vector-dropdown-content"> <div id="p-personal" class="vector-menu mw-portlet mw-portlet-personal user-links-collapsible-item" title="User menu" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="pt-sitesupport" class="user-links-collapsible-item mw-list-item"><a href="https://donate.wikimedia.org/?utm_source=donate&amp;utm_medium=sidebar&amp;utm_campaign=spontaneous&amp;uselang=en"><span>Donate</span></a></li><li id="pt-createaccount" class="user-links-collapsible-item mw-list-item"><a href="/w/index.php?title=Special:CreateAccount&amp;returnto=Best+practices+for+extensions" title="You are encouraged to create an account and log in; however, it is not mandatory"><span class="vector-icon mw-ui-icon-userAdd mw-ui-icon-wikimedia-userAdd"></span> <span>Create account</span></a></li><li id="pt-login" class="user-links-collapsible-item mw-list-item"><a href="/w/index.php?title=Special:UserLogin&amp;returnto=Best+practices+for+extensions" title="You are encouraged to log in; however, it is not mandatory [o]" accesskey="o"><span class="vector-icon mw-ui-icon-logIn mw-ui-icon-wikimedia-logIn"></span> <span>Log in</span></a></li> </ul> </div> </div> <div id="p-user-menu-anon-editor" class="vector-menu mw-portlet mw-portlet-user-menu-anon-editor" > <div class="vector-menu-heading"> Pages for logged out editors <a href="/wiki/Help:Introduction" aria-label="Learn more about editing"><span>learn more</span></a> </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="pt-anoncontribs" class="mw-list-item"><a href="/wiki/Special:MyContributions" title="A list of edits made from this IP address [y]" accesskey="y"><span>Contributions</span></a></li><li id="pt-anontalk" class="mw-list-item"><a href="/wiki/Special:MyTalk" title="Discussion about edits from this IP address [n]" accesskey="n"><span>Talk</span></a></li> </ul> </div> </div> </div> </div> </nav> </div> </header> </div> <div class="mw-page-container"> <div class="mw-page-container-inner"> <div class="vector-sitenotice-container"> <div id="siteNotice"><!-- CentralNotice --></div> </div> <div class="vector-column-start"> <div class="vector-main-menu-container"> <div id="mw-navigation"> <nav id="mw-panel" class="vector-main-menu-landmark" aria-label="Site"> <div id="vector-main-menu-pinned-container" class="vector-pinned-container"> </div> </nav> </div> </div> <div class="vector-sticky-pinned-container"> <nav id="mw-panel-toc" aria-label="Contents" data-event-name="ui.sidebar-toc" class="mw-table-of-contents-container vector-toc-landmark"> <div id="vector-toc-pinned-container" class="vector-pinned-container"> <div id="vector-toc" class="vector-toc vector-pinnable-element"> <div class="vector-pinnable-header vector-toc-pinnable-header vector-pinnable-header-pinned" data-feature-name="toc-pinned" data-pinnable-element-id="vector-toc" > <h2 class="vector-pinnable-header-label">Contents</h2> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-pin-button" data-event-name="pinnable-header.vector-toc.pin">move to sidebar</button> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-unpin-button" data-event-name="pinnable-header.vector-toc.unpin">hide</button> </div> <ul class="vector-toc-contents" id="mw-panel-toc-list"> <li id="toc-mw-content-text" class="vector-toc-list-item vector-toc-level-1"> <a href="#" class="vector-toc-link"> <div class="vector-toc-text">Beginning</div> </a> </li> <li id="toc-Workflow" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Workflow"> <div class="vector-toc-text"> <span class="vector-toc-numb">1</span> <span>Workflow</span> </div> </a> <ul id="toc-Workflow-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Code_architecture" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Code_architecture"> <div class="vector-toc-text"> <span class="vector-toc-numb">2</span> <span>Code architecture</span> </div> </a> <ul id="toc-Code_architecture-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Documentation" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Documentation"> <div class="vector-toc-text"> <span class="vector-toc-numb">3</span> <span>Documentation</span> </div> </a> <ul id="toc-Documentation-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-File_structure" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#File_structure"> <div class="vector-toc-text"> <span class="vector-toc-numb">4</span> <span>File structure</span> </div> </a> <ul id="toc-File_structure-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Database" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Database"> <div class="vector-toc-text"> <span class="vector-toc-numb">5</span> <span>Database</span> </div> </a> <ul id="toc-Database-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Coding_conventions" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Coding_conventions"> <div class="vector-toc-text"> <span class="vector-toc-numb">6</span> <span>Coding conventions</span> </div> </a> <ul id="toc-Coding_conventions-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Testing" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Testing"> <div class="vector-toc-text"> <span class="vector-toc-numb">7</span> <span>Testing</span> </div> </a> <ul id="toc-Testing-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Language" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Language"> <div class="vector-toc-text"> <span class="vector-toc-numb">8</span> <span>Language</span> </div> </a> <ul id="toc-Language-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Accessibility" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Accessibility"> <div class="vector-toc-text"> <span class="vector-toc-numb">9</span> <span>Accessibility</span> </div> </a> <ul id="toc-Accessibility-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Security" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Security"> <div class="vector-toc-text"> <span class="vector-toc-numb">10</span> <span>Security</span> </div> </a> <ul id="toc-Security-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Don&#039;t_reinvent_/_abuse_MediaWiki" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Don&#039;t_reinvent_/_abuse_MediaWiki"> <div class="vector-toc-text"> <span class="vector-toc-numb">11</span> <span>Don't reinvent / abuse MediaWiki</span> </div> </a> <ul id="toc-Don&#039;t_reinvent_/_abuse_MediaWiki-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Uncategorized" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Uncategorized"> <div class="vector-toc-text"> <span class="vector-toc-numb">12</span> <span>Uncategorized</span> </div> </a> <ul id="toc-Uncategorized-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-Meta" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#Meta"> <div class="vector-toc-text"> <span class="vector-toc-numb">13</span> <span>Meta</span> </div> </a> <ul id="toc-Meta-sublist" class="vector-toc-list"> </ul> </li> <li id="toc-See_also" class="vector-toc-list-item vector-toc-level-1 vector-toc-list-item-expanded"> <a class="vector-toc-link" href="#See_also"> <div class="vector-toc-text"> <span class="vector-toc-numb">14</span> <span>See also</span> </div> </a> <ul id="toc-See_also-sublist" class="vector-toc-list"> </ul> </li> </ul> </div> </div> </nav> </div> </div> <div class="mw-content-container"> <main id="content" class="mw-body"> <header class="mw-body-header vector-page-titlebar"> <nav aria-label="Contents" class="vector-toc-landmark"> <div id="vector-page-titlebar-toc" class="vector-dropdown vector-page-titlebar-toc vector-button-flush-left" > <input type="checkbox" id="vector-page-titlebar-toc-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-page-titlebar-toc" class="vector-dropdown-checkbox " aria-label="Toggle the table of contents" > <label id="vector-page-titlebar-toc-label" for="vector-page-titlebar-toc-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet cdx-button--icon-only " aria-hidden="true" ><span class="vector-icon mw-ui-icon-listBullet mw-ui-icon-wikimedia-listBullet"></span> <span class="vector-dropdown-label-text">Toggle the table of contents</span> </label> <div class="vector-dropdown-content"> <div id="vector-page-titlebar-toc-unpinned-container" class="vector-unpinned-container"> </div> </div> </div> </nav> <h1 id="firstHeading" class="firstHeading mw-first-heading"><span class="mw-page-title-main">Best practices for extensions</span></h1> <div class="mw-indicators"> </div> </header> <div class="vector-page-toolbar"> <div class="vector-page-toolbar-container"> <div id="left-navigation"> <nav aria-label="Namespaces"> <div id="p-associated-pages" class="vector-menu vector-menu-tabs mw-portlet mw-portlet-associated-pages" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="ca-nstab-main" class="selected vector-tab-noicon mw-list-item"><a href="/wiki/Best_practices_for_extensions" title="View the content page [c]" accesskey="c"><span>Page</span></a></li><li id="ca-talk" class="vector-tab-noicon mw-list-item"><a href="/wiki/Talk:Best_practices_for_extensions" rel="discussion" title="Discussion about the content page [t]" accesskey="t"><span>Discussion</span></a></li> </ul> </div> </div> <div id="vector-variants-dropdown" class="vector-dropdown emptyPortlet" > <input type="checkbox" id="vector-variants-dropdown-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-variants-dropdown" class="vector-dropdown-checkbox " aria-label="Change language variant" > <label id="vector-variants-dropdown-label" for="vector-variants-dropdown-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet" aria-hidden="true" ><span class="vector-dropdown-label-text">English</span> </label> <div class="vector-dropdown-content"> <div id="p-variants" class="vector-menu mw-portlet mw-portlet-variants emptyPortlet" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> </ul> </div> </div> </div> </div> </nav> </div> <div id="right-navigation" class="vector-collapsible"> <nav aria-label="Views"> <div id="p-views" class="vector-menu vector-menu-tabs mw-portlet mw-portlet-views" > <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="ca-view" class="selected vector-tab-noicon mw-list-item"><a href="/wiki/Best_practices_for_extensions"><span>Read</span></a></li><li id="ca-edit" class="vector-tab-noicon mw-list-item"><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit" title="Edit the source code of this page [e]" accesskey="e"><span>Edit</span></a></li><li id="ca-history" class="vector-tab-noicon mw-list-item"><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=history" title="Past revisions of this page [h]" accesskey="h"><span>View history</span></a></li> </ul> </div> </div> </nav> <nav class="vector-page-tools-landmark" aria-label="Page tools"> <div id="vector-page-tools-dropdown" class="vector-dropdown vector-page-tools-dropdown" > <input type="checkbox" id="vector-page-tools-dropdown-checkbox" role="button" aria-haspopup="true" data-event-name="ui.dropdown-vector-page-tools-dropdown" class="vector-dropdown-checkbox " aria-label="Tools" > <label id="vector-page-tools-dropdown-label" for="vector-page-tools-dropdown-checkbox" class="vector-dropdown-label cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--weight-quiet" aria-hidden="true" ><span class="vector-dropdown-label-text">Tools</span> </label> <div class="vector-dropdown-content"> <div id="vector-page-tools-unpinned-container" class="vector-unpinned-container"> <div id="vector-page-tools" class="vector-page-tools vector-pinnable-element"> <div class="vector-pinnable-header vector-page-tools-pinnable-header vector-pinnable-header-unpinned" data-feature-name="page-tools-pinned" data-pinnable-element-id="vector-page-tools" data-pinned-container-id="vector-page-tools-pinned-container" data-unpinned-container-id="vector-page-tools-unpinned-container" > <div class="vector-pinnable-header-label">Tools</div> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-pin-button" data-event-name="pinnable-header.vector-page-tools.pin">move to sidebar</button> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-unpin-button" data-event-name="pinnable-header.vector-page-tools.unpin">hide</button> </div> <div id="p-cactions" class="vector-menu mw-portlet mw-portlet-cactions emptyPortlet vector-has-collapsible-items" title="More options" > <div class="vector-menu-heading"> Actions </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="ca-more-view" class="selected vector-more-collapsible-item mw-list-item"><a href="/wiki/Best_practices_for_extensions"><span>Read</span></a></li><li id="ca-more-edit" class="vector-more-collapsible-item mw-list-item"><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit" title="Edit the source code of this page [e]" accesskey="e"><span>Edit</span></a></li><li id="ca-more-history" class="vector-more-collapsible-item mw-list-item"><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=history"><span>View history</span></a></li> </ul> </div> </div> <div id="p-tb" class="vector-menu mw-portlet mw-portlet-tb" > <div class="vector-menu-heading"> General </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="t-whatlinkshere" class="mw-list-item"><a href="/wiki/Special:WhatLinksHere/Best_practices_for_extensions" title="A list of all wiki pages that link here [j]" accesskey="j"><span>What links here</span></a></li><li id="t-recentchangeslinked" class="mw-list-item"><a href="/wiki/Special:RecentChangesLinked/Best_practices_for_extensions" rel="nofollow" title="Recent changes in pages linked from this page [k]" accesskey="k"><span>Related changes</span></a></li><li id="t-upload" class="mw-list-item"><a href="//commons.wikimedia.org/wiki/Special:UploadWizard" title="Upload files [u]" accesskey="u"><span>Upload file</span></a></li><li id="t-specialpages" class="mw-list-item"><a href="/wiki/Special:SpecialPages" title="A list of all special pages [q]" accesskey="q"><span>Special pages</span></a></li><li id="t-permalink" class="mw-list-item"><a href="/w/index.php?title=Best_practices_for_extensions&amp;oldid=6471904" title="Permanent link to this revision of this page"><span>Permanent link</span></a></li><li id="t-info" class="mw-list-item"><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=info" title="More information about this page"><span>Page information</span></a></li><li id="t-cite" class="mw-list-item"><a href="/w/index.php?title=Special:CiteThisPage&amp;page=Best_practices_for_extensions&amp;id=6471904&amp;wpFormIdentifier=titleform" title="Information on how to cite this page"><span>Cite this page</span></a></li><li id="t-urlshortener" class="mw-list-item"><a href="/w/index.php?title=Special:UrlShortener&amp;url=https%3A%2F%2Fwww.mediawiki.org%2Fwiki%2FBest_practices_for_extensions"><span>Get shortened URL</span></a></li><li id="t-urlshortener-qrcode" class="mw-list-item"><a href="/w/index.php?title=Special:QrCode&amp;url=https%3A%2F%2Fwww.mediawiki.org%2Fwiki%2FBest_practices_for_extensions"><span>Download QR code</span></a></li> </ul> </div> </div> <div id="p-coll-print_export" class="vector-menu mw-portlet mw-portlet-coll-print_export" > <div class="vector-menu-heading"> Print/export </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> <li id="coll-create_a_book" class="mw-list-item"><a href="/w/index.php?title=Special:Book&amp;bookcmd=book_creator&amp;referer=Best+practices+for+extensions"><span>Create a book</span></a></li><li id="coll-download-as-rl" class="mw-list-item"><a href="/w/index.php?title=Special:DownloadAsPdf&amp;page=Best_practices_for_extensions&amp;action=show-download-screen"><span>Download as PDF</span></a></li><li id="t-print" class="mw-list-item"><a href="/w/index.php?title=Best_practices_for_extensions&amp;printable=yes" title="Printable version of this page [p]" accesskey="p"><span>Printable version</span></a></li> </ul> </div> </div> <div id="p-wikibase-otherprojects" class="vector-menu mw-portlet mw-portlet-wikibase-otherprojects emptyPortlet" > <div class="vector-menu-heading"> In other projects </div> <div class="vector-menu-content"> <ul class="vector-menu-content-list"> </ul> </div> </div> </div> </div> </div> </div> </nav> </div> </div> </div> <div class="vector-column-end"> <div class="vector-sticky-pinned-container"> <nav class="vector-page-tools-landmark" aria-label="Page tools"> <div id="vector-page-tools-pinned-container" class="vector-pinned-container"> </div> </nav> <nav class="vector-appearance-landmark" aria-label="Appearance"> <div id="vector-appearance-pinned-container" class="vector-pinned-container"> <div id="vector-appearance" class="vector-appearance vector-pinnable-element"> <div class="vector-pinnable-header vector-appearance-pinnable-header vector-pinnable-header-pinned" data-feature-name="appearance-pinned" data-pinnable-element-id="vector-appearance" data-pinned-container-id="vector-appearance-pinned-container" data-unpinned-container-id="vector-appearance-unpinned-container" > <div class="vector-pinnable-header-label">Appearance</div> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-pin-button" data-event-name="pinnable-header.vector-appearance.pin">move to sidebar</button> <button class="vector-pinnable-header-toggle-button vector-pinnable-header-unpin-button" data-event-name="pinnable-header.vector-appearance.unpin">hide</button> </div> </div> </div> </nav> </div> </div> <div id="bodyContent" class="vector-body" aria-labelledby="firstHeading" data-mw-ve-target-container> <div class="vector-body-before-content"> <div id="siteSub" class="noprint">From mediawiki.org</div> </div> <div id="contentSub"><div id="mw-content-subtitle"></div></div> <div id="mw-content-text" class="mw-body-content"><div class="mw-pt-translate-header noprint nomobile" dir="ltr" lang="en"><a href="/w/index.php?title=Special:Translate&amp;group=page-Best+practices+for+extensions&amp;action=page&amp;filter=&amp;action_source=translate_page" title="Special:Translate">Translate this page</a></div><div class="mw-content-ltr mw-parser-output" lang="en" dir="ltr"><div class="mw-pt-languages noprint navigation-not-searchable" lang="en" dir="ltr"><div class="mw-pt-languages-label">Languages:</div><ul class="mw-pt-languages-list"><li><a href="/wiki/Best_practices_for_extensions/de" class="mw-pt-progress mw-pt-progress--low" title="Best practices für Erweiterungen (8% translated)" lang="de" dir="ltr">Deutsch</a></li> <li><span class="mw-pt-languages-ui mw-pt-languages-selected mw-pt-progress mw-pt-progress--complete" lang="en" dir="ltr">English</span></li> <li><a href="/wiki/Best_practices_for_extensions/dtp" class="mw-pt-progress mw-pt-progress--low" title="Best practices for extensions/dtp (5% translated)" lang="dtp" dir="ltr">Kadazandusun</a></li> <li><a href="/wiki/Best_practices_for_extensions/tr" class="mw-pt-progress mw-pt-progress--low" title="Uzantılar için en iyi uygulamalar (8% translated)" lang="tr" dir="ltr">Türkçe</a></li> <li><a href="/wiki/Best_practices_for_extensions/diq" class="mw-pt-progress mw-pt-progress--low" title="Best practices for extensions/diq (2% translated)" lang="diq" dir="ltr">Zazaki</a></li> <li><a href="/wiki/Best_practices_for_extensions/es" class="mw-pt-progress mw-pt-progress--low" title="Las mejores prácticas para las extensionesi (7% translated)" lang="es" dir="ltr">español</a></li> <li><a href="/wiki/Best_practices_for_extensions/fr" class="mw-pt-progress mw-pt-progress--complete" title="Bonnes pratiques pour les extensions (100% translated)" lang="fr" dir="ltr">français</a></li> <li><a href="/wiki/Best_practices_for_extensions/it" class="mw-pt-progress mw-pt-progress--low" title="Le migliori pratiche per le estensioni (4% translated)" lang="it" dir="ltr">italiano</a></li> <li><a href="/wiki/Best_practices_for_extensions/pl" class="mw-pt-progress mw-pt-progress--low" title="Best practices for extensions/pl (1% translated)" lang="pl" dir="ltr">polski</a></li> <li><a href="/wiki/Best_practices_for_extensions/cs" class="mw-pt-progress mw-pt-progress--complete" title="Doporučené postupy pro rozšíření (100% translated)" lang="cs" dir="ltr">čeština</a></li> <li><a href="/wiki/Best_practices_for_extensions/zh" class="mw-pt-progress mw-pt-progress--med" title="-{zh-hans:扩展;zh-hant:擴充功能;}-的最佳实践 (16% translated)" lang="zh" dir="ltr">中文</a></li> <li><a href="/wiki/Best_practices_for_extensions/ja" class="mw-pt-progress mw-pt-progress--low" title="拡張機能の最善手法 (13% translated)" lang="ja" dir="ltr">日本語</a></li></ul></div> <style data-mw-deduplicate="TemplateStyles:r6824882">.mw-parser-output table.ambox{margin:0 10%;width:unset;border:1px solid #a2a9b1;border-left:10px solid #36c;background-color:#fbfbfb;box-sizing:border-box}.mw-parser-output table.ambox+table.ambox,.mw-parser-output table.ambox+link+table.ambox,.mw-parser-output table.ambox+style+table.ambox{margin-top:-1px}.mw-parser-output .ambox td.mbox-empty-cell{border:none;padding:0;width:1px}.mw-parser-output .ambox th.mbox-text,.mw-parser-output .ambox td.mbox-text{border:none;padding:0.25em 0.5em;width:100%}.mw-parser-output .ambox td.mbox-image{padding:2px 0 2px 0.5em}.mw-parser-output .ambox td.mbox-imageright{padding:2px 0.5em 2px 0}.mw-parser-output table.ambox-notice{border-left-color:#36c}.mw-parser-output table.ambox-speedy{background-color:var(--background-color-error-subtle,#fee7e6)}.mw-parser-output table.ambox-delete,.mw-parser-output table.ambox-speedy{border-left-color:var(--background-color-error--active,#b32424)}.mw-parser-output table.ambox-content{border-left-color:#f28500}.mw-parser-output table.ambox-style{border-left-color:#fc3}.mw-parser-output table.ambox-move{border-left-color:#9932cc}.mw-parser-output table.ambox-protection{border-left-color:#a2a9b1}html body.mediawiki .mw-parser-output .ambox.mbox-small{clear:right;float:right;margin:4px 0 4px 1em;box-sizing:border-box;width:238px;font-size:88%;line-height:1.25em}html body.mediawiki .mw-parser-output .ambox.mbox-small-left{margin:4px 1em 4px 0;box-sizing:border-box;overflow:hidden;width:238px;border-collapse:collapse;font-size:88%;line-height:1.25em}</style><table class="ambox ambox-notice plainlinks metadata" role="presentation"><tbody><tr><td class="mbox-image"><div style="width:52px"><span typeof="mw:File"><span><img src="//upload.wikimedia.org/wikipedia/commons/thumb/f/f6/OOjs_UI_icon_check-constructive.svg/30px-OOjs_UI_icon_check-constructive.svg.png" decoding="async" width="30" height="30" class="mw-file-element" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/f/f6/OOjs_UI_icon_check-constructive.svg/45px-OOjs_UI_icon_check-constructive.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/f/f6/OOjs_UI_icon_check-constructive.svg/60px-OOjs_UI_icon_check-constructive.svg.png 2x" data-file-width="20" data-file-height="20"/></span></span></div></td><td class="mbox-text"><div class="mbox-text-span">This page documents a MediaWiki <a href="/wiki/Special:MyLanguage/Development_guidelines" title="Special:MyLanguage/Development guidelines">development guideline</a>, crafted over time by developer consensus (or sometimes by proclamation from a lead developer)</div></td></tr></tbody></table> <p>This page documents the current <b>best practices for developing extensions</b> to MediaWiki. Unless specified otherwise, these also apply to skins. </p><p>Each item is given a rating to reflect its relative importance. The rating uses keywords as defined by <a rel="nofollow" class="external text" href="https://www.ietf.org/rfc/rfc2119.txt">RFC 2119</a>. Here is what they mean in context: </p> <ul><li>MUST: This item is required. Meeting only these means your extension will work, but it may be unsustainable.</li> <li>SHOULD: This item is recommended. Meeting these as well means your extension works well and will likely continue to work well in the future.</li> <li>OPTIONAL: This item is optional. We encourage you to consider it. Extensions that meet these can be considered as meeting the "gold standard" to which we all aspire, and may be used as an example to other developers.</li></ul> <meta property="mw:PageProp/toc"/> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Workflow" data-mw-thread-id="h-Workflow"><span data-mw-comment-start="" id="h-Workflow"></span>Workflow<span data-mw-comment-end="h-Workflow"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=1" title="Edit section: Workflow"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Workflow","replies":[]}}--></div> <ul><li>MUST: Use the standard <a href="/wiki/Special:MyLanguage/Phabricator" title="Special:MyLanguage/Phabricator">issue tracker</a><span style="display:none"><a href="/wiki/Phabricator" title="Phabricator"> </a></span>. Create or <a href="/wiki/Special:MyLanguage/Phabricator/Creating_and_renaming_projects" title="Special:MyLanguage/Phabricator/Creating and renaming projects">request creation</a><span style="display:none"><a href="/wiki/Phabricator/Creating_and_renaming_projects" title="Phabricator/Creating and renaming projects"> </a></span> of a project in Phabricator for your extension.</li> <li>MUST: Use the standard <a href="/wiki/Special:MyLanguage/Code_review" title="Special:MyLanguage/Code review">Code review</a><span style="display:none"><a href="/wiki/Code_review" title="Code review"> </a></span> system.</li> <li>MUST: Before deployment on Wikimedia Foundation wikis – pass <a href="/wiki/Special:MyLanguage/Writing_an_extension_for_deployment" title="Special:MyLanguage/Writing an extension for deployment">performance and security reviews</a><span style="display:none"><a href="/wiki/Writing_an_extension_for_deployment" title="Writing an extension for deployment"> </a></span>.</li> <li>SHOULD: The extension should be suitable for use on any wiki, avoid hardcoding specifics for Wikimedia Foundation or other organizations.</li> <li>SHOULD: Before deployment on Wikimedia Foundation wikis – add the extension and your team to the <a href="/wiki/Special:MyLanguage/Developers/Maintainers" title="Special:MyLanguage/Developers/Maintainers">Maintainers</a><span style="display:none"><a href="/wiki/Developers/Maintainers" title="Developers/Maintainers"> </a></span> page.</li> <li>SHOULD: Have co-maintainers! There should be two maintainers minimum. (Doesn't have to be Wikimedia Foundation staff.)</li> <li>OPTIONAL: Create a <a href="/wiki/Special:MyLanguage/MediaWiki-Docker/Extension" title="Special:MyLanguage/MediaWiki-Docker/Extension">MediaWiki-Docker</a><span style="display:none"><a href="/wiki/MediaWiki-Docker/Extension" title="MediaWiki-Docker/Extension"> </a></span> installation guide for your extension.</li> <li>OPTIONAL: Create a <a href="/wiki/Special:MyLanguage/MediaWiki-Vagrant" title="Special:MyLanguage/MediaWiki-Vagrant">MediaWiki-Vagrant</a><span style="display:none"><a href="/wiki/MediaWiki-Vagrant" title="MediaWiki-Vagrant"> </a></span> role for your extension.</li></ul> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Code_architecture" data-mw-thread-id="h-Code_architecture"><span data-mw-comment-start="" id="h-Code_architecture"></span>Code architecture<span data-mw-comment-end="h-Code_architecture"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=2" title="Edit section: Code architecture"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Code_architecture","replies":[]}}--></div> <ul><li>MUST: Use <a href="/wiki/Special:MyLanguage/Manual:Structured_logging" title="Special:MyLanguage/Manual:Structured logging">structured logging</a>, with a channel name specific to your extension and with appropriate use of message severity levels. This aids <a href="/wiki/Special:MyLanguage/Manual:How_to_debug" title="Special:MyLanguage/Manual:How to debug">debugging</a>. For Wikimedia Foundation deployment, this also helps your team in monitoring the extension. See also: <a href="https://wikitech.wikimedia.org/wiki/OpenSearch_Dashboards" class="extiw" title="wikitech:OpenSearch Dashboards">Logstash on Wikitech</a>.</li> <li>SHOULD: Provide the same functionality through the API (<a href="/wiki/Special:MyLanguage/API:Main_page" title="Special:MyLanguage/API:Main page">Action API</a><span style="display:none"><a href="/wiki/API:Main_page" title="API:Main page"> </a></span> or <a href="/wiki/Special:MyLanguage/API:REST_API" title="Special:MyLanguage/API:REST API">REST API</a><span style="display:none"><a href="/wiki/API:REST_API" title="API:REST API"> </a></span>) and the graphical interface (<code>index.php</code>). <ul><li>OPTIONAL: The functionality should be implemented without significant code duplication (<i>e.g.</i> shared backend service class with light API and SpecialPage bindings.)</li></ul></li> <li>SHOULD: Use <a href="/wiki/Special:MyLanguage/Dependency_Injection" title="Special:MyLanguage/Dependency Injection">Dependency Injection</a><span style="display:none"><a href="/wiki/Dependency_Injection" title="Dependency Injection"> </a></span> ("DI") and service classes registered in service wiring.</li> <li>SHOULD: Avoid global state, especially singletons or other hidden state via static class variable (unless for debug logs and other state instrisically about the current process and not about any particular wiki or caller). Avoid static calls for anything other than stateless utility methods.</li> <li>SHOULD: Avoid use of older unrefactored global or static functions from unrelated classes when writing new DI-based service classes.</li> <li>SHOULD: Only throw exceptions for situations that callers should not handle and thus are meant to bubble up.</li> <li>SHOULD: Don't hardcode wikitext and assumptions about templates, especially in a way that's not configurable for each website.</li> <li>SHOULD: Code should be readable by someone who is familiar in that area.</li> <li>SHOULD: Have a clear <a href="/wiki/Special:MyLanguage/Architecture_guidelines#Separation_of_concerns_—_UI_and_business_logic" title="Special:MyLanguage/Architecture guidelines">separation of concerns</a><span style="display:none"><a href="/wiki/Architecture_guidelines#Separation_of_concerns_—_UI_and_business_logic" title="Architecture guidelines"> </a></span> between what it actually does, and how it is presented to the user.</li> <li>SHOULD: Think twice before adding new public APIs that must remain for content compatibility, such as new wikitext syntax functionality.</li> <li>SHOULD: Not tightly integrate skin functionality with extension functionality.</li> <li>SHOULD: <a href="/wiki/Special:MyLanguage/Just_make_it_a_user_preference" title="Special:MyLanguage/Just make it a user preference">Not add new user preferences</a><span style="display:none"><a href="/wiki/Just_make_it_a_user_preference" title="Just make it a user preference"> </a></span>, unless you have a really good reason for doing so.</li> <li>OPTIONAL: Expose JavaScript methods for use by <a href="/wiki/Special:MyLanguage/user_scripts" title="Special:MyLanguage/user scripts">user scripts</a><span style="display:none"><a href="/wiki/User_scripts" class="mw-redirect" title="User scripts"> </a></span> and <a href="/wiki/Special:MyLanguage/Extension:Gadgets" title="Special:MyLanguage/Extension:Gadgets">gadgets</a><span style="display:none"><a href="/wiki/Extension:Gadgets" title="Extension:Gadgets"> </a></span>, and to enable easy debugging from the console.</li></ul> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Documentation" data-mw-thread-id="h-Documentation"><span data-mw-comment-start="" id="h-Documentation"></span>Documentation<span data-mw-comment-end="h-Documentation"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=3" title="Edit section: Documentation"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Documentation","replies":[]}}--></div> <ul><li>MUST: Have an <a href="/wiki/Special:AllPages/Extension:" title="Special:AllPages/Extension:"><i>Extension:…</i> page</a> on mediawiki.org. <ul><li>MUST: Add it to the appropriate <a href="/wiki/Special:MyLanguage/Category:Extensions" title="Special:MyLanguage/Category:Extensions">extension categories</a><span style="display:none"><a href="/wiki/Category:Extensions" title="Category:Extensions"> </a></span>.</li> <li>MUST: Explain briefly what it does.</li> <li>MUST: Document provided hooks under <code>Extension:ExtensionName/Hooks/HookName</code> using <style data-mw-deduplicate="TemplateStyles:r4719022">.mw-parser-output .monospaced{font-family:monospace,monospace}</style><span class="monospaced">{{<a href="/w/index.php?title=Template:Template:ExtensionHook&amp;action=edit&amp;redlink=1" class="new" title="Template:Template:ExtensionHook (page does not exist)">Template:ExtensionHook</a>}}</span> (if any).</li> <li>MUST: List all extensions on which it depends, and how to install and configure those.</li> <li>SHOULD: Set a <a href="/wiki/Special:MyLanguage/Compatibility#MediaWiki_extensions" title="Special:MyLanguage/Compatibility">compatibility policy</a><span style="display:none"><a href="/wiki/Compatibility#MediaWiki_extensions" title="Compatibility"> </a></span> for your extension in the infobox.</li> <li>SHOULD: Describe all configuration settings in one place, from most-used to most-obscure.</li> <li>OPTIONAL: Mention any similar extensions and explain how it compares and differs to those.</li> <li>OPTIONAL: Write instructions for how to uninstall the extension, or document the limited ability to uninstall.</li></ul></li> <li>MUST: Have a <a href="/wiki/Special:AllPages/Help:Extension:" title="Special:AllPages/Help:Extension:"><i>Help:Extension:…</i> page</a> on mediawiki.org if the extension has web-facing user interfaces. <ul><li>OPTIONAL: Add screenshots in multiple languages, and ideally include one in a <a href="/wiki/Special:MyLanguage/Directionality_support" title="Special:MyLanguage/Directionality support">right-to-left</a><span style="display:none"><a href="/wiki/Directionality_support" title="Directionality support"> </a></span> language.</li> <li>OPTIONAL: Mention some edge cases that were tested for – to prove due diligence of not just testing on simple/generic articles.</li></ul></li> <li>SHOULD: Consistently use <code class="mw-highlight" style="white-space:nowrap"><span class="p">‎&lt;</span><span class="nt">code</span><span class="p">></span></code>, <code class="mw-highlight" style="white-space:nowrap"><span class="p">‎&lt;</span><span class="nt">syntaxhighlight</span><span class="p">></span></code>, <code class="mw-highlight" style="white-space:nowrap"><span class="p">‎&lt;</span><span class="nt">kbd</span><span class="p">></span></code>, and <code class="mw-highlight" style="white-space:nowrap"><span class="p">‎&lt;</span><span class="nt">pre</span><span class="p">></span></code> in documentation as appropriate.</li> <li>OPTIONAL: Add or update the extension's page on <a href="/wiki/Special:MyLanguage/WikiApiary" title="Special:MyLanguage/WikiApiary">WikiApiary</a><span style="display:none"><a href="/wiki/WikiApiary" title="WikiApiary"> </a></span>.</li></ul> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="File_structure" data-mw-thread-id="h-File_structure"><span data-mw-comment-start="" id="h-File_structure"></span>File structure<span data-mw-comment-end="h-File_structure"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=4" title="Edit section: File structure"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-File_structure","replies":[]}}--></div> <p>Overall, the extension's file layout should be organized: consistent naming, directory structure that is logical and not messy. </p> <ul><li>MUST: Using the following standard directory layout: <ul><li><code>src/</code> (when using PSR4 name-spacing, preferred) or <code>includes/</code>: Contains all (and only) PHP classes. <ul><li>i18n files for special page alias, magic words or namespaces located in root folder.</li> <li>SHOULD: Use <a rel="nofollow" class="external text" href="https://www.php-fig.org/psr/psr-4/">PSR-4</a> structure for classes and files, using <a href="/wiki/Special:MyLanguage/Manual" title="Special:MyLanguage/Manual">Extension.json/Schema#AutoloadNamespaces</a><span style="display:none"><a href="/wiki/Manual" class="mw-redirect" title="Manual"> </a></span> in <code>extension.json</code> instead of listing all classes.</li> <li>SHOULD: Classes in <code>MediaWiki\Extension\&lt;ExtensionName></code> namespace (or <code>MediaWiki\Skin\&lt;SkinName></code> if a skin). <code>MediaWiki\&lt;ExtensionName></code> is permissible if the extension name is a sufficiently unique word and not something generic (<i>e.g.</i> not a verb or noun).</li> <li>SHOULD: One class per file.</li></ul></li> <li><code>modules/</code> (or <code>resources</code>) - Contains JavaScript and CSS for <a href="/wiki/Special:MyLanguage/ResourceLoader" title="Special:MyLanguage/ResourceLoader">ResourceLoader</a><span style="display:none"><a href="/wiki/ResourceLoader" title="ResourceLoader"> </a></span>.</li> <li><code>maintenance/</code> command-line <a href="/wiki/Special:MyLanguage/maintenance" title="Special:MyLanguage/maintenance">maintenance</a><span style="display:none"><a href="/wiki/Maintenance" class="mw-redirect" title="Maintenance"> </a></span> scripts</li> <li><code>i18n/</code> - Contains <a href="/wiki/Special:MyLanguage/Localisation" title="Special:MyLanguage/Localisation">localised messages</a><span style="display:none"><a href="/wiki/Localisation" title="Localisation"> </a></span> in JSON files.</li> <li><code>sql/</code> - SQL files for database modifications (<i>e.g.</i> called by <a href="/wiki/Special:MyLanguage/Manual:Hooks/LoadExtensionSchemaUpdates" title="Special:MyLanguage/Manual:Hooks/LoadExtensionSchemaUpdates">LoadExtensionSchemaUpdates</a><span style="display:none"><a href="/wiki/Manual:Hooks/LoadExtensionSchemaUpdates" title="Manual:Hooks/LoadExtensionSchemaUpdates"> </a></span>)</li> <li><code>tests/</code>: <ul><li><code>tests/parser/</code> - Contains <a href="/wiki/Special:MyLanguage/Parser_tests" title="Special:MyLanguage/Parser tests">parser test</a><span style="display:none"><a href="/wiki/Parser_tests" class="mw-redirect" title="Parser tests"> </a></span> files.</li> <li><code>tests/phpunit/</code> - Contains <a href="/wiki/Special:MyLanguage/Manual:PHP_unit_testing" title="Special:MyLanguage/Manual:PHP unit testing">PHPUnit</a><span style="display:none"><a href="/wiki/Manual:PHP_unit_testing" title="Manual:PHP unit testing"> </a></span> test cases. <ul><li><code>tests/phpunit/unit/</code> - Contains test cases extending <code>MediaWikiUnitTestCase</code></li> <li><code>tests/phpunit/integration/</code> - Contains test cases extending <code>MediaWikiIntegrationTestCase</code></li></ul></li> <li><code>tests/qunit/</code> - Contains <a href="/wiki/Special:MyLanguage/Manual:JavaScript_unit_testing" title="Special:MyLanguage/Manual:JavaScript unit testing">Qunit</a><span style="display:none"><a href="/wiki/Manual:JavaScript_unit_testing" title="Manual:JavaScript unit testing"> </a></span> test cases.</li> <li><code>tests/selenium/</code> - Contains <a href="/wiki/Special:MyLanguage/Selenium" title="Special:MyLanguage/Selenium">Selenium browser test</a><span style="display:none"><a href="/wiki/Selenium" title="Selenium"> </a></span> files.</li></ul></li> <li><code>COPYING</code> or <code>LICENSE</code> - Contains full copy of the relevant license the code is released under.</li></ul></li> <li>SHOULD: Avoid having many files in the root level directory.</li> <li>SHOULD: Avoid having dozens of nested directories that all only contain one or two things.</li> <li>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).</li> <li>SHOULD: Write a README file that summarizes the docs and gives detailed installation instructions.</li> <li>SHOULD: Declare <a href="/wiki/Special:MyLanguage/ResourceLoader/Foreign_resources" title="Special:MyLanguage/ResourceLoader/Foreign resources">foreign resources</a><span style="display:none"><a href="/wiki/ResourceLoader/Foreign_resources" title="ResourceLoader/Foreign resources"> </a></span> in a <code>foreign-resources.yaml</code> file.</li></ul> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Database" data-mw-thread-id="h-Database"><span data-mw-comment-start="" id="h-Database"></span>Database<span data-mw-comment-end="h-Database"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=5" title="Edit section: Database"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Database","replies":[]}}--></div> <ul><li>MUST: If adding database tables, use the <a href="/wiki/Special:MyLanguage/Manual:Hooks/LoadExtensionSchemaUpdates" title="Special:MyLanguage/Manual:Hooks/LoadExtensionSchemaUpdates">LoadExtensionSchemaUpdates</a><span style="display:none"><a href="/wiki/Manual:Hooks/LoadExtensionSchemaUpdates" title="Manual:Hooks/LoadExtensionSchemaUpdates"> </a></span> hook to ensure update.php works.</li> <li>MUST: Uses the <a href="/wiki/Special:MyLanguage/Rdbms" title="Special:MyLanguage/Rdbms">Wikimedia-Rdbms</a><span style="display:none"><a href="/wiki/Rdbms" title="Rdbms"> </a></span> library for all database access. Doing so avoids most SQL injection attack vectors, takes care of ensuring transactional correctness, and follows performance best practices.</li> <li>SHOULD: Work well in a distributed environment (concurrency, multiple databases, clustering).</li> <li>SHOULD: If it needs persistence, create nice SQL (primary keys, indexes where needed) and uses some caching mechanism where/if necessary.</li> <li>SHOULD: Never add fields to the core tables nor alter them in any way. To persist data associated with core tables, create a dedicated table for the extension and reference the core table's primary key. This makes it easier to remove an extension.</li> <li>SHOULD: it should use <a href="/wiki/Special:MyLanguage/Manual:Schema_changes" title="Special:MyLanguage/Manual:Schema changes">abstract schema</a><span style="display:none"><a href="/wiki/Manual:Schema_changes" title="Manual:Schema changes"> </a></span>.</li> <li>OPTIONAL: If the extension persists data and supports uninstalling, provide a maintenance script that automates this (<i>e.g.</i> drop tables, prune relevant log entries and page properties).</li></ul> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Coding_conventions" data-mw-thread-id="h-Coding_conventions"><span data-mw-comment-start="" id="h-Coding_conventions"></span>Coding conventions<span data-mw-comment-end="h-Coding_conventions"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=6" title="Edit section: Coding conventions"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Coding_conventions","replies":[]}}--></div> <p>Overall, follow the <a href="/wiki/Special:MyLanguage/Manual:Coding_conventions" title="Special:MyLanguage/Manual:Coding conventions">MediaWiki coding conventions</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions" title="Manual:Coding conventions"> </a></span> for <a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/PHP" title="Special:MyLanguage/Manual:Coding conventions/PHP">PHP</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/PHP" title="Manual:Coding conventions/PHP"> </a></span>, <a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/JavaScript" title="Special:MyLanguage/Manual:Coding conventions/JavaScript">JavaScript</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/JavaScript" title="Manual:Coding conventions/JavaScript"> </a></span>, <a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/CSS" title="Special:MyLanguage/Manual:Coding conventions/CSS">CSS</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/CSS" title="Manual:Coding conventions/CSS"> </a></span>, and any other languages that are in-use and have applicable code conventions. </p> <ul><li>SHOULD: Run <a href="/wiki/Special:MyLanguage/Continuous_integration/PHP_CodeSniffer" title="Special:MyLanguage/Continuous integration/PHP CodeSniffer">MediaWiki-CodeSniffer</a><span style="display:none"><a href="/wiki/Continuous_integration/PHP_CodeSniffer" title="Continuous integration/PHP CodeSniffer"> </a></span> to enforce PHP conventions (check <a href="/wiki/Special:MyLanguage/Continuous_integration/Entry_points" title="Special:MyLanguage/Continuous integration/Entry points">CI Entry points</a><span style="display:none"><a href="/wiki/Continuous_integration/Entry_points" title="Continuous integration/Entry points"> </a></span>).</li> <li>SHOULD: Run Phan for PHP static analysis (check <a href="/wiki/Special:MyLanguage/Continuous_integration/Entry_points" title="Special:MyLanguage/Continuous integration/Entry points">CI Entry points</a><span style="display:none"><a href="/wiki/Continuous_integration/Entry_points" title="Continuous integration/Entry points"> </a></span>).</li> <li>SHOULD: Run ESLint for JavaScript conventions and static analysis (check <a href="/wiki/Special:MyLanguage/Continuous_integration/Entry_points" title="Special:MyLanguage/Continuous integration/Entry points">CI Entry points</a><span style="display:none"><a href="/wiki/Continuous_integration/Entry_points" title="Continuous integration/Entry points"> </a></span>).</li> <li>SHOULD: Run Stylelint for CSS conventions (check <a href="/wiki/Special:MyLanguage/Continuous_integration/Entry_points" title="Special:MyLanguage/Continuous integration/Entry points">CI Entry points</a><span style="display:none"><a href="/wiki/Continuous_integration/Entry_points" title="Continuous integration/Entry points"> </a></span>).</li> <li>SHOULD: Avoid writing all code into one large function (in JavaScript especially).</li> <li>OPTIONAL: 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.</li></ul> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Testing" data-mw-thread-id="h-Testing"><span data-mw-comment-start="" id="h-Testing"></span>Testing<span data-mw-comment-end="h-Testing"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=7" title="Edit section: Testing"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Testing","replies":[]}}--></div> <ul><li>SHOULD: Have and run <a href="/wiki/Special:MyLanguage/Manual:PHP_unit_testing" title="Special:MyLanguage/Manual:PHP unit testing">PHPUnit</a><span style="display:none"><a href="/wiki/Manual:PHP_unit_testing" title="Manual:PHP unit testing"> </a></span> and <a href="/wiki/Special:MyLanguage/Manual:JavaScript_unit_testing" title="Special:MyLanguage/Manual:JavaScript unit testing">QUnit</a><span style="display:none"><a href="/wiki/Manual:JavaScript_unit_testing" title="Manual:JavaScript unit testing"> </a></span> tests. <ul><li>OPTIONAL: Split out integration and unit tests (see <a href="https://phabricator.wikimedia.org/T87781" class="extiw" title="phab:T87781">Phab:T87781</a>).</li></ul></li> <li>SHOULD: If there are parser functions or tags, have and run <a href="/wiki/Special:MyLanguage/Manual:Parser_tests" title="Special:MyLanguage/Manual:Parser tests">parser tests</a><span style="display:none"><a href="/wiki/Manual:Parser_tests" title="Manual:Parser tests"> </a></span>.</li> <li>OPTIONAL: Have and run <a href="/wiki/Special:MyLanguage/Selenium/Ruby/Browser_testing" title="Special:MyLanguage/Selenium/Ruby/Browser testing">browser tests</a><span style="display:none"><a href="/wiki/Selenium/Ruby/Browser_testing" title="Selenium/Ruby/Browser testing"> </a></span>.</li> <li>OPTIONAL: Test against <a href="/wiki/Special:MyLanguage/Directionality_support" title="Special:MyLanguage/Directionality support">right-to-left</a><span style="display:none"><a href="/wiki/Directionality_support" title="Directionality support"> </a></span> (RTL) languages! (how to verify?).</li> <li>OPTIONAL: Test against <a href="/wiki/Special:MyLanguage/Writing_systems#LanguageConverter" title="Special:MyLanguage/Writing systems">language converter</a><span style="display:none"><a href="/wiki/Writing_systems#LanguageConverter" title="Writing systems"> </a></span> languages! (how to verify?).</li></ul> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Language" data-mw-thread-id="h-Language"><span data-mw-comment-start="" id="h-Language"></span>Language<span data-mw-comment-end="h-Language"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=8" title="Edit section: Language"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Language","replies":[]}}--></div> <p>Various aspects of language support are also known as <a href="/wiki/Special:MyLanguage/Localisation" title="Special:MyLanguage/Localisation">Localisation</a><span style="display:none"><a href="/wiki/Localisation" title="Localisation"> </a></span> (L10n), internationalization (i18n), multlingualization, and globalization. Overall, your extension should be fully usable and compatible with non-English and non-left-to-right languages. </p> <ul><li>MUST: Use the proper <a href="/wiki/Special:MyLanguage/Localisation" title="Special:MyLanguage/Localisation">Localisation</a><span style="display:none"><a href="/wiki/Localisation" title="Localisation"> </a></span> functions (<a href="/wiki/Special:MyLanguage/Manual:Messages_API" title="Special:MyLanguage/Manual:Messages API">wfMessage</a><span style="display:none"><a href="/wiki/Manual:Messages_API" title="Manual:Messages API"> </a></span>), and not have hardcoded non-translatable strings in your code.</li> <li>MUST: Use the standard internationalization systems in MediaWiki.</li> <li>MUST: Use a clear and unique prefix named after the extension for all interface messages.</li> <li>MUST: Regularly submit messages to, and merge updated translations from, <a href="/wiki/Special:MyLanguage/translatewiki.net" title="Special:MyLanguage/translatewiki.net">Translatewiki.net</a><span style="display:none"><a href="/wiki/Translatewiki.net" title="Translatewiki.net"> </a></span>. For extensions hosted in Wikimedia Gerrit, translatewiki.net staff will generally proactively do this for you. They start an automatic process which subscribes to new messages from your extension, and also automatically exports and merges updated translations back into your repository once a day. If this hasn't happened within a week, <a href="https://translatewiki.net/wiki/Special:MyLanguage/Project:About#Contact_us" class="extiw" title="translatewiki:Special:MyLanguage/Project:About">contact TWN staff</a>.</li> <li>MUST: Add <code>qqq.json</code> <a href="/wiki/Special:MyLanguage/Localisation#Message_documentation" title="Special:MyLanguage/Localisation">message documentation</a><span style="display:none"><a href="/wiki/Localisation#Message_documentation" title="Localisation"> </a></span> for all messages that exist in <code>en.json</code></li> <li>SHOULD: lint the <a href="/wiki/Special:MyLanguage/Localisation_file_format" title="Special:MyLanguage/Localisation file format">message files</a><span style="display:none"><a href="/wiki/Localisation_file_format" class="mw-redirect" title="Localisation file format"> </a></span> in CI using <a rel="nofollow" class="external text" href="https://github.com/wikimedia/banana-checker">banana-checker</a>.</li> <li>SHOULD: Escape parameters to localisation messages as close to output as possible. Document whether functions take/accept wikitext vs. HTML.</li> <li>OPTIONAL: If an extension uses particular terms, write a glossary of these terms, and link to it from the message documentation. Example: <a href="https://meta.wikimedia.org/wiki/Special:MyLanguage/Abstract_Wikipedia/Glossary" class="extiw" title="m:Special:MyLanguage/Abstract Wikipedia/Glossary">Abstract Wikipedia/Glossary</a>.</li></ul> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Accessibility" data-mw-thread-id="h-Accessibility"><span data-mw-comment-start="" id="h-Accessibility"></span>Accessibility<span data-mw-comment-end="h-Accessibility"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=9" title="Edit section: Accessibility"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Accessibility","replies":[]}}--></div> <p>Refer to <a href="/wiki/Special:MyLanguage/Accessibility_guide_for_developers" title="Special:MyLanguage/Accessibility guide for developers">Accessibility guide for developers</a><span style="display:none"><a href="/wiki/Accessibility_guide_for_developers" title="Accessibility guide for developers"> </a></span>. Note that those are not yet integrated into the guidelines and for the purposes of the extension best practices may be considered as OPTIONAL, pending <a href="/wiki/Topic:Wqvqvhgsvpu1je15" title="Topic:Wqvqvhgsvpu1je15">further discussion</a>. </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Security" data-mw-thread-id="h-Security"><span data-mw-comment-start="" id="h-Security"></span>Security<span data-mw-comment-end="h-Security"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=10" title="Edit section: Security"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Security","replies":[]}}--></div> <p>See also <a href="/wiki/Special:MyLanguage/Security_for_developers" title="Special:MyLanguage/Security for developers">Security for developers</a><span style="display:none"><a href="/wiki/Security_for_developers" title="Security for developers"> </a></span>. </p> <ul><li>MUST: Shelling out should escape arguments.</li> <li>MUST: All write actions must be protected against <a href="/wiki/Special:MyLanguage/cross-site_request_forgery" title="Special:MyLanguage/cross-site request forgery">Cross-site request forgery</a><span style="display:none"><a href="/wiki/Cross-site_request_forgery" title="Cross-site request forgery"> </a></span> (CSRF).</li> <li>MUST: Make sure privacy related issues (checkuser, revision and log suppression and deletion) are still covered when refactoring or writing new code.</li> <li>SHOULD: Use the standard MediaWiki CSRF token system.</li> <li>SHOULD: Don't modify HTML after it has been sanitized (common pattern is to use regex, but that's bad).</li> <li>SHOULD: Don't load any resources from external domains. This is also needed for privacy and improves performance.</li> <li>SHOULD: Discuss creation of new user rights or user groups with the community first. Adding user rights is easy in code, but must be carefully considered with respect to who may grant these rights, and who carries those rights by default.</li></ul> <div style="background-color: #fff0f0; border: 1px dashed blue; margin: 0.0em; padding: 0.2em;"> <span style="background-color: yellow"><b>TODO:</b></span> Should extensions be creating user groups in their default configuration? Or should we recommend leaving new rights unassigned or assigned to core groups by default?</div> <p><br/> </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Don't_reinvent_/_abuse_MediaWiki" data-mw-thread-id="h-Don&#039;t_reinvent_/_abuse_MediaWiki"><span id="Don.27t_reinvent_.2F_abuse_MediaWiki"></span><span data-mw-comment-start="" id="h-Don't_reinvent_/_abuse_MediaWiki"></span>Don't reinvent / abuse MediaWiki<span data-mw-comment-end="h-Don't_reinvent_/_abuse_MediaWiki"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=11" title="Edit section: Don&#039;t reinvent / abuse MediaWiki"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Don't_reinvent_\/_abuse_MediaWiki","replies":[]}}--></div> <p>As a general principle, do not re-implement or compete with functionality already provided by MediaWiki core. </p> <ul><li>MUST: Use MediaWiki functionality/wrappers for things like <a href="/wiki/Special:MyLanguage/Manual:WebRequest.php" title="Special:MyLanguage/Manual:WebRequest.php">WebRequest</a><span style="display:none"><a href="/wiki/Manual:WebRequest.php" title="Manual:WebRequest.php"> </a></span> vs. <code>$_GET</code>, <i>etc.</i></li> <li>MUST: Use <a href="/wiki/Special:MyLanguage/Manual:Hooks" title="Special:MyLanguage/Manual:Hooks">hooks</a><span style="display:none"><a href="/wiki/Manual:Hooks" title="Manual:Hooks"> </a></span> where possible as opposed to workarounds or novel ways of modifying, injecting, or extending functionality.</li> <li>MUST Use MediaWiki's validation/sanitization methods <i>e.g.</i> those in the <span class="plainlinks"><a class="external text" href="https://doc.wikimedia.org/mediawiki-core/master/php/classMediaWiki_1_1Html_1_1Html.html">Html</a></span> and <span class="plainlinks"><a class="external text" href="https://doc.wikimedia.org/mediawiki-core/master/php/classMediaWiki_1_1Parser_1_1Sanitizer.html">Sanitizer</a></span> classes.</li> <li>MUST: Don't disable parser cache unless you have a really good reason.</li> <li>MUST: Use <a href="/wiki/Special:MyLanguage/Composer" title="Special:MyLanguage/Composer">Composer</a><span style="display:none"><a href="/wiki/Composer" title="Composer"> </a></span> for 3rd party PHP library management.</li> <li>SHOULD: Don't reimplement the wheel. Prefer stable and well-maintained libraries when they exist.</li> <li>SHOULD: Don't disable <a href="/wiki/Special:MyLanguage/Manual:OutputPage.php" title="Special:MyLanguage/Manual:OutputPage.php">OutputPage</a><span style="display:none"><a href="/wiki/Manual:OutputPage.php" title="Manual:OutputPage.php"> </a></span>. (<a href="https://phabricator.wikimedia.org/T140664" class="extiw" title="phab:T140664">T140664</a>)</li> <li>SHOULD: If an abstraction exists (<i>e.g.</i> <a href="/wiki/Special:MyLanguage/Manual:ContentHandler" title="Special:MyLanguage/Manual:ContentHandler">Manual:ContentHandler</a><span style="display:none"><a href="/wiki/Manual:ContentHandler" title="Manual:ContentHandler"> </a></span>), use that instead of hooks.</li> <li>SHOULD: Don't make things harder for yourself – use standard functionality like extension.json's tests/PHPUnit auto-discovery stuff.</li> <li>SHOULD: Use global MediaWiki configuration such as read-only mode.</li></ul> <p><br/> </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Uncategorized" data-mw-thread-id="h-Uncategorized"><span data-mw-comment-start="" id="h-Uncategorized"></span>Uncategorized<span data-mw-comment-end="h-Uncategorized"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=12" title="Edit section: Uncategorized"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Uncategorized","replies":[]}}--></div> <ul><li>Know when you should use ParserOutput methods <i>vs.</i> similar methods on OutputPage.</li></ul> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="Meta" data-mw-thread-id="h-Meta"><span data-mw-comment-start="" id="h-Meta"></span>Meta<span data-mw-comment-end="h-Meta"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=13" title="Edit section: Meta"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Meta","replies":[]}}--></div> <p>This page was first drafted during the <a href="https://phabricator.wikimedia.org/T172845" class="extiw" title="phab:T172845">2017 Wikimania Hackathon</a>. </p> <div class="mw-heading mw-heading2 ext-discussiontools-init-section"><h2 id="See_also" data-mw-thread-id="h-See_also"><span data-mw-comment-start="" id="h-See_also"></span>See also<span data-mw-comment-end="h-See_also"></span></h2><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/w/index.php?title=Best_practices_for_extensions&amp;action=edit&amp;section=14" title="Edit section: See also"><span>edit</span></a><span class="mw-editsection-bracket">]</span></span><!--__DTELLIPSISBUTTON__{"threadItem":{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-See_also","replies":[]}}--></div> <ul><li><a href="/wiki/Special:MyLanguage/API:Client_code/Gold_standard" title="Special:MyLanguage/API:Client code/Gold standard">API:Client code/Gold standard</a><span style="display:none"><a href="/wiki/API:Client_code/Gold_standard" title="API:Client code/Gold standard"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Technical_Collaboration_Guidance" title="Special:MyLanguage/Technical Collaboration Guidance">Technical Collaboration Guidance</a><span style="display:none"><a href="/wiki/Technical_Collaboration_Guidance" title="Technical Collaboration Guidance"> </a></span></li> <li><a href="https://en.wikipedia.org/wiki/User:Risker/Risker%27s_checklist_for_content-creation_extensions" class="extiw" title="w:User:Risker/Risker's checklist for content-creation extensions">w:User:Risker/Risker's checklist for content-creation extensions</a></li> <li><a href="/wiki/Special:MyLanguage/Best_practices_for_using_MediaWiki" title="Special:MyLanguage/Best practices for using MediaWiki">Best practices for using MediaWiki</a><span style="display:none"><a href="/wiki/Best_practices_for_using_MediaWiki" title="Best practices for using MediaWiki"> </a></span></li> <li><a href="https://phabricator.wikimedia.org/T172845" class="extiw" title="phab:T172845">phab:T172845</a> - "What makes a high quality MediaWiki extension?" – Hackathon session</li></ul> <div class="navbox-styles nomobile"><style data-mw-deduplicate="TemplateStyles:r6230902">.mw-parser-output .navbox{border:1px solid #aaa;box-sizing:border-box;width:100%;margin:auto;clear:both;font-size:88%;text-align:center;padding:1px}.mw-parser-output .navbox-inner,.mw-parser-output .navbox-subgroup{width:100%}.mw-parser-output .navbox+.navbox-styles+.navbox{margin-top:-1px}.mw-parser-output .navbox th,.mw-parser-output .navbox-title,.mw-parser-output .navbox-abovebelow{text-align:center;padding-left:1em;padding-right:1em}.mw-parser-output th.navbox-group{white-space:nowrap;text-align:right}.mw-parser-output .navbox,.mw-parser-output .navbox-subgroup{background:#fdfdfd}.mw-parser-output .navbox-list{border-color:#fdfdfd}.mw-parser-output .navbox th,.mw-parser-output .navbox-title{background:#eaeeff}.mw-parser-output .navbox-abovebelow,.mw-parser-output th.navbox-group,.mw-parser-output .navbox-subgroup .navbox-title{background:#ddddff}.mw-parser-output .navbox-subgroup .navbox-group,.mw-parser-output .navbox-subgroup .navbox-abovebelow{background:#e6e6ff}.mw-parser-output .navbox-even{background:#f7f7f7}.mw-parser-output .navbox-odd{background:transparent}.mw-parser-output th.navbox-title1{border-left:2px solid #fdfdfd;width:100%}.mw-parser-output td.navbox-list1{text-align:left;border-left-width:2px;border-left-style:solid}.mw-parser-output .navbox .hlist td dl,.mw-parser-output .navbox .hlist td ol,.mw-parser-output .navbox .hlist td ul,.mw-parser-output .navbox td.hlist dl,.mw-parser-output .navbox td.hlist ol,.mw-parser-output .navbox td.hlist ul{padding:0.125em 0}.mw-parser-output .navbox .hlist dd,.mw-parser-output .navbox .hlist dt,.mw-parser-output .navbox .hlist li{white-space:nowrap}.mw-parser-output .navbox .hlist dd dl,.mw-parser-output .navbox .hlist dt dl,.mw-parser-output .navbox .hlist li ol,.mw-parser-output .navbox .hlist li ul{white-space:normal}.mw-parser-output ol+.navbox-styles+.navbox,.mw-parser-output ul+.navbox-styles+.navbox{margin-top:0.5em}</style></div><div role="navigation" class="navbox" aria-labelledby="Development_guidelines" style="border: 1px solid #aaa; padding: 3px;;padding:3px"><table class="nowraplinks hlist navbox-inner" style="border-spacing:0;background:transparent;color:inherit"><tbody><tr><th scope="col" class="navbox-title" colspan="2"><style data-mw-deduplicate="TemplateStyles:r6387430">.mw-parser-output .hlist dl,.mw-parser-output .hlist ol,.mw-parser-output .hlist ul{margin:0;padding:0}.mw-parser-output .hlist dd,.mw-parser-output .hlist dt,.mw-parser-output .hlist li{margin:0;display:inline}.mw-parser-output .hlist dl dl,.mw-parser-output .hlist dl ol,.mw-parser-output .hlist dl ul,.mw-parser-output .hlist ol dl,.mw-parser-output .hlist ol ol,.mw-parser-output .hlist ol ul,.mw-parser-output .hlist ul dl,.mw-parser-output .hlist ul ol,.mw-parser-output .hlist ul ul{display:inline}.mw-parser-output .hlist .mw-empty-li,.mw-parser-output .hlist .mw-empty-elt{display:none}.mw-parser-output .hlist dt:after{content:": "}.mw-parser-output .hlist dd:after,.mw-parser-output .hlist li:after{content:" · ";font-weight:bold}.mw-parser-output .hlist dd:last-child:after,.mw-parser-output .hlist dt:last-child:after,.mw-parser-output .hlist li:last-child:after{content:none}.mw-parser-output .hlist dd dd:first-child:before,.mw-parser-output .hlist dd dt:first-child:before,.mw-parser-output .hlist dd li:first-child:before,.mw-parser-output .hlist dt dd:first-child:before,.mw-parser-output .hlist dt dt:first-child:before,.mw-parser-output .hlist dt li:first-child:before,.mw-parser-output .hlist li dd:first-child:before,.mw-parser-output .hlist li dt:first-child:before,.mw-parser-output .hlist li li:first-child:before{content:" (";font-weight:normal}.mw-parser-output .hlist dd dd:last-child:after,.mw-parser-output .hlist dd dt:last-child:after,.mw-parser-output .hlist dd li:last-child:after,.mw-parser-output .hlist dt dd:last-child:after,.mw-parser-output .hlist dt dt:last-child:after,.mw-parser-output .hlist dt li:last-child:after,.mw-parser-output .hlist li dd:last-child:after,.mw-parser-output .hlist li dt:last-child:after,.mw-parser-output .hlist li li:last-child:after{content:")";font-weight:normal}.mw-parser-output .hlist ol{counter-reset:listitem}.mw-parser-output .hlist ol>li{counter-increment:listitem}.mw-parser-output .hlist ol>li:before{content:" "counter(listitem)"\a0 "}.mw-parser-output .hlist dd ol>li:first-child:before,.mw-parser-output .hlist dt ol>li:first-child:before,.mw-parser-output .hlist li ol>li:first-child:before{content:" ("counter(listitem)"\a0 "}</style><style data-mw-deduplicate="TemplateStyles:r4692751">.mw-parser-output .navbar{display:inline;font-size:88%;font-weight:normal}.mw-parser-output .navbar ul{display:inline;white-space:nowrap}.mw-parser-output .navbar li{word-spacing:-0.125em}.mw-parser-output .navbox .navbar{display:block;font-size:100%}.mw-parser-output .navbox-title .navbar{float:left;text-align:left;margin-right:0.5em;width:6em}</style><div class="plainlinks hlist navbar mini"><ul><li class="nv-view"><a href="/wiki/Template:Development_guidelines_navigation" title="Template:Development guidelines navigation"><abbr title="View this template" style=";;background:none transparent;border:none;box-shadow:none; padding:0;">v</abbr></a></li><li class="nv-talk"><a href="/wiki/Template_talk:Development_guidelines_navigation" title="Template talk:Development guidelines navigation"><abbr title="Discuss this template" style=";;background:none transparent;border:none;box-shadow:none; padding:0;">t</abbr></a></li><li class="nv-edit"><a class="external text" href="https://www.mediawiki.org/w/index.php?title=Template:Development_guidelines_navigation&amp;action=edit"><abbr title="Edit this template" style=";;background:none transparent;border:none;box-shadow:none; padding:0;">e</abbr></a></li></ul></div><div id="Development_guidelines" style="font-size:114%;margin:0 4em"><a href="/wiki/Special:MyLanguage/Development_guidelines" title="Special:MyLanguage/Development guidelines"><span style="font-weight: bold;">Development guidelines</span></a><span style="display:none"><a href="/wiki/Development_guidelines" title="Development guidelines"> </a></span></div></th></tr><tr><th scope="row" class="navbox-group" style="width:1%">Policies</th><td class="navbox-list1 navbox-list navbox-odd" style="width:100%;padding:0px"><div style="padding:0em 0.25em"> <ul><li><a href="/wiki/Special:MyLanguage/Development_policy" title="Special:MyLanguage/Development policy">Development policy</a><span style="display:none"><a href="/wiki/Development_policy" title="Development policy"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Wikimedia_Engineering_Architecture_Principles" title="Special:MyLanguage/Wikimedia Engineering Architecture Principles">Architecture principles</a><span style="display:none"><a href="/wiki/Wikimedia_Engineering_Architecture_Principles" title="Wikimedia Engineering Architecture Principles"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Wikimedia_services_policy" title="Special:MyLanguage/Wikimedia services policy">Services policy</a><span style="display:none"><a href="/wiki/Wikimedia_services_policy" title="Wikimedia services policy"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Support_policy_for_PHP" title="Special:MyLanguage/Support policy for PHP">Support policy for PHP</a><span style="display:none"><a href="/wiki/Support_policy_for_PHP" title="Support policy for PHP"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Gerrit/Privilege_policy" title="Special:MyLanguage/Gerrit/Privilege policy">Gerrit/Privilege policy</a><span style="display:none"><a href="/wiki/Gerrit/Privilege_policy" title="Gerrit/Privilege policy"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Stable_interface_policy" title="Special:MyLanguage/Stable interface policy">Stable interface policy</a><span style="display:none"><a href="/wiki/Stable_interface_policy" title="Stable interface policy"> </a></span> <ul><li><a href="/wiki/Special:MyLanguage/Stable_interface_policy/Frontend" title="Special:MyLanguage/Stable interface policy/Frontend">Frontend</a><span style="display:none"><a href="/wiki/Stable_interface_policy/Frontend" title="Stable interface policy/Frontend"> </a></span></li></ul></li> <li><a href="/wiki/Special:MyLanguage/MediaWiki_database_policy" title="Special:MyLanguage/MediaWiki database policy">MediaWiki database policy</a><span style="display:none"><a href="/wiki/MediaWiki_database_policy" title="MediaWiki database policy"> </a></span></li></ul> </div></td></tr><tr><th scope="row" class="navbox-group" style="width:1%">General guidelines</th><td class="navbox-list1 navbox-list navbox-even" style="width:100%;padding:0px"><div style="padding:0em 0.25em"> <ul><li><a href="/wiki/Special:MyLanguage/Security_for_developers" title="Special:MyLanguage/Security for developers">Security for developers</a><span style="display:none"><a href="/wiki/Security_for_developers" title="Security for developers"> </a></span></li> <li><a href="https://wikitech.wikimedia.org/wiki/Performance/Guides/Backend_performance_practices" class="extiw" title="wikitech:Performance/Guides/Backend performance practices">Performance guidelines</a></li> <li><a href="/wiki/Special:MyLanguage/Gerrit/Commit_message_guidelines" title="Special:MyLanguage/Gerrit/Commit message guidelines">Commit message guidelines</a><span style="display:none"><a href="/wiki/Gerrit/Commit_message_guidelines" title="Gerrit/Commit message guidelines"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Security_checklist_for_developers" title="Special:MyLanguage/Security checklist for developers">Security checklist for developers</a><span style="display:none"><a href="/wiki/Security_checklist_for_developers" title="Security checklist for developers"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Localisation" title="Special:MyLanguage/Localisation">Localisation</a><span style="display:none"><a href="/wiki/Localisation" title="Localisation"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Design/Living_style_guide" title="Special:MyLanguage/Design/Living style guide">Design style guide</a><span style="display:none"><a href="/wiki/Design/Living_style_guide" title="Design/Living style guide"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Documentation/Style_guide" title="Special:MyLanguage/Documentation/Style guide">Documentation/Style guide</a><span style="display:none"><a href="/wiki/Documentation/Style_guide" title="Documentation/Style guide"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Accessibility_guide_for_developers" title="Special:MyLanguage/Accessibility guide for developers">Accessibility guide for developers</a><span style="display:none"><a href="/wiki/Accessibility_guide_for_developers" title="Accessibility guide for developers"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Inclusive_language" title="Special:MyLanguage/Inclusive language">Inclusive language</a><span style="display:none"><a href="/wiki/Inclusive_language" title="Inclusive language"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Guidelines_for_a_healthy_code_review_culture" title="Special:MyLanguage/Guidelines for a healthy code review culture">Guidelines for a healthy code review culture</a><span style="display:none"><a href="/wiki/Guidelines_for_a_healthy_code_review_culture" title="Guidelines for a healthy code review culture"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Collaborative_programming" title="Special:MyLanguage/Collaborative programming">Collaborative programming</a><span style="display:none"><a href="/wiki/Collaborative_programming" title="Collaborative programming"> </a></span></li> <li><b><a href="/wiki/Special:MyLanguage/Best_practices_for_extensions" title="Special:MyLanguage/Best practices for extensions">Best practices for extensions</a></b><span style="display:none"><a class="mw-selflink selflink"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:Pre-commit_checklist" title="Special:MyLanguage/Manual:Pre-commit checklist">Pre-commit checklist</a><span style="display:none"><a href="/wiki/Manual:Pre-commit_checklist" title="Manual:Pre-commit checklist"> </a></span></li></ul> </div></td></tr><tr><th scope="row" class="navbox-group" style="width:1%">Code conventions</th><td class="navbox-list1 navbox-list navbox-odd" style="width:100%;padding:0px"><div style="padding:0em 0.25em"> <ul><li><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions#Code_structure" title="Special:MyLanguage/Manual:Coding conventions">All languages</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions#Code_structure" title="Manual:Coding conventions"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/PHP" title="Special:MyLanguage/Manual:Coding conventions/PHP">PHP</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/PHP" title="Manual:Coding conventions/PHP"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:PHP_unit_testing/Writing_unit_tests#Test_conventions" title="Special:MyLanguage/Manual:PHP unit testing/Writing unit tests">PHPUnit</a><span style="display:none"><a href="/wiki/Manual:PHP_unit_testing/Writing_unit_tests#Test_conventions" title="Manual:PHP unit testing/Writing unit tests"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/JavaScript" title="Special:MyLanguage/Manual:Coding conventions/JavaScript">JavaScript</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/JavaScript" title="Manual:Coding conventions/JavaScript"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/CSS" title="Special:MyLanguage/Manual:Coding conventions/CSS">CSS</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/CSS" title="Manual:Coding conventions/CSS"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/Selenium" title="Special:MyLanguage/Manual:Coding conventions/Selenium">Selenium</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/Selenium" title="Manual:Coding conventions/Selenium"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/Lua" title="Special:MyLanguage/Manual:Coding conventions/Lua">Lua</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/Lua" title="Manual:Coding conventions/Lua"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/Python" title="Special:MyLanguage/Manual:Coding conventions/Python">Python</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/Python" title="Manual:Coding conventions/Python"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/Java" title="Special:MyLanguage/Manual:Coding conventions/Java">Java</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/Java" title="Manual:Coding conventions/Java"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/SVG" title="Special:MyLanguage/Manual:Coding conventions/SVG">SVG</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/SVG" title="Manual:Coding conventions/SVG"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/Vue" title="Special:MyLanguage/Manual:Coding conventions/Vue">Vue</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/Vue" title="Manual:Coding conventions/Vue"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/Database" title="Special:MyLanguage/Manual:Coding conventions/Database">Database schemas</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/Database" title="Manual:Coding conventions/Database"> </a></span></li> <li><a href="/wiki/Special:MyLanguage/Product_Analytics/Style_guide" title="Special:MyLanguage/Product Analytics/Style guide">Analytics (Python, R, SQL)</a><span style="display:none"><a href="/wiki/Product_Analytics/Style_guide" title="Product Analytics/Style guide"> </a></span></li></ul> </div></td></tr><tr><th scope="row" class="navbox-group" style="width:1%">API client code</th><td class="navbox-list1 navbox-list navbox-even" style="width:100%;padding:0px"><div style="padding:0em 0.25em"> <ul><li><a href="/wiki/Special:MyLanguage/API:Client_code/Gold_standard" title="Special:MyLanguage/API:Client code/Gold standard">Standards for API client libraries</a><span style="display:none"><a href="/wiki/API:Client_code/Gold_standard" title="API:Client code/Gold standard"> </a></span></li></ul> </div></td></tr><tr><th scope="row" class="navbox-group" style="width:1%">Drafts</th><td class="navbox-list1 navbox-list navbox-odd" style="width:100%;padding:0px"><div style="padding:0em 0.25em"> <ul><li><i><a href="/wiki/Special:MyLanguage/Manual:Coding_conventions/Documentation" title="Special:MyLanguage/Manual:Coding conventions/Documentation">Code documentation</a><span style="display:none"><a href="/wiki/Manual:Coding_conventions/Documentation" title="Manual:Coding conventions/Documentation"> </a></span></i></li></ul> </div></td></tr></tbody></table></div> <!-- NewPP limit report Parsed by mw‐web.eqiad.main‐6b6c9bdc8b‐lxlzq Cached time: 20241103124329 Cache expiry: 2592000 Reduced expiry: false Complications: [show‐toc] DiscussionTools time usage: 0.032 seconds CPU time usage: 0.654 seconds Real time usage: 0.899 seconds Preprocessor visited node count: 6664/1000000 Post‐expand include size: 75945/2097152 bytes Template argument size: 23341/2097152 bytes Highest expansion depth: 16/100 Expensive parser function count: 70/500 Unstrip recursion depth: 0/20 Unstrip post‐expand size: 9085/5000000 bytes Lua time usage: 0.219/10.000 seconds Lua memory usage: 1963466/52428800 bytes Number of Wikibase entities loaded: 0/400 --> <!-- Transclusion expansion time report (%,ms,calls,template) 100.00% 559.175 1 -total 69.27% 387.351 99 Template:Ll 42.62% 238.340 1 Template:Conventions_navigation 41.57% 232.432 1 Template:Navbox 30.09% 168.255 198 Template:Translatable 26.06% 145.715 99 Template:Pagelang 10.32% 57.723 26 Template:TNTN 9.36% 52.320 1 Template:Development_guideline 8.40% 46.991 1 Template:Mbox 1.87% 10.448 1 Translations:Code_review/Page_display_title/en --> <!-- Saved in parser cache with key mediawikiwiki:pcache:idhash:708355-0!canonical and timestamp 20241103124329 and revision id 6471904. Rendering was triggered because: page-view --> </div><!--esi <esi:include src="/esitest-fa8a495983347898/content" /> --><noscript><img src="https://login.wikimedia.org/wiki/Special:CentralAutoLogin/start?type=1x1" alt="" width="1" height="1" style="border: none; position: absolute;"></noscript> <div class="printfooter" data-nosnippet="">Retrieved from "<a dir="ltr" href="https://www.mediawiki.org/w/index.php?title=Best_practices_for_extensions&amp;oldid=6471904">https://www.mediawiki.org/w/index.php?title=Best_practices_for_extensions&amp;oldid=6471904</a>"</div></div> <div id="catlinks" class="catlinks" data-mw="interface"><div id="mw-normal-catlinks" class="mw-normal-catlinks"><a href="/wiki/Special:Categories" title="Special:Categories">Category</a>: <ul><li><a href="/wiki/Category:Development_guidelines" title="Category:Development guidelines">Development guidelines</a></li></ul></div></div> </div> </main> </div> <div class="mw-footer-container"> <footer id="footer" class="mw-footer" > <ul id="footer-info"> <li id="footer-info-lastmod"> This page was last edited on 16 April 2024, at 11:04.</li> <li id="footer-info-copyright">Text is available under the <a rel="nofollow" class="external text" href="https://creativecommons.org/licenses/by-sa/4.0/deed.en">Creative Commons Attribution-ShareAlike License</a>; additional terms may apply. Text in <a class="external text" href="https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents">the Help: namespace</a> is available under the <a rel="nofollow" class="external text" href="https://creativecommons.org/publicdomain/zero/1.0/">Creative Commons CC0 License</a>. By using this site, you agree to the <a class="external text" href="https://foundation.wikimedia.org/wiki/Special:MyLanguage/Policy:Terms_of_Use">Terms of Use</a> and <a class="external text" href="https://foundation.wikimedia.org/wiki/Special:MyLanguage/Policy:Privacy_policy">Privacy Policy</a>.</li> </ul> <ul id="footer-places"> <li id="footer-places-privacy"><a href="https://foundation.wikimedia.org/wiki/Special:MyLanguage/Policy:Privacy_policy">Privacy policy</a></li> <li id="footer-places-about"><a href="/wiki/Project:About">About mediawiki.org</a></li> <li id="footer-places-disclaimers"><a href="/wiki/Project:General_disclaimer">Disclaimers</a></li> <li id="footer-places-wm-codeofconduct"><a href="https://www.mediawiki.org/wiki/Special:MyLanguage/Code_of_Conduct">Code of Conduct</a></li> <li id="footer-places-developers"><a href="https://developer.wikimedia.org">Developers</a></li> <li id="footer-places-statslink"><a href="https://stats.wikimedia.org/#/www.mediawiki.org">Statistics</a></li> <li id="footer-places-cookiestatement"><a href="https://foundation.wikimedia.org/wiki/Special:MyLanguage/Policy:Cookie_statement">Cookie statement</a></li> <li id="footer-places-mobileview"><a href="//m.mediawiki.org/w/index.php?title=Best_practices_for_extensions&amp;mobileaction=toggle_view_mobile" class="noprint stopMobileRedirectToggle">Mobile view</a></li> </ul> <ul id="footer-icons" class="noprint"> <li id="footer-copyrightico"><a href="https://wikimediafoundation.org/" class="cdx-button cdx-button--fake-button cdx-button--size-large cdx-button--fake-button--enabled"><img src="/static/images/footer/wikimedia-button.svg" width="84" height="29" alt="Wikimedia Foundation" loading="lazy"></a></li> <li id="footer-poweredbyico"><a href="https://www.mediawiki.org/" class="cdx-button cdx-button--fake-button cdx-button--size-large cdx-button--fake-button--enabled"><img src="/w/resources/assets/poweredby_mediawiki.svg" alt="Powered by MediaWiki" width="88" height="31" loading="lazy"></a></li> </ul> </footer> </div> </div> </div> <div class="vector-settings" id="p-dock-bottom"> <ul></ul> </div><script>(RLQ=window.RLQ||[]).push(function(){mw.config.set({"wgHostname":"mw-web.codfw.main-f69cdc8f6-nt2gf","wgBackendResponseTime":131,"wgDiscussionToolsPageThreads":[{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Workflow","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Code_architecture","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Documentation","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-File_structure","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Database","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Coding_conventions","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Testing","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Language","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Accessibility","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Security","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Don't_reinvent_/_abuse_MediaWiki","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Uncategorized","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-Meta","replies":[]},{"headingLevel":2,"name":"h-","type":"heading","level":0,"id":"h-See_also","replies":[]}],"wgPageParseReport":{"discussiontools":{"limitreport-timeusage":"0.032"},"limitreport":{"cputime":"0.654","walltime":"0.899","ppvisitednodes":{"value":6664,"limit":1000000},"postexpandincludesize":{"value":75945,"limit":2097152},"templateargumentsize":{"value":23341,"limit":2097152},"expansiondepth":{"value":16,"limit":100},"expensivefunctioncount":{"value":70,"limit":500},"unstrip-depth":{"value":0,"limit":20},"unstrip-size":{"value":9085,"limit":5000000},"entityaccesscount":{"value":0,"limit":400},"timingprofile":["100.00% 559.175 1 -total"," 69.27% 387.351 99 Template:Ll"," 42.62% 238.340 1 Template:Conventions_navigation"," 41.57% 232.432 1 Template:Navbox"," 30.09% 168.255 198 Template:Translatable"," 26.06% 145.715 99 Template:Pagelang"," 10.32% 57.723 26 Template:TNTN"," 9.36% 52.320 1 Template:Development_guideline"," 8.40% 46.991 1 Template:Mbox"," 1.87% 10.448 1 Translations:Code_review/Page_display_title/en"]},"scribunto":{"limitreport-timeusage":{"value":"0.219","limit":"10.000"},"limitreport-memusage":{"value":1963466,"limit":52428800}},"cachereport":{"origin":"mw-web.eqiad.main-6b6c9bdc8b-lxlzq","timestamp":"20241103124329","ttl":2592000,"transientcontent":false}}});});</script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10