---
# System prepended metadata

title: Aqua's Luau Style Guide Dark Mode

---

{%hackmd theme-dark %}

![](https://hackmd.io/_uploads/rJyO6Gbih.png)


---
## Author's Notes

This guide is not claiming to be the one answer to how to code. It's highly opinionated and you can disagree with the guide's practices. So take from it what you will. Use this as a guideline or completely as is, or don't, it's completely up to you!


---


## Resources

The resources listed below are what this guide adheres to unless specified otherwise.
It's best you read through them, you'll most likely get something out of it.
[Best Practices by: evaera](https://gist.github.com/evaera/fee751d4e228dd262fe1174ba142a719)
[Performance Tips by: Roberto Ierusalimschy](https://www.lua.org/gems/sample.pdf)
[Lua Style Guide by: Roblox](https://roblox.github.io/lua-style-guide/)
[**Light Mode Version**](https://hackmd.io/@aquaisaquafina/aquas_luau_style_guide)



---




## Aims

You should always aim for your code to be more readable than writeable.
This guide is meant to do just that while staying consistent.

---

## Comments

Comments are something that should be used sparringly. This doesn't mean you should hesitate when it comes to using them. However, you don't want to explain what every little thing does to the point that it's unnecessary. In addition, instead of explaining what each section does say why it's there.

**Note: When describing why a function is there, put the comment inside the function as the first line rather than on the outside!** This ensures that when you hide the codeblock in your IDE it also hides the comment. 

#### EXAMPLE

##### Good
```lua=
do
    local realAccessKey1: string = HttpService:GenerateGUID()
    local realAccessKey2: string = HttpService:GenerateGUID()

    local function DoNuke(accessKey1: string, accessKey2: string)
        -- Needed to stop the humans from overpopulating.
        if realAccessKey1 ~= accessKey1 then return end
        if realAccessKey2 ~= accessKey2 then return end
        -- Needed or else nuke can be activated without the keys.
        -- Some big bad person could get their hands on it :(.
        KillAllHumans()
    end
end
```

##### Bad

```lua=
do
    local realAccessKey1: string = HttpService:GenerateGUID() -- Generates Key
    local realAccessKey2: string = HttpService:GenerateGUID() -- Generates Key

    local function DoNuke(accessKey1: string, accessKey2: string)
        -- Does nuke.
        if realAccessKey1 ~= accessKey1 then return end
        if realAccessKey2 ~= accessKey2 then return end
        -- Checks if the real access keys are equal to the ones inputted.
        KillAllHumans()
    end
end
```

---

## Variables

For variables you may want to use abbreviations as they are easier to type, however the trade-off is that it makes it just as much harder to read.

### Casing

Variables by default should be in PascalCase unless specified below.

#### EXCEPTIONS

* Setting constants should be fully capitalized.
* Variables inside functions should be in snakeCase unless its a service or a client controller. (Services should always be defined at the top of a script, this is an edgecase that involves the use of frameworks, knit being an example.)
* Variables that are folder instances should be prefixed with "_".
* Private tables should be prefixed with "T_"
* Dummy variables should be substituted with "_"


---

## Blocks

Each block of code should be spaced out by a line based of relevance. This keeps the code tidy and allows for others to have a much easier time understanding what your code does.

### Do Blocks

You can use a do block if limitating the scope proves useful. 

#### EXAMPLE
###### THIS EXAMPLE IS TAKEN DIRECTLY FROM ROBLOX'S STYLE GUIDE, IT DOES NOT ADHERE TO THIS STYLE GUIDE.
```lua=
local getId
do
    local lastId = 0
    getId = function()
        lastId = lastId + 1
        return lastId
    end
end
```

### Use of Parenthesis

Use parenthesis in if statements if it improves readability. This is based on the programmers judgement.

#### EXAMPLE

##### Good


You can easily see the seperation.
```lua=
if (x > y and y > z) or (y > x and z > y) then
    -- Do Stuff
end
```

##### Bad

Harder to see the seperation.
```lua=
if x > y and y > z or y > x and z > y then
    -- Do Stuff
end
```

### In-line Statements

You should use in-line if statements if they are used for breaking out of a function.

#### Example

##### Good

```lua=
local badValue = true
if badValue then return end
-- Escapes if the scary bad value is there :(
```
Condensed into a single line, saving space without harming readability.

##### Bad

```lua=
local badValue = true
if badValue then
    return
end
-- Escapes if the scary bad value is there :(
```
3 Lines used for something that can easily be condensed without harming readability.

### Conditional Checks

You should avoid things like "if value ~= nil" and instead do "if value". This saves spaces and makes the code more readable. You can also use the not operator.

#### Example

##### Good

```lua=
local badValue = nil
if badValue then return end
-- Escapes if the scary bad value is there :(
```

```lua=
local badValue = nil
if not badValue then return end
-- Escapes if the scary bad value is not there :)
```

##### Bad

```lua=
local badValue = nil
if badValue ~= nil then return end
-- Escapes if the scary bad value is there :(
```
```lua=
local badValue = nil
if badValue == nil then return end
-- Escapes if the scary bad value is not there :)
```

### Nesting

You should try and reduce nesting code as much as possible. Doing this is as simple as returning out with in-line if statements.

#### Example

##### Good
```lua=
local badValue = nil
if badValue then return end
-- Does stuff here.
```

##### Bad

```lua=
local badValue = nil
if not badValue then
   -- Does stuff here.
end
```

---

## Whitespace

You should try and keep whitespacing to a minimum whilst not being too shallow. Too much whitespacing makes the code harder to read. However too little makes it condensed and thus, also hard to read.

**DO NOT EVER** use extra horizontal whitespace.
**ALWAYS** end a script with an extra line. (You can see this automatically being done inside newly created module scripts.)

### General

#### EXAMPLE

###### Good

```lua=
--[[ Services ]]--

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local GuiService = game:GetService("GuiService")

--[[ Private Variables ]]--

local Platform: "Unknown" | "PC" | "Mobile" | "Console" = "Unknown"

local _Packages = ReplicatedStorage.Packages
local _AvailableInputs = script.AvailableInputs
local _Skills = ReplicatedStorage.Skills
```

###### Bad

```lua=
--[[ Services ]]--



local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local GuiService = game:GetService("GuiService")



--[[ Private Variables ]]--




local Platform: "Unknown" | "PC" | "Mobile" | "Console" = "Unknown"




local _Packages = ReplicatedStorage.Packages
local _AvailableInputs = script.AvailableInputs
local _Skills = ReplicatedStorage.Skills
```

### Tab Width
This guide uses a tab width of 3 spaces, it just looks and reads better. See for yourself! This is mainly preference and isn't a requirement for the guide. **Note: The practices stated in the guide are not shown  in the code below, the code was created before this guide came into creation!**

![](https://hackmd.io/_uploads/rJ4hQzbj2.png)



---


## Frameworks

There are a lot of different frameworks however Knit is the one primarily used with this style guide. It's not a necessity to use a framework but is recommended as it helps with management along with plenty other benefits.
[Official Knit Documentation](https://sleitnick.github.io/Knit/)



---


## Type Checking

We use type checking everywhere in this style guide. Make sure to familarize yourself with type checking via the basics linked below before reading on.
[Luau Type Checking Basics](https://devforum.roblox.com/t/type-checking-for-beginners/1027242)

When you create a function always use type assignment to function variables.
Additionally, when a function returns, assign the type that it returns.

#### EXAMPLE

##### Good

```lua=
local function Add(number1: number, number2: number): number
	if typeof(number1) ~= "number" or typeof(number2) ~= "number" then return end
	-- We need this so the function doesn't error if something other than a number is inputed into the arguments.
	return number1 + number2
end

Add(1, 2)--> 3
-- We know it takes in two arguments that are both numbers and returns a number.
```

##### Bad

```lua=
local function Add(number1, number2)
	if typeof(number1) ~= "number" or typeof(number2) ~= "number" then return end
	-- We need this so the function doesn't error if something other than a number is inputed into the arguments.
	return number1 + number2
end

Add(?, ?)--> ?
-- We don't know what it takes in or returns.
```

Of course we would know what that Add function takes in and what it does, but for more complex functions we might not. Thus it's useful to use type checking so we know exactly what it takes in and returns without having to backtrack and look at the function.


---

## OOP/Classes

Classes should never use inheritance and instead favor composition. This makes it similar to an ECS system. This means better code reusability, a smaller hierarchy, and only carrying over the functionality we need.
[Luau OOP Composition Guide](https://www.youtube.com/watch?v=KNT-vjqB_qI)


---

## Format

This guide uses a template to ensure easier locating of individual parts in a script. Most people will disagree with this and say code is meant to look boring. However, I believe there is nothing wrong with prettying up your code. Well, as long as it doesn't compromise functionality and readability.

#### Template
```lua=
--[[ Services ]]--



--[[ Private Variables ]]--



--[[ Tables  ]]--



--[[ Modules ]]--



--[[ Private Functions ]]--



--[[ Handling ]]--
```
This allows us to search for sections and know where everything is placed.
Below is an example of this template filled out using the Knit framework.

```lua=
--[[ Services ]]--

local ReplicatedStorage = game:GetService("ReplicatedStorage")

--[[ Private Variables ]]--

local _Packages = ReplicatedStorage.Packages
local _Debris = workspace.Debris

--[[ Tables  ]]--



--[[ Modules ]]--

local Knit = require(_Packages.Knit)
local Debris = require(_Packages.Debris)

--[[ Private Functions ]]--



--[[ Handling ]]--

local DebrisController = Knit.CreateController { Name = "DebrisController" }

function DebrisController:KnitInit() 
	_Debris.ChildAdded:Connect(function(object: Instance)
		local destroyWait = object:GetAttribute("DestroyWait") or 6
		Debris:AddItem(object, destroyWait)
	end)
end

function DebrisController:KnitStart() 

end

return DebrisController
```


---

## Organization
Although this isn't related to a code style, organization of a project can affect how the code is used and thus is included here.

You should try to keep a maximum of 1 script and 1 local script. However, if your game uses a loading screen/system then it's perfectly acceptable to have another local script for handling that alone. I call this the 1:1 rule, however this is commonly referred to as single script architecture.

#### Folders
* Packages --> ReplicatedStorage
* Modules --> ReplicatedStorage, StarterPlayerScripts, ServerScriptService

#### Script Type Locations | Parenting/Ancestors
* Scripts --> ServerScriptService
* LocalScripts --> StarterPlayerScripts, ReplicatedFirst
* ModuleScripts --> ReplicatedStorage, StarterPlayerScripts, ServerScriptService

For further clarification, I would think of it like this, you want to hide as much code as possible from the client. This is called abstraction. Based of this, if you need to access a function inside a module from both the client and server, then you would put it under replicated storage. If you only need it on the server, then putting it where the scripts are would be best. The inverse also applies for local scripts.

This way of doing things provides 2 main benefits.

1. It allows you to know what each modules access level is; server, client, or both.
2. It follows the abstraction principle, and keeps unnecessary code from the client's view.

**NOTE:** The purpose of the **packages** folder is to store modules that can be accessed and used in other projects without outside dependants. You should **refrain** from storing something game specific inside the folder. 

**NOTE:** It's okay to have nested dependencies under the actual package as long as it's not from the outside.

#### Examples

##### Good

![](https://hackmd.io/_uploads/BygtF4Mj3.png)



The "Hitbox" module depends on the "SETTINGS" module. The "Knit" module depends on both the "KnitClient" and "KnitServer" modules. This is acceptable because those dependencies are directly nested under the module script that is the main package.

##### Bad

![](https://hackmd.io/_uploads/HyW5FVzj3.png)


Lets say the "Knit" module depends on the "Hitbox" module to run and vice versa. This is bad because they won't work on their own. Meaning they are unable to be carried over to other games by themselves alone.

---

## End

Well that's it for the style guide. You can leave a heart if you want. Have any suggestions/questions/concerns? Feel free to dm me on discord @aquamizu. I'm always welcome to hear anyone's opinion!
