{"id":15008103,"url":"https://github.com/shaharband/lua-style-guide","last_synced_at":"2026-02-07T00:33:06.595Z","repository":{"id":255771868,"uuid":"853039333","full_name":"ShaharBand/lua-style-guide","owner":"ShaharBand","description":"style guide for Lua ","archived":false,"fork":false,"pushed_at":"2024-10-12T20:58:30.000Z","size":286,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-12T08:24:08.781Z","etag":null,"topics":["lua","style-guide","styleguide"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ShaharBand.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-09-05T21:55:43.000Z","updated_at":"2024-10-12T20:58:33.000Z","dependencies_parsed_at":null,"dependency_job_id":"2df2a9eb-e37e-4099-99ae-b2b8c6915aa3","html_url":"https://github.com/ShaharBand/lua-style-guide","commit_stats":null,"previous_names":["shaharband/lua-style-guide"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ShaharBand/lua-style-guide","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShaharBand%2Flua-style-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShaharBand%2Flua-style-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShaharBand%2Flua-style-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShaharBand%2Flua-style-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ShaharBand","download_url":"https://codeload.github.com/ShaharBand/lua-style-guide/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShaharBand%2Flua-style-guide/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266774795,"owners_count":23982247,"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","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["lua","style-guide","styleguide"],"created_at":"2024-09-24T19:15:09.382Z","updated_at":"2026-02-07T00:33:06.586Z","avatar_url":"https://github.com/ShaharBand.png","language":null,"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg style=\"display: block; -webkit-user-select: none; margin: auto; cursor: zoom-in; background-color: hsl(0, 0%, 90%);\" src=\"/luaa.gif\" width=\"250\" height=\"250\"/\u003e\n\u003c/div\u003e\n\n# Lua Style Guide\n\nInspired by Python PEP8, this style guide provides conventions and best practices for writing clean, readable, and maintainable Lua code. \nBy adhering to these guidelines, you can ensure consistency across your codebase and enhance collaboration with other developers.\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[![License: MIT](https://img.shields.io/badge/License-MIT-green2.svg)](LICENSE)\n[![last commit](https://img.shields.io/github/last-commit/ShaharBand/lua-style-guide.svg)](https://github.com/ShaharBand/lua-style-guide/commits/main) \n[![stars](https://img.shields.io/github/stars/ShaharBand/lua-style-guide.svg?style=badge)](https://github.com/ShaharBand/lua-style-guide/stargazers) \n\u003cbr\u003e \n\n## Table of Contents\n1. [Variables](#1-variables)\n   1. [Naming Conventions](#11-naming-conventions)\n   2. [Variable Scope](#12-variable-scope)\n   3. [Constants](#13-constants)\n   4. [Strings](#14-strings)\n   5. [Tables](#15-tables)\n2. [Statements](#2-statements)\n   1. [If then else](#21-if-then-else)\n   2. [Control Flow: Loops](#22-control-flow-loops)\n3. [Functions](#3-functions)\n   1. [Naming Conventions](#31-naming-conventions)\n4. [Formatting](#4-formatting)\n   1. [Indentation and Whitespace](#41-indentation-and-whitespace)\n   2. [Line Length and Breaks](#42-line-length-and-breaks)\n   3. [Spacing](#43-spacing)\n5. [Modules](#5-modules)\n   1. [Module Pattern](#51-module-pattern)\n   2. [Requiring and Locals](#52-requiring-and-locals)\n6. [Error Handling](#6-error-handling)\n   1. [pcall and xpcall](#61-pcall-and-xpcall)\n   2. [Assertions](#62-assertions)\n7. [Comments and Documentation](#7-comments-and-documentation)\n   1. [Comments](#71-comments)\n   2. [LDoc/EmmyLua](#72-ldocemmylua)\n8. [Performance](#8-performance)\n9. [Metatables and OOP](#9-metatables-and-oop)\n10. [Coroutines](#10-coroutines)\n11. [Tooling](#11-tooling)\n12. [Testing](#12-testing)\n \n\u003cbr\u003e\n\n## 1. Variables\n\n\n  ## 1.1 Naming Conventions\n  \n  - Use descriptive names that clearly indicate the variable's purpose.\n  - Single-letter variable names should be avoided except for very small scopes (less than ten lines) or for iterators.\n  - `i` should be used only as a counter variable in for loops (either numeric for or `ipairs`).\n  - Prefer more descriptive names than `k` and `v` when iterating with `pairs`, unless you are writing a function that operates on generic tables.\n  - Use `_` for ignored variables.\n  - Variable names with larger scope should be more descriptive than those with smaller scope. \n  - Follow consistent naming patterns to improve readability and maintainability.\n\n  - Use `snake_case` for local variable names.\n  - Use `UPPER_CASE_WITH_UNDERSCORES` for constants.\n\n  ```lua\n  -- global\n  MAX_USERS = 100\n  PI = 3.14159\n  \n  -- local\n  local user_name = \"JohnDoe\"\n  local total_count = 0\n  ```\n\n\n  \u003e Note: There is some confusion in Lua about variable naming conventions. In case of a framework or large projects, follow the already existing conventions according to the scope to avoid mixing new conventions.\n\n  **[[⬆]](#table-of-contents)**\n    \n  ## 1.2 Variable Scope\n  Besides global variables, Lua supports local variables. We create local variables with the local statement:\n  ```lua\n  j = 10         -- global variable\n  local i = 1    -- local variable\n  ```\n    \n  Unlike global variables, local variables have their scope limited to the block where they are declared. \n  A block is the body of a control structure, the body of a function, or a chunk (the file or string with the code where the variable is declared).\n  \n  It is good programming style to use local variables whenever possible. \n  Local variables help you avoid cluttering the global environment with unnecessary names. \n  Moreover, the access to local variables is faster than to global ones.\n  \n  ```lua\n  variable = 1       -- bad\n  local variable = 1 -- good\n  ```\n  \n  Assign variables at the top of their scope where possible. This makes it easier to check for existing variables.\n  \n  ```lua\n  -- bad example\n  local bad = function()\n    test()\n    -- other stuff\n\n    local name = get_current_username()\n\n    if name == 'test' then\n      return false\n    end\n\n    return name\n  end\n  ```\n  ```lua\n  -- good example\n  local function good()\n    local name = get_current_username()\n\n    test()\n    -- other stuff\n\n    if name == 'test' then\n      return false\n    end\n\n    return name\n  end\n  ```\n  **[[⬆]](#table-of-contents)**\n  \n  ## 1.3 Constants\n  \n  Constants are variables that should not change once they are set. \n  Using constants can make your code more readable and prevent accidental modifications. \n  In Lua, we typically follow the convention of naming them in `UPPER_CASE_WITH_UNDERSCORES`.\n  \n  Define constants at the top of the file or the top of their respective scope.\n  Use constants to avoid magic numbers and hard-coded values within your code.\n  Group related constants together for better organization and readability.\n  \n  ```lua\n  -- Define constants at the top of the file\n  MAX_HEALTH = 100\n  INITIAL_SCORE = 0\n  GAME_TITLE = \"Super Lua Game\"\n  \n  -- Function using constants\n  local function initialize_game()\n    local player_health = MAX_HEALTH\n    local player_score = INITIAL_SCORE\n    print(\"Welcome to \" .. GAME_TITLE)\n  end\n  \n  initialize_game()\n  ```\n  **[[⬆]](#table-of-contents)**\n\n  ## 1.4 Strings\n  \n  - Prefer \"double quotes\" for general strings; use 'single quotes' when the string contains double quotes.\n  - Use `string.format` for readability instead of many concatenations.\n  - Avoid concatenation inside tight loops; collect parts and use `table.concat`.\n  - Use long brackets `[[ ... ]]` for multi-line literals.\n  - Escape only when needed (e.g., `\\n`, `\\t`, `\\\\`).\n  \n  ```lua\n  local greeting = \"Hello, \\\"Lua\\\"\"\n  local msg = string.format(\"%s scored %d points\", player_name, score)\n\n  -- bad\n  local msg = \"Hello \" .. name .. \", you have \" .. count .. \" messages.\"\n\n  -- good\n  local msg = string.format(\"Hello %s, you have %d messages.\", name, count)\n\n  -- bad (slow + messy)\n  local result = \"\"\n  for i = 1, 1000 do\n    result = result .. i .. \",\"\n  end\n\n  -- good\n  local parts = {}\n  for i = 1, 1000 do\n    parts[#parts+1] = string.format(\"%d,\", i)\n  end\n  local result = table.concat(parts)\n\n  -- bad\n  local text = \"This is line 1\\nThis is line 2\\nThis is line 3\"\n\n  -- good\n  local text = [[\n  This is line 1\n  This is line 2\n  This is line 3\n  ]]\n\n  ```\n\n\n  ## 1.5 Tables\n  \n  The table type implements associative arrays. An associative array is an array that can be indexed not only with numbers, but also with strings or any other value of the language, except `nil`.\n\n  - Initializing a table with populating its fields all at once, if possible.\n  - Add a trailing comma to all fields, including the last one.\n    \n  \u003e This makes the structure of your tables more evident at a glance, Trailing commas make it quicker to add new fields and produces shorter diffs.\n  ```lua\n  local player_data = {\n    name = \"Shahar\",\n    level = 12,\n  }\n  ```\n  **[[⬆]](#table-of-contents)**\n  \n\u003cbr\u003e\n\n## 2. Statements\nLua provides a small and conventional set of control structures, with if for conditional and while, repeat, and for for iteration. \nAll control structures have an explicit terminator: end terminates the if, for and while structures; and until terminates the repeat structure.\n\nThe condition expression of a control structure may result in any value. Lua treats as true all values different from false and nil.\n\n  ## 2.1 If then else\n  An if statement tests its condition and executes its then-part or its else-part accordingly. The else-part is optional.\n  When you write nested ifs, you can use elseif. \n  It is similar to an else followed by an if, but it avoids the need for multiple ends:\n  \n  ```lua\n  -- good\n  if op == \"+\" then\n    r = a + b\n  elseif op == \"-\" then\n    r = a - b\n  elseif op == \"*\" then\n    r = a * b\n  elseif op == \"/\" then\n    r = a / b\n  else\n    error(\"invalid operation\")\n  end\n  ```\n  **[[⬆]](#table-of-contents)**\n    \n  ## 2.2 Control Flow: Loops\n  In Lua, there are two main types of loops: `while` and `repeat`. \n  Understanding the differences between them is essential for writing efficient and effective code.\n  \n  ### `while` Loop\n  The `while` loop tests its condition before executing the loop's body. If the condition is `false`, the loop ends immediately. \n  If the condition is `true`, Lua executes the body and then repeats the test.\n  \n  ```lua\n  while false do\n    print(\"hi\")\n  end\n  -- This will never print anything\n  ```\n  \n  ### `repeat` Loop\n  \n  The `repeat` loop, also known as `repeat-until`, behaves differently. \n  It always executes its body at least once because the condition is tested after the body. \n  The loop continues until the condition evaluates to `true`.\n  \n  ```lua\n  -- Print the first non-empty line\n  repeat\n    line = io.read()\n  until line ~= \"\"\n  print(line)\n  ```\n  \n  ### Choosing the Right Loop\n  Choosing between `while` and `repeat` can help avoid unnecessary code and ensure that your loops function as intended. \n  Use `while` when you need to test the condition before executing the loop body. \n  Use `repeat` when the loop body must execute at least once before the condition is tested.\n\n  **[[⬆]](#table-of-contents)**\n    \n\u003cbr\u003e\n\n## 3. Functions\n\n\n  ## 3.1 Naming Conventions\n\n  - Use `snake_case` for function and parameters name.\n  - Use descriptive names that clearly indicate the function's purpose.\n  - Don’t use spaces between function name and opening round bracket. Split arguments with one whitespace character.\n  ```lua\n  -- good\n  function calculate_player_score()   \n    -- function logic\n  end\n\n  -- good\n  function update_database_record(record)\n    -- function logic\n  end\n  ```\n\n  ```lua\n  --- bad (function camelCase name)\n  function calcScore() \n    -- function logic\n  end\n\n  -- bad (function undescriptive name)\n  function updRec()        \n    -- function logic\n  end\n\n  -- bad (function name PascalCase)\n  function CalculateScore(p)                  \n    -- function logic\n  end\n\n  -- bad (function name UPPER_SNAKE_CASE)\n  function CALC_SCORE(p)                  \n    -- function logic\n  end\n\n  -- bad (bad parameter name)\n  function CalculatePlayerScore(Player)                  \n    -- function logic\n  end\n  ```\n\n  **[[⬆]](#table-of-contents)**\n\n\u003cbr\u003e\n  \n## 4. Formatting\n\n  ### 4.1 Indentation and Whitespace\n  - Use tabs for indentation not spaces manually.\n  - Trim trailing whitespace; keep files UTF-8 without BOM.\n  - One statement per line.\n\n  ```lua\n  -- bad (spaces)\n  function greet()\n    print(\"hello\")\n  end\n\n  -- good (tabs)\n  function greet()\n\t  print(\"hello\")\n  end\n  ```\n\n  ### 4.2 Line Length and Breaks\n  - Soft limit lines to ~100–120 columns.\n  - Break long expressions at operators, align continued lines.\n\n  ```lua\n  -- bad\n  local msg = string.format(\"Hello %s, you have %d unread messages and %d notifications today.\", name, unread, notifications)\n\n  -- good\n  local msg = string.format(\n    \"Hello %s, you have %d unread messages and %d notifications today.\",\n    name,\n    unread,\n    notifications\n  )\n  ```\n\n  ### 4.3 Spacing\n  - One space around binary operators (`=`, `+`, `..`, etc.).\n  - No space after function names before `(`.\n  - One blank line between top-level definitions.\n\n  ```lua\n  -- bad\n  local sum = 1 + 2\n  print (sum)\n  function c () end\n\n  -- good\n  local sum = 1 + 2\n  print(sum)\n\n  function a() end\n\n  function b() end\n  ```\n\n  **[[⬆]](#table-of-contents)**\n\n\u003cbr\u003e\n\n## 5. Modules\n\n  ### 5.1 Module Pattern\n  - Prefer returning a table of functions/values from files.\n  - Avoid polluting `_G` with globals.\n  ```lua\n  -- good\n  local M = {}\n\n  function M.add(a, b)\n    return a + b\n  end\n\n  return M\n\n  --  bad (puts function in _G)\n  function add(a, b)\n    return a + b\n  end\n\n  ```\n\n  ### 5.2 Requiring and Locals\n  - Assign `require` results to locals for speed and clarity.\n  ```lua\n  local utils = require(\"utils\")\n  local trim = utils.trim\n  ```\n\n  - Prefer explicit imports over pulling from `_G`.\n  ```lua\n  -- bad\n  result = some_global_lib.process(data)\n\n  -- good\n  local lib = require(\"some_global_lib\")\n  local result = lib.process(data)\n  ```\n\n  **[[⬆]](#table-of-contents)**\n\n\u003cbr\u003e\n\n## 6. Error Handling\n\n  ### 6.1 pcall and xpcall\n  - Use `pcall`/`xpcall` for recoverable failures; avoid bare errors in libraries.\n  ```lua\n  local ok, result = pcall(do_work)\n  if not ok then\n    log_error(result)\n  end\n  ```\n\n  ```lua\n  -- with traceback\n  local ok, err = xpcall(do_work, debug.traceback)\n  if not ok then\n    log_error(err)\n  end\n  ```\n\n  ### 6.2 Assertions\n  - Use `assert` for programmer errors and preconditions.\n  ```lua\n  local file = assert(io.open(path, \"r\"))\n  ```\n\n  **[[⬆]](#table-of-contents)**\n\n\u003cbr\u003e\n\n## 7. Comments and Documentation\n\n  ### 7.1 Comments\n  - Use `--` for single-line comments; `--[[ ... ]]` for multi-line.\n  - Explain \"why\" not \"what\"; keep comments up to date.\n  - Comments should be avoided if the code is clear\n\n  ### 7.2 LDoc/EmmyLua\n  - Document public APIs with LDoc/EmmyLua-style annotations for better tooling.\n  ```lua\n  --- Calculates the area of a circle\n  -- @param r number radius\n  -- @return number area\n  local function circle_area(r)\n    return math.pi * r * r\n  end\n  ```\n\n  **[[⬆]](#table-of-contents)**\n\n\u003cbr\u003e\n\n## 8. Performance\n  - Prefer locals over globals (faster lookups).\n  - Hoist invariants out of loops; cache `#t` when needed.\n  - Use `table.concat` for building strings.\n  - Pre-size arrays when possible: `local t = table.create and table.create(n) or {}`.\n\n  ```lua\n  -- bad (global lookup each time)\n  for i = 1, n do\n    total = total + math.abs(values[i])\n  end\n\n  -- good (cache locals)\n  local abs = math.abs\n  for i = 1, n do\n    total = total + abs(values[i])\n  end\n  ```\n\n  **[[⬆]](#table-of-contents)**\n\n\u003cbr\u003e\n\n## 9. Metatables and OOP\n  - Keep metatable usage minimal and explicit.\n  - Use `__index` for method lookup; avoid magic unless well-documented.\n  ```lua\n  local Point = {}\n  Point.__index = Point\n  function Point.new(x, y)\n    return setmetatable({ x = x, y = y }, Point)\n  end\n  function Point:len()\n    return math.sqrt(self.x * self.x + self.y * self.y)\n  end\n  ```\n\n  ```lua\n  -- bad (implicit globals, hidden metatable behavior)\n  P = {}\n  setmetatable(P, { __index = function() return 0 end })\n  ```\n\n  **[[⬆]](#table-of-contents)**\n\n\u003cbr\u003e\n\n## 10. Coroutines\n  - Use coroutines to model producers/consumers and cooperative tasks.\n  - Keep coroutine lifecycles clear; avoid deep nesting.\n  ```lua\n  local function generator(n)\n    return coroutine.wrap(function()\n      for i = 1, n do coroutine.yield(i) end\n    end)\n  end\n  ```\n\n  ```lua\n  -- consumer\n  local next_num = generator(3)\n  print(next_num()) -- 1\n  print(next_num()) -- 2\n  print(next_num()) -- 3\n  ```\n\n  **[[⬆]](#table-of-contents)**\n\n\u003cbr\u003e\n\n## 11. Tooling\n  - Formatter: stylua; Linter: luacheck; Docs: LDoc.\n  - Configure CI to run formatting and linting.\n  ```bash\n  stylua .\n  luacheck .\n  ```\n\n  ```lua\n  -- .luacheckrc (example)\n  -- globals = { \"vim\" }\n  -- ignore  = { \"631\" } -- line too long (if policy allows)\n  ```\n\n  **[[⬆]](#table-of-contents)**\n\n\u003cbr\u003e\n\n## 12. Testing\n  - Use busted or luaunit; structure tests mirroring module layout.\n  - Keep tests deterministic; avoid global state.\n  ```lua\n  describe(\"math utils\", function()\n    it(\"adds\", function()\n      assert.are.equal(3, add(1, 2))\n    end)\n  end)\n  ```\n\n  **[[⬆]](#table-of-contents)**\n\n\u003cbr\u003e\n\nMentions:\n- https://www.lua.org/\n- https://github.com/Olivine-Labs/lua-style-guide/\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshaharband%2Flua-style-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshaharband%2Flua-style-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshaharband%2Flua-style-guide/lists"}