{"id":13897074,"url":"https://github.com/Olivine-Labs/lua-style-guide","last_synced_at":"2025-07-17T13:32:16.824Z","repository":{"id":6263767,"uuid":"7496828","full_name":"Olivine-Labs/lua-style-guide","owner":"Olivine-Labs","description":"Olivine Labs Lua Style Guide","archived":false,"fork":false,"pushed_at":"2021-08-29T23:04:49.000Z","size":40,"stargazers_count":507,"open_issues_count":11,"forks_count":75,"subscribers_count":25,"default_branch":"master","last_synced_at":"2024-08-07T18:42:39.429Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Olivine-Labs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-01-08T06:32:32.000Z","updated_at":"2024-07-31T01:42:07.000Z","dependencies_parsed_at":"2022-09-15T17:13:37.416Z","dependency_job_id":null,"html_url":"https://github.com/Olivine-Labs/lua-style-guide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Olivine-Labs%2Flua-style-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Olivine-Labs%2Flua-style-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Olivine-Labs%2Flua-style-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Olivine-Labs%2Flua-style-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Olivine-Labs","download_url":"https://codeload.github.com/Olivine-Labs/lua-style-guide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226265524,"owners_count":17597223,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-06T18:03:19.884Z","updated_at":"2024-11-25T02:31:36.980Z","avatar_url":"https://github.com/Olivine-Labs.png","language":null,"funding_links":[],"categories":["Others","Resources"],"sub_categories":["Style Guides"],"readme":"# Lua Style Guide\n\nThis style guide contains a list of guidelines that we try to follow for our\nprojects. It does not attempt to make arguments for the styles; its goal is\nto provide consistency across projects.\n\nFeel free to fork this style guide and change to your own\nliking, and file issues / pull requests if you have questions, comments, or if\nyou find any mistakes or typos.\n\n\n## \u003ca name='TOC'\u003eTable of Contents\u003c/a\u003e\n\n  1. [Types](#types)\n  1. [Tables](#tables)\n  1. [Strings](#strings)\n  1. [Functions](#functions)\n  1. [Properties](#properties)\n  1. [Variables](#variables)\n  1. [Conditional Expressions \u0026 Equality](#conditionals)\n  1. [Blocks](#blocks)\n  1. [Whitespace](#whitespace)\n  1. [Commas](#commas)\n  1. [Semicolons](#semicolons)\n  1. [Type Casting \u0026 Coercion](#type-coercion)\n  1. [Naming Conventions](#naming-conventions)\n  1. [Accessors](#accessors)\n  1. [Constructors](#constructors)\n  1. [Modules](#modules)\n  1. [File Structure](#file-structure)\n  1. [Testing](#testing)\n  1. [Performance](#performance)\n  1. [Resources](#resources)\n  1. [In the Wild](#in-the-wild)\n  1. [Contributors](#contributors)\n  1. [License](#license)\n\n## \u003ca name='types'\u003eTypes\u003c/a\u003e\n\n  - **Primitives**: When you access a primitive type you work directly on its value\n\n    + `string`\n    + `number`\n    + `boolean`\n    + `nil`\n\n    ```lua\n    local foo = 1\n    local bar = foo\n\n    bar = 9\n\n    print(foo, bar) -- =\u003e 1\t9\n    ```\n\n  - **Complex**: When you access a complex type you work on a reference to its value\n\n    + `table`\n    + `function`\n    + `userdata`\n\n    ```lua\n    local foo = { 1, 2 }\n    local bar = foo\n\n    bar[0] = 9\n    foo[1] = 3\n\n    print(foo[0], bar[0]) -- =\u003e 9   9\n    print(foo[1], bar[1]) -- =\u003e 3   3\n    print(foo[2], bar[2]) -- =\u003e 2   2\t\t\n    ```\n\n    **[[⬆]](#TOC)**\n\n## \u003ca name='tables'\u003eTables\u003c/a\u003e\n\n  - Use the constructor syntax for table property creation where possible.\n\n    ```lua\n    -- bad\n    local player = {}\n    player.name = 'Jack'\n    player.class = 'Rogue'\n\n    -- good\n    local player = {\n      name = 'Jack',\n      class = 'Rogue'\n    }\n    ```\n\n  - Define functions externally to table definition.\n\n    ```lua\n    -- bad\n    local player = {\n      attack = function() \n      -- ...stuff...\n      end\n    }\n\n    -- good\n    local function attack()\n    end\n\n    local player = {\n      attack = attack\n    }\n    ```\n\n  - Consider `nil` properties when selecting lengths.\n    A good idea is to store an `n` property on lists that contain the length\n    (as noted in [Storing Nils in Tables](http://lua-users.org/wiki/StoringNilsInTables))\n\n    ```lua\n    -- nils don't count\n    local list = {}\n    list[0] = nil\n    list[1] = 'item'\n\n    print(#list) -- 0\n    print(select('#', list)) -- 1\n    ```\n\n  - When tables have functions, use `self` when referring to itself.\n\n    ```lua\n    -- bad\n    local me = {\n      fullname = function(this)\n        return this.first_name + ' ' + this.last_name\n      end\n    }\n\n    -- good\n    local me = {\n      fullname = function(self)\n        return self.first_name + ' ' + self.last_name\n      end\n    }\n    ```\n\n    **[[⬆]](#TOC)**\n\n## \u003ca name='strings'\u003eStrings\u003c/a\u003e\n\n  - Use single quotes `''` for strings.\n\n    ```lua\n    -- bad\n    local name = \"Bob Parr\"\n\n    -- good\n    local name = 'Bob Parr'\n\n    -- bad\n    local fullName = \"Bob \" .. self.lastName\n\n    -- good\n    local fullName = 'Bob ' .. self.lastName\n    ```\n\n  - Strings longer than 80 characters should be written across multiple lines \n    using concatenation. This allows you to indent nicely.\n\n    ```lua\n    -- bad\n    local errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'\n\n    -- bad\n    local errorMessage = 'This is a super long error that \\\n    was thrown because of Batman. \\\n    When you stop to think about \\\n    how Batman had anything to do \\\n    with this, you would get nowhere \\\n    fast.'\n\n\n    -- bad\n    local errorMessage = [[This is a super long error that\n      was thrown because of Batman.\n      When you stop to think about\n      how Batman had anything to do\n      with this, you would get nowhere\n      fast.]]\n\n    -- good\n    local errorMessage = 'This is a super long error that ' ..\n      'was thrown because of Batman. ' ..\n      'When you stop to think about ' ..\n      'how Batman had anything to do ' ..\n      'with this, you would get nowhere ' ..\n      'fast.'\n    ```\n\n    **[[⬆]](#TOC)**\n\n\n## \u003ca name='functions'\u003eFunctions\u003c/a\u003e\n  - Prefer lots of small functions to large, complex functions. [Smalls Functions Are Good For The Universe](http://kikito.github.io/blog/2012/03/16/small-functions-are-good-for-the-universe/).\n\n  - Prefer function syntax over variable syntax. This helps differentiate\n    between named and anonymous functions.\n\n    ```lua\n    -- bad\n    local nope = function(name, options)\n      -- ...stuff...\n    end\n\n    -- good\n    local function yup(name, options)\n      -- ...stuff...\n    end\n    ```\n\n  - Never name a parameter `arg`, this will take precendence over the `arg` object that is given to every function scope in older versions of Lua.\n\n    ```lua\n    -- bad\n    local function nope(name, options, arg) \n      -- ...stuff...\n    end\n\n    -- good\n    local function yup(name, options, ...)\n      -- ...stuff...\n    end\n    ```\n\n  - Perform validation early and return as early as possible.\n\n    ```lua\n    -- bad\n    local is_good_name = function(name, options, arg)\n      local is_good = #name \u003e 3\n      is_good = is_good and #name \u003c 30\n\n      -- ...stuff...\n\n      return is_bad\n    end\n\n    -- good\n    local is_good_name = function(name, options, args)\n      if #name \u003c 3 or #name \u003e 30 then return false end\n\n      -- ...stuff...\n\n      return true\n    end\n    ```\n\n  **[[⬆]](#TOC)**\n\n\n## \u003ca name='properties'\u003eProperties\u003c/a\u003e\n\n  - Use dot notation when accessing known properties.\n\n    ```lua\n    local luke = {\n      jedi = true,\n      age = 28\n    }\n\n    -- bad\n    local isJedi = luke['jedi']\n\n    -- good\n    local isJedi = luke.jedi\n    ```\n\n  - Use subscript notation `[]` when accessing properties with a variable\n    or if using a table as a list.\n\n    ```lua\n    local luke = {\n      jedi = true,\n      age = 28\n    }\n\n    local function getProp(prop) \n      return luke[prop]\n    end\n\n    local isJedi = getProp('jedi')\n    ```\n\n    **[[⬆]](#TOC)**\n\n\n## \u003ca name='variables'\u003eVariables\u003c/a\u003e\n\n  - Always use `local` to declare variables. Not doing so will result in\n    global variables to avoid polluting the global namespace.\n\n    ```lua\n    -- bad\n    superPower = SuperPower()\n\n    -- good\n    local superPower = SuperPower()\n    ```\n\n  - Assign variables at the top of their scope where possible. This makes it\n    easier to check for existing variables.\n\n    ```lua\n    -- bad\n    local bad = function()\n      test()\n      print('doing stuff..')\n\n      //..other stuff..\n\n      local name = getName()\n\n      if name == 'test' then\n        return false\n      end\n\n      return name\n    end\n\n    -- good\n    local function good()\n      local name = getName()\n\n      test()\n      print('doing stuff..')\n\n      //..other stuff..\n\n      if name == 'test' then\n        return false\n      end\n\n      return name\n    end\n    ```\n\n    **[[⬆]](#TOC)**\n\n\n## \u003ca name='conditionals'\u003eConditional Expressions \u0026 Equality\u003c/a\u003e\n\n  - False and nil are *falsy* in conditional expressions. All else is true.\n\n    ```lua\n    local str = ''\n\n    if str then\n      -- true\n    end\n    ```\n\n  - Use shortcuts when you can, unless you need to know the difference between\n    false and nil.\n\n    ```lua\n    -- bad\n    if name ~= nil then\n      -- ...stuff...\n    end\n\n    -- good\n    if name then\n      -- ...stuff...\n    end\n    ```\n\n  - Prefer *true* statements over *false* statements where it makes sense. \n    Prioritize truthy conditions when writing multiple conditions.\n\n    ```lua\n    --bad\n    if not thing then\n      -- ...stuff...\n    else\n      -- ...stuff...\n    end\n\n    --good\n    if thing then\n      -- ...stuff...\n    else\n      -- ...stuff...\n    end\n    ```\n\n  - Prefer defaults to `else` statements where it makes sense. This results in\n    less complex and safer code at the expense of variable reassignment, so\n    situations may differ.\n\n    ```lua\n    --bad\n    local function full_name(first, last)\n      local name\n\n      if first and last then\n        name = first .. ' ' .. last\n      else\n        name = 'John Smith'\n      end\n\n      return name\n    end\n\n    --good\n    local function full_name(first, last)\n      local name = 'John Smith'\n\n      if first and last then\n        name = first .. ' ' .. last\n      end\n\n      return name\n    end\n    ```\n\n  - Short ternaries are okay.\n\n    ```lua\n    local function default_name(name)\n      -- return the default 'Waldo' if name is nil\n      return name or 'Waldo'\n    end\n\n    local function brew_coffee(machine)\n      return machine and machine.is_loaded and 'coffee brewing' or 'fill your water'\n    end\n    ```\n\n\n    **[[⬆]](#TOC)**\n\n\n## \u003ca name='blocks'\u003eBlocks\u003c/a\u003e\n\n  - Single line blocks are okay for *small* statements. Try to keep lines to 80 characters.\n    Indent lines if they overflow past the limit.\n\n    ```lua\n    -- good\n    if test then return false end\n\n    -- good\n    if test then\n      return false\n    end\n\n    -- bad\n    if test \u003c 1 and do_complicated_function(test) == false or seven == 8 and nine == 10 then do_other_complicated_function()end\n\n    -- good\n    if test \u003c 1 and do_complicated_function(test) == false or\n        seven == 8 and nine == 10 then\n\n      do_other_complicated_function() \n      return false \n    end\n    ```\n\n    **[[⬆]](#TOC)**\n\n\n## \u003ca name='whitespace'\u003eWhitespace\u003c/a\u003e\n\n  - Use soft tabs set to 2 spaces. Tab characters and 4-space tabs result in public flogging.\n\n    ```lua\n    -- bad\n    function() \n    ∙∙∙∙local name\n    end\n\n    -- bad\n    function() \n    ∙local name\n    end\n\n    -- good\n    function() \n    ∙∙local name\n    end\n    ```\n\n  - Place 1 space before opening and closing braces. Place no spaces around parens.\n\n    ```lua\n    -- bad\n    local test = {one=1}\n\n    -- good\n    local test = { one = 1 }\n\n    -- bad\n    dog.set('attr',{\n      age = '1 year',\n      breed = 'Bernese Mountain Dog'\n    })\n\n    -- good\n    dog.set('attr', {\n      age = '1 year',\n      breed = 'Bernese Mountain Dog'\n    })\n    ```\n\n  - Place an empty newline at the end of the file.\n\n    ```lua\n    -- bad\n    (function(global) \n      -- ...stuff...\n    end)(self)\n    ```\n\n    ```lua\n    -- good\n    (function(global) \n      -- ...stuff...\n    end)(self)\n\n    ```\n\n  - Surround operators with spaces.\n\n    ```lua\n    -- bad\n    local thing=1\n    thing = thing-1\n    thing = thing*1\n    thing = 'string'..'s'\n\n    -- good\n    local thing = 1\n    thing = thing - 1\n    thing = thing * 1\n    thing = 'string' .. 's'\n    ```\n\n  - Use one space after commas.\n\n    ```lua\n    --bad\n    local thing = {1,2,3}\n    thing = {1 , 2 , 3}\n    thing = {1 ,2 ,3}\n\n    --good\n    local thing = {1, 2, 3}\n    ```\n\n  - Add a line break after multiline blocks.\n\n    ```lua\n    --bad\n    if thing then\n      -- ...stuff...\n    end\n    function derp()\n      -- ...stuff...\n    end\n    local wat = 7\n\n    --good\n    if thing then\n      -- ...stuff...\n    end\n\n    function derp()\n      -- ...stuff...\n    end\n\n    local wat = 7\n    ```\n\n  - Delete unnecessary whitespace at the end of lines.\n\n    **[[⬆]](#TOC)**\n\n## \u003ca name='commas'\u003eCommas\u003c/a\u003e\n\n  - Leading commas aren't okay. An ending comma on the last item is okay but discouraged.\n\n    ```lua\n    -- bad\n    local thing = {\n      once = 1\n    , upon = 2\n    , aTime = 3\n    }\n\n    -- good\n    local thing = {\n      once = 1,\n      upon = 2,\n      aTime = 3\n    }\n\n    -- okay\n    local thing = {\n      once = 1,\n      upon = 2,\n      aTime = 3,\n    }\n    ```\n\n    **[[⬆]](#TOC)**\n\n\n## \u003ca name='semicolons'\u003eSemicolons\u003c/a\u003e\n\n  - **Nope.** Separate statements onto multiple lines.\n\n    ```lua\n    -- bad\n    local whatever = 'sure';\n    a = 1; b = 2\n\n    -- good\n    local whatever = 'sure'\n    a = 1\n    b = 2\n    ```\n\n    **[[⬆]](#TOC)**\n\n\n## \u003ca name='type-coercion'\u003eType Casting \u0026 Coercion\u003c/a\u003e\n\n  - Perform type coercion at the beginning of the statement. Use the built-in functions. (`tostring`, `tonumber`, etc.)\n\n  - Use `tostring` for strings if you need to cast without string concatenation.\n\n    ```lua\n    -- bad\n    local totalScore = reviewScore .. ''\n\n    -- good\n    local totalScore = tostring(reviewScore)\n    ```\n\n  - Use `tonumber` for Numbers.\n\n    ```lua\n    local inputValue = '4'\n\n    -- bad\n    local val = inputValue * 1\n\n    -- good\n    local val = tonumber(inputValue)\n    ```\n\n    **[[⬆]](#TOC)**\n\n\n## \u003ca name='naming-conventions'\u003eNaming Conventions\u003c/a\u003e\n\n  - Avoid single letter names. Be descriptive with your naming. You can get\n    away with single-letter names when they are variables in loops.\n\n    ```lua\n    -- bad\n    local function q() \n      -- ...stuff...\n    end\n\n    -- good\n    local function query() \n      -- ..stuff..\n    end\n    ```\n\n  - Use underscores for ignored variables in loops.\n\n    ```lua\n    --good\n    for _, name in pairs(names) do\n      -- ...stuff...\n    end\n    ```\n\n  - Use snake_case when naming objects, functions, and instances. Tend towards\n    verbosity if unsure about naming.\n\n    ```lua\n    -- bad\n    local OBJEcttsssss = {}\n    local thisIsMyObject = {}\n    local this-is-my-object = {}\n\n    local c = function()\n      -- ...stuff...\n    end\n\n    -- good\n    local this_is_my_object = {}\n\n    local function do_that_thing()\n      -- ...stuff...\n    end\n    ```\n\n  - Use PascalCase for factories.\n\n    ```lua\n    -- bad\n    local player = require('player')\n\n    -- good\n    local Player = require('player')\n    local me = Player({ name = 'Jack' })\n    ```\n\n    **[[⬆]](#TOC)**\n\n  - Use `is` or `has` for boolean-returning functions that are part of tables.\n\n    ```lua\n    --bad\n    local function evil(alignment)\n      return alignment \u003c 100\n    end\n\n    --good\n    local function is_evil(alignment)\n      return alignment \u003c 100\n    end\n    ```\n\n## \u003ca name='modules'\u003eModules\u003c/a\u003e\n\n  - The module should return a table or function.\n  - The module should not use the global namespace for anything ever. The\n    module should be a closure.\n  - The file should be named like the module.\n\n    ```lua\n    -- thing.lua\n    local thing = { }\n\n    local meta = {\n      __call = function(self, key, vars)\n        print key\n      end\n    }\n\n\n    return setmetatable(thing, meta)\n    ```\n\n  - Note that modules are [loaded as singletons](http://lua-users.org/wiki/TheEssenceOfLoadingCode)\n    and therefore should usually be factories (a function returning a new instance of a table)\n    unless static (like utility libraries.)\n\n  **[[⬆]](#TOC)**\n\n## \u003ca name='file-structrure'\u003eFile Structure\u003c/a\u003e\n\n  - Files should be named in all lowercase.\n  - Lua files should be in a top-level `src` folder. The main library file should \n    be called `modulename.lua`.\n  - Rockspecs, license, readme, etc should be in the top level.\n  - Tests should be in a top-level `spec` folder.\n  - Executables should be in a top-level `bin` folder.\n  - Example:\n\n    ```\n    ./my_module\n      bin/\n        script.sh\n\n      spec/\n        my_module_spec.lua\n        some_file.lua\n\n      src/\n        my_module.lua\n        some_file.lua\n\n      README.md\n      LICENSE.md\n    ```\n\n## \u003ca name='testing'\u003eTesting\u003c/a\u003e\n\n  - Use [busted](http://olivinelabs.com/busted) and write lots of tests in a /spec \n    folder. Separate tests by module.\n  - Use descriptive `describe` and `it` blocks so it's obvious to see what\n    precisely is failing.\n  - Test interfaces. Don't test private methods. If you need to test something\n    that is private, it probably shouldn't be private in the first place.\n  - Example:\n\n    ```\n    ./my_module\n      bin/\n        script.sh\n\n      spec/\n        my_module_spec.lua\n\n        util/\n          formatters_spec.lua\n\n      src/\n        my_module.lua\n\n        util/\n          formatters.lua\n\n      README.md\n      LICENSE.md\n    ```\n\n    **[[⬆]](#TOC)**\n\n## \u003ca name='contributors'\u003eContributors\u003c/a\u003e\n\n  - [View contributors](https://github.com/Olivine-Labs/lua-style-guide/graphs/contributors)\n\n    **[[⬆]](#TOC)**\n\n## \u003ca name='license'\u003eLicense\u003c/a\u003e\n\n  - Released under CC0 (Public Domain).\n    Information can be found at [http://creativecommons.org/publicdomain/zero/1.0/](http://creativecommons.org/publicdomain/zero/1.0/).\n\n**[[⬆]](#TOC)**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOlivine-Labs%2Flua-style-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FOlivine-Labs%2Flua-style-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOlivine-Labs%2Flua-style-guide/lists"}