Lua Style in Roblox — Format Before Review
Decompiled or copy-pasted Lua often arrives as a single horizontal smear: every statement on one line, end keywords stacked without structure, and a PR description that says “small fix.” Reviewers bounce it not because they hate Lua, but because they cannot see control flow. Formatting is not fashion — it is how you make if / for / function boundaries visible before someone ships a logic error wrapped in noise.
What “style” means for Lua specifically
Lua (and Luau in Roblox) is whitespace-insensitive for semantics. That freedom is exactly why teams need a written convention:
- Indent width (2 or 4 spaces, or tabs — one choice)
- Where
then/dosit relative to the condition - How long lines wrap (operators at end vs start of line)
- Table layout: one entry per line past a size threshold
- Blank lines between top-level functions
Without agreement, every contributor reformats the lines they touch and diffs become unreadable.
Bugs that formatting surfaces
Mismatched end — Dense code hides an extra or missing end. Once indented, the cliff of nesting makes the mistake obvious.
Accidental global assignments — foo = 1 vs local foo = 1 is easier to spot when declarations line up at the left of a scope.
Table comma mistakes — Trailing commas and missing commas in large tables stand out when each field owns a line.
Copy-paste block scope — An if that was supposed to wrap three statements only wraps one; indentation shows the real structure immediately.
None of these require a type system. They require a human-readable tree.
A lightweight team guide that sticks
Keep the doc short enough that people read it:
- Always
localunless the symbol must be global (and comment why). - Prefer early returns over deep nesting.
- Name Instances and remotes clearly; formatters will not fix
thing1. - Run the formatter before opening the PR, not after the first review comment.
- Do not hand-align columns with extra spaces; let the tool own layout.
Roblox-specific note: ModuleScripts and server/client boundaries matter more than brace style. Formatting should not obscure RemoteEvent names or WaitForChild chains — keep those strings untouched.
How to use a formatter without fear
- Format a single ModuleScript first and playtest that system.
- Diff only whitespace + your intentional edits in source control.
- If Studio or a linter complains, fix the rule config — do not disable formatting forever.
- Roll out folder-by-folder for large places so bisecting regressions stays possible.
Paste gnarly snippets into lua-formatter when you are cleaning a gist, a DevForum paste, or a decompiled fragment before you drop it into Studio. Use it as a readability pass, then paste back and let Rojo/Studio own the file on disk.
What not to argue about in review
- Tabs vs spaces after the decision is recorded
- Whether
and/orchains wrap at column 80 vs 100 - Alphabetical order of
requires unless the team already automated it
Spend review time on replication, security (who can fire the remote), and performance of hot paths in Heartbeat. Spend formatter time making those discussions possible.
Before you merge messy Lua
Ask: can a teammate see every end that closes every block in under ten seconds? If not, format first. Readable Lua is not “enterprise theater” — it is how Roblox teams avoid shipping silent control-flow bugs that only appear under a specific player state. Format for humans, playtest in Studio, and keep the indent rule boring and consistent.