Reading/Web/Coding conventions

This document briefly describes guidelines for writing code for projects owned by the Web team. In general, we strive for compliance with MediaWiki code conventions. However, additions and deviations from these conventions are noted here.

Design principles

 * Inheritance should be avoided. Favor composition over inheritance and use interfaces (in PHP) where polymorphism is needed. New PHP classes should use the  keyword by default to discourage misuse internally and by third-parties.


 * Classes should be kept small and their purpose should be well defined. Avoid cramming too many responsibilities into one class. It might especially be tempting to do this in classes that have generic sounding names (e.g SkinMinerva.php), but don't do it! It's much easier to understand and change classes that follow the single responsibility principle.


 * Write your best and help others become better programmers. Patches should be small as is practical, deliberate, and meticulous. For paid contributors (staff), they should be no less than professional and set a good example.

Indenting
e.g. (function { var foo;
 * Always use tabs for indents.
 * Always indent - even inside JavaScript closures

Commit Messages
The commit message should provide enough context for a reviewer to understand the reason for and the reasoning around the change. Therefore, an ideal commit message includes: Wherever possible, the commit message should include links to supporting information, which will most likely be documentation.
 * 1) A brief statement of problem, which, in the case of stories and bugs, might also include a summary of the discussion on the associated Phabricator task
 * 2) A high-level description of the solution
 * 3) If applicable, why the solution was chosen over its alternatives
 * 4) Issues/concerns with the chosen solution, e.g. performance concerns, which, in the case of the mobile web, are prevalent

Commit footer labels

 * Bugs and Stories: If the commit is associated with a Phabricator task, the commit message should finish with  where TXXXX is the bug number in Phabricator. There should be no whitespace between this line and the Gerrit   so that it gets reported to Phabricator by the gerrit bot.
 * Dependencies: When committing a patch with a dependency or dependencies, be sure to include  where   is the gerrit Change-Id of the commit that needs to be merged beforehand. When multiple dependencies exist be sure to list them on separate lines. When doing this Jenkins will not allow the merging of your code unless the dependency is present.

Filenames
File names should use camelCase. In case of PHP files, the name should start with a capital letter and should be named after the main class they contain. If a JavaScript file defines a constructor, then its name should also start with a capital letter. Other files should be started with a lowercase letter.

Do not use the  prefix.

PHP test files should be suffixed with, JavaScript test files should be suffixed with  e.g..

Template and Less/CSS files used only by a single class in JavaScript should be named after the class, e.g..

Code deprecation
The code, Api and functions of some of our projects such as MobileFrontend can be depended upon by other extensions. Removing existing interfaces or public functions can impair the functionality in these projects. To avoid problems when removing code from these types of projects, it's advised to deprecate it before really removing it from the code base. That allows other extensions to still use the existing functions, but get a warning message that the code will be removed in a future release.

When deprecating or removing there is no need to update any release notes unlike in core.

Deprecation in PHP
See Deprecation policy

There are two things that should be added to code when it is deprecated. If Foo is replaced by another function, you should still return the result of the new function to keep Foo functional with the same call signature and result format.
 * 1) First it needs to be marked as such in the source code with comments (typically, adding   to the function's Docblock), and all callers should be fixed.
 * 2) It also should be marked with  . This will throw errors to any developers still using the code. All callers (in extensions and core) should have since been fixed, but this helps weed out stragglers over the next version. The first argument should be   to use the name of the method in the deprecation. Or more detailed text if you are deprecating just a small chunk of code. The second argument must be the version of MediaWiki you are deprecating the method in. This will allow deprecations to be filtered and also tracked.  For example:

Deprecation in JavaScript
Like in PHP, you should document the depreaction of code in JavaScript, too (e.g. in the docblock). Unlike in PHP, deprecation in JavaScript works in another way. Instead of adding a function which will create a deprecation notice, you use  to define a function in an object, which will, when accessed, produce a deprecation warning in the console with a backtrace. E.g., if you want to deprecate function foo in object bar, you could do something like:

PHP
We adhere to Manual:Coding conventions

Config variables
Global config variables set by the extension or skin, need to identify themselves in some way, by general MW convention.

Config variables should be camelCased and prefixed with $wgExtensionName (or an abbreviated version e.g. MF for MobileFrontend) or $wgVector (for skins)

Examples:
 * $wgMFPhotoUploadEndpoint
 * $wgMobileFrontendLogo

Previously '$wgMobileFrontend' was used but was abandoned in favor of '$wgMF...' because of brevity.

ResourceLoaderModules
When naming a ResourceLoader module in Resources.php, the module name should reflect the functionality it offers and where. For example, the name  describes a module containing styles that are only applied to the Special:MobileDiff page,   describes scripts that should only be applied to the Special:MobileDiff.

Try to group modules by their type:
 * modules related to special pages should start with
 * modules related to the Minerva skin used by MobileFrontend should start with
 * modules implementing a particular feature should start with a word describing it, e.g.

There is usually no need include 'beta' or 'alpha' in the module name. Please try to use comments instead to describe where those features reside.

Naming functions

 * When returning true or false the function should be prefixed with 'is' or 'has' e.g. isBetaGroupMember, isLoggedIn etc. Functions should be camelCased.

ResourceLoader modules
When naming classes try to avoid the MF prefix and instead try and describe what it does differently from a normal ResourceLoader module. Examples:


 * MobileSiteModule
 * MobileDeviceDetectModule.php

All classes should be written on the basis that one day they might make it into the desktop site. Note: MFResourceLoaderModule needs renaming - it pre-parses messages and provides template rendering.

i18n messages
If your message contains a single quote/apostrophe, wrap the entire message in double quotes rather than escaping the single quote.

Mustache
Mustache properties must be typed at the top of each Mustache file. For example, at the time of this writing, the VectorTabs.mustache file looks like:

Comments and documentation
Function input and output types must be documented with JSDoc syntax.

URL Routing (Use of the URL hash)
When navigating to a route always prefix it with '/' to distinguish it from element IDs. e.g.  not.

File organisation
Always use camelCase. Group related files in the resources folder describing the functionality.

Naming conventions
Standard MediaWiki naming conventions apply. Any JavaScript must pass the Manual:Coding_conventions/JavaScript.

Use camelCase for variable names.

When naming events use lowercase letters and a hyphen as the word separator ( is good,   is bad).

Start constructor functions with capital letters.

Use  as the name of the variable holding event data in event handlers.

Modules
Each JavaScript file can be a module, i.e. can expose some functionality to other JavaScript files.

A module has one of two purposes
 * 1) Provide reusable functionality via a class and the use of M.define: A module should only create one class and should use an uppercase character when naming the class e.g. Toast, Api, EditorOverlay. The module may also define an instance of itself via M.define - e.g. M.define( 'api' ) = new Api
 * 2) Execute some code providing some new functionality: This tends to use classes defined in other modules.

When writing modules be sure to wrap each module (in fact, every JavaScript file) in a closure.

Use closure's arguments to alias  and   objects as  and   respectively (but only if the module needs them):

Expose module's functionality using :

Module's name should be the same as module's file name (without the extension).

If the only thing exposed by a module is a class/constructor function, then the module name (and file name) should be capitalized:

Use other module's functionality using :

jQuery
Use  rather than   when creating new DOM nodes (or even better, use templates).

Use  to bind events rather than other convenience functions such as click

Use  or   as a return value of asynchronous functions. Use / instead of  / /  as only the former is ES6 Promise compatible.

Use jQuery objects in favor of native DOM elements (for the sake of consistency).

Avoid using  unless necessary. Most of the JavaScript files are loaded at the bottom of the page anyway.

Avoid jQuery/HTML spaghetti code, instead use  and Hogan templates (same syntax as Mustache).

Transpiling
We use transpiling in code repositories such as Extension:Popups

Given that ES6 template literals provide similar readability to template and are part of JavaScript itself, we consider this to be a favorable and sustainable alternative to Mustache templates where available. Additionally, although the usage of template strings requires transpilation, adding transpiling support enables other ES6 syntaxes to be used such as let / const, arrow functions, and destructuring, all of which are considered language improvements that Extension:Popups can leverage in many areas.

When transpiling, extra care is necessary in code review to pay heed to browser support for native functions e.g. Object.assign; Array.prototype.include; given using modern JavaScript gives the false illusion that browser support is relatively modern. It is not feasible to use a linter to check for forbidden methods (see phab:T190104) so take care!

CSS/Less
Ensure all CSS passes the Manual:Coding_conventions/CSS.

Naming Conventions
The current convention for Vector styles follows a BEM-like structure wherein the following naming pattern is used:

vs.

Use lowercase letters and a hyphen as the word separator in class and id names ( is good,   is bad,   is bad either for non-English native devs).

The decision not to adhere to a strict BEM approach at this time was informed ultimately by the difficulty in enforcing BEM structure through linting rules.

Lengthy discussion about naming conventions/methodologies continues to evolve and as guidelines are agreed upon (i.e. flattening grandchildren, capping class names, etc), the class naming conventions will be updated accordingly.

Colors
Reuse colors in variables.less file. Do not introduce new colours outside this variable file, especially when you work with the color gray.

Icons
Use ResourceLoaderOOUIIconPackModule for generating icon definitions. Do not ship SVGs where possible.

HTML
HTML should validate via W3C validator. Note, at the current time do not worry about markup introduced via wikitext and the 1 validation error that occurs due to MediaWiki core.

Tests
Frontend and backend changes should be accompanied by unit tests where practical.

QUnit

 * All QUnit tests should be suffixed with '.test' e.g. 'foo.js' should be tested by 'foo.test.js'

e.g. resources/module.name/foo.js should have a test as tests/qunit/module.name/foo.test.js e.g. QUnit.module( 'MobileFrontend modules/editor/EditorApi' )
 * Tests should mirror the directory structure of the thing they are testing
 * QUnit "modules" (not to be confused with ResourceLoader modules) should be named after the JavaScript module they are testing and prefixed with

PHPUnit

 * All PHPUnit tests should be suffixed with '_test'

Browser tests
Please look at the guidelines for writing browser tests.