Help:Lua/Lua best practice

As any "best practice", this Lua best practice is heavily influenced by any individual contributors impression of what should be good practice. Use whatever you believe is important on your specific project, but do also know that these rules have been found to work!

The examples in the following are for Lua, but it should be easy to reformulate for JavaScript, PHP, and Python. The reason for giving examples for Lua is that modules for this language are used a lot, often by non-programmers that does not see the consequence of their choices.

As a general advice it can be a great help to at least import the content into a proper programming environment and check it there for obvious flaws and errors. Several free options exist, among them Eclipse, TextMate, and Visual Studio Code just to mention a few.

Especially, use a lint tool like lualint or luacheck. The later is somewhat better. Check also if there are tools for mess detection available for your programming environment.

Remember; consistent style is more important than than the "right" style – even if that style is the one you prefer!

Names

 * Descriptive names
 * Chose descriptive names, avoid overly generic names like  unless your code is truly generic. Don't repeat a description on several levels, like a method name set to the same as a class name, unless the method in fact return a class of that type. Think colorful words, more descriptive for larger scopes, but throw away unneeded words.


 * Short names
 * Cryptic short names should be avoided. Spell out the names, but stay concise. Don't use, say instead   or  . Often you can reformulate the sort names and still keep them short, like   and  . Use a plural name for tables holding several values for example.


 * Iterator names
 * Iterators usually have short names in Lua, and the names carry special meaning. This can create problems if several nested loops shall use the same names. A common solution is to prefix or suffix an additional part to the name, usually from the plural form used by the collection, or to use a singular form of the name of the collection.


 * Names with types
 * Because Lua has a very weak type system arguments should either be transformed to the correct type, or the function should throw an error. If the function exists in special incarnations to handle different types, then that should be part of the function name.


 * Acronyms as names
 * Acronyms are intimidating for those that does not know their meaning. Do not use them unless it the simplest and most obvious cases. In a library for creating HTML markup  might be acceptable, but in a library for creating infoboxes the same may not be acceptable.


 * Format names
 * Formatting of names should follow the convention on the actual project. Lua as such uses several formatting styles, but in Wikimedia-projects it seems to be UpperCamelCase for class names, and lowerCamelCase for function, method and variable names. Module names seems to be interpreted as variable names. There are no clear practice for constants, but UPPER_CASE_EMBEDDED_UNDERSCORE seems like a wise bet.


 * Single underscore
 * In Lua a single underscore name is used wherever a name must be provided, but it is not used. That is it is a placeholder that is otherwise ignored. This is recognized by some lint tools, and should also be respected in ordinary code.


 * Leading underscore
 * By convention a leading underscore is used for names that are somewhat private but of some reason must be placed so it is accessible from the outside. Variables with such names should be treated as private to whatever defined them, unless they have some very clear explanation that say you can do some specific operations on them.

Formatting

 * Limit line length
 * Lines should be limited to a reasonable length, typically around 80 characters. The reason is that long lines are hard to read, but more import, long lines tend to contain several intermingled concepts and editing them will tend to create new bugs.


 * Indentation of newlines
 * Programmers have opinions on how to do indentation of newlines. Whatever opinion you have, do remember that crowdsourced projects should adhere to some common standards. Note that while a tab looks like the same as 4 spaces in the online editor, it does not show up like this in the normal view. Because the online editor do indentation in specific ways you should set your programming environment to act the same way.


 * Binaries and linebreaks
 * Sometimes it is necessary to put a linebreak inside an expression, typically an if-clause. This pose a special problem, as it opens up for copy-paste induced errors. To avoid problems a simple formatting tricks can be used, simply put the logical operator in front and the binary on a new line.
 * {| class="wikitable mw-collapsible mw-collapsed"

!colspan="2"|Example !Good !Bad
 * - valign="top"
 * }
 * Whitespace
 * Whitespace is usually a GoodThing™, but people tend to disagree on how to use it. Done right it tend to increase readability of your code, but done wrong your code can be really hard to read. Find a style guide and stick to it. Or use a tool.
 * Whitespace
 * Whitespace is usually a GoodThing™, but people tend to disagree on how to use it. Done right it tend to increase readability of your code, but done wrong your code can be really hard to read. Find a style guide and stick to it. Or use a tool.

Documentation

 * Interface documentation
 * The interface is the exposed functions from the module, that is the functions you can invoke from the parser function. As a minimum document whatever arguments the interface function takes, and whatever value it return. Only a single value is used from whatever the function returns. Remember to document if and how the metafunction  will transform the value.


 * Overview documentation
 * The documentation should have an overview to give a general explanation of why and how the module, function, or variable is the way it is. This is not just the intended behaviour, it is why the behaviour is the way it is.


 * Parameter annotation
 * Each parameter should be properly annotated. That is the expected type should be given and a short descriptive string. Write as if LDoc or a similar tool was available, usually as.


 * Return annotation
 * Each return value should be properly annotated. That is the expected type should be given and a short descriptive string. Write as if LDoc or a similar tool was available, usually as.

Comments
Inline comments for the code itself.


 * Why-form of comments
 * Comments are not for you who code some algorithm, they are for the one who reads your code. In the code path there should be answers to the how-questions, and in the comments there should be answers to the why-questions. Do not fall in the trap of replicating what you do in the code path in the comments, good comments provide new insight into the code.


 * State your intent
 * State the intent of your code, in a clear and precise language. Do not just add a lot of words, think about the reader, what do she need to know to understand your code.


 * Refactor on comments
 * When the code has become to complex, it is common to start explaining the code for yourself. If you need your own comments, then something has gotten to involved and you should refactor the code.


 * Comments on bad names
 * A comment saying that a name is bad in some respect does not help. Do not comment on bad names, fix them!


 * Todo comments
 * The marker todo is one of several common ones, and can be interpreted as "code the main programmer has not gotten around to fix yet". Usually you should write it as.


 * Fixme comments
 * The marker fixme is one of several common ones, and can be interpreted as "code other than main programmer has identified as broken". Usually you should write it as . This is an invite to other to try to fix the code.


 * Hack comments
 * The marker hack is one of several common ones, and can be interpreted as "code other than main programmer has identified as broken and tried fixing in an unelegant way". Usually you should write it as . This is an invite to other to try to fix the code.


 * XXX comments
 * The marker xxx is one of several common ones, and can be interpreted as "code identified as dangerously broken". Usually you should write it as . This marker should not be in production code, the code should be fixed asap.


 * Comments on magic numbers
 * Constants and other magic numbers should have comments explaining why they has the specific content. Why is the line length set to 80 chars? Why is π truncated to 3.14?

Code
Small patterns and coding practices.


 * Use patterns
 * A lot of work has gone into identifying common software design patterns, and developing good general solutions to those patterns. If you suspect you are attempting to do something that has a common pattern then do a search and check out how to implement the pattern. Still, know that patterns for languages with closures can be very different from those without closures. Usually Lua use tables to create objects, but can also use closures.


 * String quotes
 * In Lua there are several types of string quotes. Pick one of them and stick with it. If you need several types of quotes, either use your typical quote at the outermost level or at the innermost, and work your way out. Inside out with single quote as the primary quote and double quotes as secondary seems to be common, but could simply accidental. If more quotes are necessary, then start using double square brackets at the ternary level.


 * Return early
 * Return as soon as you can, especially if something fails. Usually programmers are told to stick with a single return point, but due to the language constructs in Lua this seems to create deeply nested code. It is thus better to return early.
 * {| class="wikitable mw-collapsible mw-collapsed"

!colspan="2"|Example !Good !Bad
 * - valign="top"
 * }
 * Avoid else
 * All conditionals can be written so en explicit else-clause goes away. All of them. But sometimes the else-clause makes the code easier to read or make double set of if-clauses go away. In those cases it is acceptable to keep the, otherwise it should be removed. Always be suspicious about else-clauses.
 * Avoid else
 * All conditionals can be written so en explicit else-clause goes away. All of them. But sometimes the else-clause makes the code easier to read or make double set of if-clauses go away. In those cases it is acceptable to keep the, otherwise it should be removed. Always be suspicious about else-clauses.


 * Truthy first
 * Usually you are free to order the order of then- and else-clauses as you see fit, and then you should order them so the if-clause has a positive test. This makes the code easier to read. If you can avoid the else-clause that is although more important.
 * {| class="wikitable mw-collapsible mw-collapsed"

!colspan="2"|Example !Good !Bad
 * - valign="top"
 * }
 * Interrogated first
 * Usually the interrogated value goes first in a logical expression, as this is is more natural to read and thus less error prone. The inverted form is sometimes called the Yoda form. Sometimes (but rarely) the natural form does not work as expected, and the code terminates. This can be because Lua fails figure out a proper tail recursive form of the expression, and then the stack overflows.
 * {| class="wikitable mw-collapsible mw-collapsed"
 * Usually the interrogated value goes first in a logical expression, as this is is more natural to read and thus less error prone. The inverted form is sometimes called the Yoda form. Sometimes (but rarely) the natural form does not work as expected, and the code terminates. This can be because Lua fails figure out a proper tail recursive form of the expression, and then the stack overflows.
 * {| class="wikitable mw-collapsible mw-collapsed"

!colspan="2"|Example !Good !Bad
 * - valign="top"
 * }
 * Provide defaults
 * In languages with an  operator like in Lua it is very easy to provide defaults. This is nice as some common constructs fails badly if given an uninitialized value. This happen for example when a table is unitialized and the code tries to index the nil value.
 * {| class="wikitable mw-collapsible mw-collapsed"
 * In languages with an  operator like in Lua it is very easy to provide defaults. This is nice as some common constructs fails badly if given an uninitialized value. This happen for example when a table is unitialized and the code tries to index the nil value.
 * {| class="wikitable mw-collapsible mw-collapsed"

!colspan="2"|Example !Good !Bad
 * - valign="top"
 * }
 * Binary conditional
 * Lua does not have an binary conditional operator, but you can do the same anyhow with an . What goes on is although a little mysterious to newcomers to Lua. The   and   pass on truthy values, and thus they can be part of other expressions than just logical ones.
 * {| class="wikitable mw-collapsible mw-collapsed"
 * Lua does not have an binary conditional operator, but you can do the same anyhow with an . What goes on is although a little mysterious to newcomers to Lua. The   and   pass on truthy values, and thus they can be part of other expressions than just logical ones.
 * {| class="wikitable mw-collapsible mw-collapsed"

!colspan="2"|Example !Good !Bad
 * - valign="top"
 * }
 * Ternary conditional
 * Lua does not have an ternary conditional operator, but you can do the same anyhow with an  and an  . What goes on is although a little mysterious to newcomers to Lua. The   and   pass on truthy values, and thus they can be part of other expressions than just logical ones.
 * {| class="wikitable mw-collapsible mw-collapsed"
 * Lua does not have an ternary conditional operator, but you can do the same anyhow with an  and an  . What goes on is although a little mysterious to newcomers to Lua. The   and   pass on truthy values, and thus they can be part of other expressions than just logical ones.
 * {| class="wikitable mw-collapsible mw-collapsed"

!colspan="2"|Example !Good !Bad
 * - valign="top"
 * }
 * Avoid repeat…until
 * The loop construct  delays the test until after the loop. This is error prone and should be avoided. The only times this is acceptable is when the test itself is costly compared to the executed block, or avoiding the repeat-clause would imply additional cleanup or finalize code.
 * Avoid repeat…until
 * The loop construct  delays the test until after the loop. This is error prone and should be avoided. The only times this is acceptable is when the test itself is costly compared to the executed block, or avoiding the repeat-clause would imply additional cleanup or finalize code.


 * Test arguments
 * There is a small utility libraryUtil to test arguments types, and it should be used to avoid simple mistakes. Often the arguments are valid when other code is correct, but in fringe cases something creates a call outside the expected type range and the code fails in mysterious ways. Proper testing might catch an error early, and increase the probability of discovering the real root cause. Still note that type testing is a test for failure, and not a test for correctness.
 * {| class="wikitable mw-collapsible mw-collapsed"

!colspan="2"|Example !Good !Bad
 * - valign="top"
 * }
 * }
 * }

Patterns
A few very common software patterns.


 * Command pattern
 * The single most common software pattern in Lua modules, that is most commonly missing, is the command pattern. Some data is available, and some action should be chosen given this data. A typical smell that a command pattern is missing is long chains of if-then-elseif-end tests. The most common variant does not have named commands, but an accept function.
 * {| class="wikitable mw-collapsible mw-collapsed"

!colspan="2"|Example
 * }
 * }
 * }

Testability
About testing and how to make the code testable. Note that the original book from the gang of four has patterns for strongly typed languages, which does not fit weakly typed languages very well.


 * Complexity
 * There are usually several execute paths through a given code, and this makes the code difficult to understand. The more code paths the more difficult the code become. This can be measured as cyclomatic complexity, and is a count of paths by counting branch points. This gives a fair, but not very good measure on code quality.


 * Avoid globals
 * Functions with globals are hard to test, no matter how the globals are included in the code. To avoid this accidental trap libraries can be included to wet the module, like . Still note that requiring some code is like importing a global, so be careful not to create a bigger problem.
 * {| class="wikitable mw-collapsible mw-collapsed"

!colspan="2"|Example !Good !Bad
 * - valign="top"
 * }
 * }
 * }

Programming environment
Some advices for how to set up your external programming environment.

Visual Studio Code
Use something like the following extensions.
 * Lua
 * vscode-luacheck
 * This needs a file  with a single line , otherwise it will complain a lot about the   global.
 * Lua snippets for Wikimedia

Other tools
This is tools you might find useful.
 * GitHub: LuaDist/luametrics

Reading

 * Kerievsky, Joshua; Refactoring to Patterns
 * Boswell, Dustin; Foucher, Trevor; The Art of Readable Code
 * Stefanov, Stoyan; JavaScript Patterns
 * Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John; Design Patterns: Elements of Reusable Object-Oriented Software
 * Programming in Lua (5.0)
 * Lua 5.1 Reference Manual
 * LuaUsers: Sample Code
 * PEP 8 -- Style Guide for Python Code