Roblox Lua Style Guide: Keep code clean and consistent

This is a guide about Lua code style. It’s important to keep all code consistent across scripts, especially when working in teams. Thus, I’ve compiled a list of style conventions that should be used to help keep all code clean and orderly. This may be edited and updated as time passes.

I’ve based this off of two other Lua style guides.

1.) Roblox’s style guide
2.) A lua style guide from Ovine Labs

Also, I’ve left out certain sections as I did not consider them very necessary. Sections may be added as time passes.

File Structure

Files should consist of these things (if present) in order:

1.) Services used by the file, using Get Service
2.) Module imports
3.) Constants
4.) Variables and Functions

Variables and Naming Conventions

  • Always try to assign variables at the top of their scope whenever possible. This makes it easier to check for existing variables.

  • Always try to use local to declare variables.

Good:

local newBoolVariable = true

Bad:

newBoolVariable = false
  • A File’s name should match the name of the object it exports.

    • If your module exports a single function named doSomething , the file should be named doSomething.lua .
  • When assigning variables and other names, make sure to spell out words fully. Abbreviations take up less space, but are not as easy to read.

  • Use camelCase for assigning names to local variables.(Member variable - Wikipedia)

  • Use Upper_Snake_Case for local constant values that do not change.

  • Use PascalCase for everything else

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Running_Velocity = 50
local Walking_Velocity = 25

local isWalking = false
local isRunning = false

local PlayerCharacter = {}

local function PlayerCharacter.IsMoving()
    return isRunning or isWalking
end

Tables

  • Avoid tables with both list-like and dictionary-like keys. Iterating over these mixed tables is troublesome.

  • Iterate over list-like tables with ipairs and dictionary-like tables with pairs. This helps clarify what kind of table we’re expecting in a given block of code.

  • Break dictionary-like tables with more than a couple keys onto multiple lines. This makes it easier to read and understand what is inside the table.

Good:

    local Inventory = {
        backpack = {}, 
        belt = {},
        primaryWeapon = "Hatchet",
}

Bad:

local Inventory = {backpack = {}, belt = {}, primaryWeapon = "Hatchet"}
  • Use the constructor syntax for table creation whenever possible. This just makes it easier to understand what is membered inside the table.

Good:

local Beans = {
    quality = 90
    hungerRefill = 80
    thirstRefill = 10
}

Bad:

local Beans = {}
Beans.quality = 90
Beans.hungerRefill = 80
Beans.thirstRefill = 10
  • Define functions externally to table definition

Good:

local Beans = {
    quality = 90
    hungerRefill = 80
    thirstRefill = 10
}

function Beans:eat()
    -- EAT DEM BEANS!!!
end

Bad:

local Beans = {
    quality = 90
    hungerRefill = 80
    thirstRefill = 10

    eat = function()
        -- eat dem beans ;(
    end
}

Functions

  • Declare functions using local whenever possible.

    • Sometimes, exceptions are made. An exception can be made for late-initializing functions in conditionals. However, notice how the function is still tied to a local value.
local doSomething

if _Condition then
    function doSomething()
        -- Version of doSomething with _Condition enabled
    end
else
    function doSomething()
        -- Version of doSomething with _Condition disabled
    end
end
  • Try to keep functions as small, and as focused on one goal as humanly possible. This is one of the most important things for well organized, clean, and readable code. Most important!

  • Keep arguments in a function limited to as few as possible.

  • When declaring a function that is membered to a table, use function-prefix syntax. Differentiate between . and : to denote intended calling convention.

    • If the function calls an event or action, use :

    • If the function returns a value, use .

    Pet = {}

    function Pet:walkTo(position)
        -- walks the pet to position
    end

    function Pet.getName()
        -- returns the pet's name
    end
  • When declaring any function, prefer function syntax over variable syntax.

Good:

local function newFunction()
        -- Stuff here
end

Bad:

local newFunction = function()
    -- Stuff here
end
  • Perform validation to functions as early as possible. This is to make sure no extra resources are being used that aren’t necessary.
local canRunNewFunction = false

local function newFunction()
    if not canRunNewFunction then return end

    print ("Running newFunction")
    -- Since canRunNewFunction is false, 
    -- the rest of the function will not run.
end

Code Blocks

  • Single line blocks are okay for small statements. Try to keep lines to 80 characters. Indent lines if they overflow past the limit.
if true then return true end -- This is okay because it's small and simple to read
  • Use do blocks if limiting the scope of a variable is necessary.
local getId
do
    local lastId = 0
    function getId()
        lastId = lastId + 1
        return lastId
    end
end

Strings

  • For most cases, strings should be defined by double quotes

    • There are some exceptions. For example, sometimes a string needs to actually have quotation marks in the string itself. In this scenario, brackets can be used.
print ([["By God! That's an exception!", he exclaimed.]])
  • Strings longer than 80 characters should be written across multiple lines using concatenation. This allows you to indent nicely.

Good:

superLongString = "This is a super long string that "..
    "I am writing for the purpose of "..
    "this resource. As you can see, "..
    "it is not on one line and is much"..
    "easier to read! This is the best "..
    "way to do if if you want to read "..
    "it easily."

Bad:

superLongString = "This is  a super long string that I am writing for the purpose of this resource. As you can see, it is on one line and it is hard to read! This is not the best way to do it if you want to read it easily.

Whitespace

Whitespace is one of the most important things to keep in check for clean and readable code.

  • Always use proper indentation with tabs.

    • Seriously, though, indent with tabs
  • Avoid leaving whitespace at the end of lines.

  • Use one statement per line. Prefer to put function bodies on new lines.

  • No vertical alignment. It just looks ugly.

Good:

variableLong1 = true
variable2 = false

Bad:

variableLong1 = true
variable2     = false
  • Use a single empty line to express groups when useful. Do not start blocks with a blank line. Excess empty lines harm whole-file readability.

  • Put a space before and after operators

  • Put a space after commas

Good:

function add(a, b)
    return a + b
end

Bad:

function add(a,b)
    return a+b
end
  • When creating blocks, inline any opening syntax elements.

Good:

local foo = {
    bar = 2,
}

if foo then
    -- do something
end

Bad:

local foo =
{
    bar = 2,
}

if foo
then
    -- do something
end
28 Likes