New Lua Style Guide for the Community

137 views
Skip to first unread message

Shahar Band

unread,
Sep 9, 2025, 2:33:09 PMSep 9
to lua-l

Hello everyone,

I’ve put together a Lua style guide to help improve consistency and readability when writing Lua code. It’s inspired by common patterns I’ve seen in the community, with some additional conventions that I’ve found useful in practice.

The guide covers topics such as:

  • Naming conventions

  • Code formatting (indentation, whitespace, line breaks)

  • Table and function usage

  • Performance tips

  • Common pitfalls and best practices

I’ve published it here:
https://github.com/ShaharBand/lua-style-guide

My hope is that it can serve as a shared reference for teams and individuals working with Lua, and that it evolves with community feedback.

I’d love your thoughts—suggestions, criticisms, or additions are all welcome! If you find it useful, please feel free to contribute via pull requests or issues.

Thanks,
Shahar Band

Marc Balmer

unread,
Sep 9, 2025, 2:36:35 PMSep 9
to lu...@googlegroups.com
what for?

--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/lua-l/c74955e0-6e08-4ba3-857b-13e29267e40cn%40googlegroups.com.

Sainan

unread,
Sep 9, 2025, 2:38:03 PMSep 9
to lu...@googlegroups.com
'Style guide' feels really antiquated. I get it's neat to have some idea of how to format code, but really, in the age of AI agents, you need a linter/formatter to actually enforce such things, otherwise the code just exists as it was created.

-- Sainan

Shahar Band

unread,
Sep 9, 2025, 2:41:55 PMSep 9
to lua-l

  I created the style guide mainly to improve consistency in Lua code. Unlike languages like Python or Go, Lua doesn’t have a widely adopted standard formatter or style guide, so codebases often look very different. This guide helps teams and individuals write cleaner, more readable, and maintainable Lua code.  
ב-יום שלישי, 9 בספטמבר 2025 בשעה 21:36:35 UTC+3, Marc Balmer כתב/ה:

Shahar Band

unread,
Sep 9, 2025, 2:42:25 PMSep 9
to lua-l

i might make one if the future depending on the feedback
ב-יום שלישי, 9 בספטמבר 2025 בשעה 21:38:03 UTC+3, Sainan כתב/ה:

Sainan

unread,
Sep 9, 2025, 2:52:08 PMSep 9
to lu...@googlegroups.com
Well, Lua might not have something as ubiquitous as 'prettier' in JS, but it does have various formatters (e.g. https://github.com/JohnnyMorganz/StyLua).

In general, I do find Lua code is written as you describe it. That's the beautiful thing about ideas, they are persistent little buggers! So, your project is that you wrote down those ideas. I just don't see anyone reading it because, well... they kinda don't need to? (Unless an archivist is looking at this 100 years in the future.)

-- Sainan

Sewbacca

unread,
Sep 9, 2025, 4:23:06 PMSep 9
to lu...@googlegroups.com
On 9/9/2025 8:16 PM, Shahar Band wrote:

I’ve published it here:
https://github.com/ShaharBand/lua-style-guide

I noticed an inconsistencies according to 4.3:
  • For 2.1, the multiplication and division don't follow your spacing guideline.
  • Also in 1.4 using your own guideline parts[#parts+1] should really be written parts[#parts + 1].

Also I'd like to give my 4 cents about formatting here:

  • I like to omit spaces around .., since that operator is already quite fat and it makes long concatenation easier to read.
    I prefer string.format for complicated formatting though.
  • I do like the style for you used for appending to a table, as seen in 1.4: parts[#parts+1]
    This expression is so idiomatic and established, that it just makes sense to shorten it for reading ease.
  • In some complex math expressions I like to avoid spaces around * and / (and sometimes even for + and - when using single letter variables),
    so it gets easier to read.
  • There are quite a few more coding guidelines I have in mind, but the last one I'd like to mention (I saw it somewhere on StackOverflow first)
    is for defining a Lua class, when defining multiple in one file:
---@class MyClass
local MyClass = {} do
	MyClass.__index = MyClass

	-- optional init function
	-- returning self here so that `setmetatable({}, MyClass):init(...)` is returning the object
	function MyClass:init(...)
		-- init stuff
		return self
	end
end

local factory = {}

-- When multiple constructors make sense, I'll use a factory
function factory.newwhatever(...)
	---@class MyClass
	local o = setmetatable({}, MyClass)
	-- optional, might just setup the fields right here, which LuaLS will remember, when using `---@class` as above
	o:init(...)
	return o
end

return factory

Javier Guerra Giraldez

unread,
Sep 9, 2025, 5:12:34 PMSep 9
to lu...@googlegroups.com
On Tue, 9 Sept 2025 at 13:38, 'Sainan' via lua-l <lu...@googlegroups.com> wrote:
'Style guide' feels really antiquated. I get it's neat to have some idea of how to format code, but really, in the age of AI agents, you need a linter/formatter to actually enforce such things, otherwise the code just exists as it was created.

Code style is about readability.   no matter who or what wrote the code, if there's no consistency, it's harder to read.  While it's true that a good **standard** formatter is the best way to keep code consistent, they don't work **at all** if there's no agreed style guide behind them.

case in point:  a big part of why i no longer like to program in Python, is because the last decade the worst ideas became almost "official", so now it's nearly impossible to consistently configure formatters and linters to an acceptable style.   Go and Zig are far more strict in that aspect, the formats aren't configurable at all, but the styles are (mostly) sane, so they do a good job to improve readability.  In both cases, there is a style guide defined before, and the formatters (which existed basically on day one of each language) are based on them.

[[ and of course, no formatter can make slop code readable, so the existence of "AI agents" in no way makes them antiquated ]]

--
Javier

Sainan

unread,
Sep 9, 2025, 5:22:47 PMSep 9
to lu...@googlegroups.com
> and of course, no formatter can make slop code readable, so the existence of "AI agents" in no way makes them antiquated

This reads like you never used AI agents... So, quick intro is that they struggle with code style due to how they see the world; e.g. something like 'use tabs instead of spaces' may seem simple to you, but can be quite difficult for an AI agent. Obviously, this is not a big enough issue to prevent their adoption, especially because formatters eliminate style-related problems outright.

-- Sainan

Berwyn Hoyt

unread,
Sep 9, 2025, 8:20:27 PMSep 9
to lu...@googlegroups.com
Nice job, Shahar! It's good to have something written down. For example this guide can be used as a base guide for a programming team, even if the team policy is something like "Use the Band Guide in general except for ..."

It would also be very interesting to me if you could post this with the ability to vote on the options. I think github discussion can do voting. For example, I would vote for using single-quotes by default (less noisy), but would also be interested to know what the majority thought. I'd vote for generally declaring variables where they're first used rather than at the top of their scope.

Cheers,
Berwyn

--

sur-behoffski

unread,
Sep 9, 2025, 8:20:53 PMSep 9
to lu...@googlegroups.com
(Various commentators):
>> [...]

I note that "Code is read many times more than it is written" is a maxim
Knuth worked to in creating Literate Programming. I only know of one
startup where the originating author had extensively used a variant of
Knuth's tangle/weave, and that project had to revert to a more linear C
style, since the pool of available C programmers found the literate code
style/layout too hard to manage.

So a readable, consistent style is valuable.

--

Referring back to Gerard Holzmann's JPL video "Mars Code", he noted that
many coding styles sheets spent significant time in indentation, braces,
tabs versus spaces, and the like. His comment in the video was that
most of these did not materially contribute to bug-free code, and his
"Power of 10" rules contained just ten demands, as it had been shown
from field analysis that, for each rule, an example could be produced
where code that violated that rule had ended up generating defective
operation of some code in the field.

Before discussing the ten rules [including no recursion!], he commented
on, and was very dismissive of, rules based on style, and noted that,
for C, if someone came up with code written in a different style, then
the utility "indent" could quickly and painlessly enforce the in-house
layout.

--

So, I would be interested in a Lua-specific version of indent, so that
any valid Lua program could be reshaped according to the template
specified by such "LuaIndent" program -- and, easily, the rules could be
specified by a Lua script.

I'd prefer to see such a tool, before getting into the nuts and bolts of
any canonical style. (I suspect that the Programming in Lua books would
be influential in shaping a style, because of their many example code
snippets.)

--

cheers, s-b etc

Sean Conner

unread,
Sep 9, 2025, 9:29:17 PMSep 9
to lu...@googlegroups.com
It was thus said that the Great Shahar Band once stated:
>
> I’ve put together a *Lua style guide* to help improve consistency and
> readability when writing Lua code. It’s inspired by common patterns I’ve
> seen in the community, with some additional conventions that I’ve found
> useful in practice.
>
> I’d love your thoughts—suggestions, criticisms, or additions are all
> welcome! If you find it useful, please feel free to contribute via pull
> requests or issues.

Since you asked ... (disclaimer: I'm not fond of style guides myself
because they tend not to hew towards my opinions).

First comment: you link to another Lua style guide at the end. Why not
just use that guide? Why bother with a new guide at all? Continuing
onwards, from section 1.1:

> * Use snake_case for local variable names.

Why not lowerUpper or UpperUpper for local variables? Or even
runonsentance? It might be a bit of an ask to expect rationals for these
decisions, but it would be nice. If you were to ask me for rationals for
how I format my code, I can a rational. You may not agree with it, but I do
have reasons.

> * Use UPPER_CASE_WITH_UNDERSCORES for constants.

This comment is in conjuction with section 1.3 (constants) and 3.1
(function names). Technically, most functions, once defined, don't change
and are therefore a constant. I know you are really talking about magic
numbers, but other values can be constants too---in my CBOR implementation
[1] if you need to use CBOR "null" and "undefined" they can be implemented
as:

null = {}
undefined = {}

in order to get unique values that can be checked. Technically, per your
guide, these should be NULL and UNDEFINED but I was matching CBOR terms, so
lowercase they are.

And because values other than numbers can technically be a constant,
shouldn't the following also be true if I use your style?

local LPEG = require "lpeg"
local function FOO(a,b,c) ... end

Loaded modules are, for the most part, treated as constants in most Lua
code I've seen, and it's rare (although not uncommong) to never redefine
most functions to technically, they are constants as well.

And does this apply to constants within a module? The LPeg module
contatins a .version member, a string, which is the version. Shouldn't it
technically be .VERSION?

Sorry, just taking this advice to it's absurd end here.

You talk a bit about strings in section 1.4. I treat '' and "" as
semantic elements. Strings that the user can see get "" and strings used
more for enumeration or non-visible reasons get ''. Some examples:

-- here, 'error' is an enumeration of the level, while
the other string is user visible

syslog('error',"could not read %q: %s",filename,errno[err])

-- here, the filename is in quotes (even though it's not user
-- visible, it *is* a name) and the mode is in '' because it's
-- more of an enumeration of modes than a string

file = io.open("datafile.txt",'r')

If the string will contains either double quotes or single quotes, I'll
use the bracket form to avoid excessive esacping (mostly when generating
HTML output).

> * Escape only when needed (e.g., \n, \t, \\).

Why even mention this?

In section 1.5, nil and NaN can't be used for indexing (it's not just
nil). It takes some work to get a NaN (0/0 is one way). Also, I'm very
surprised you didn't mandate brace placement in tables (points off for using
K&R style braces---boo! Not a fan!)

Why does section 2 even exist? Is this a style guide, or the fundamentals
of Lua coding? Also, for looping, you forgot recursion:

function ItemsInTable(t,c)
c = c or 0
if not t[c] then
return c
else
return ItemsInTable(t,c+1)
end
end

I'm not saying that's a performance example of calculating the length of a
table, but that it *is* an example of a loop in a recursive style.

No further comments about section 3 that haven't been said already.

In section 4.1, you might get some strong pushback to using tabs in source
code, if just from the Python people (where white space is used
semantically). They're hard to distinguish from a space charater, and they
seriously mess up diffs when viewing patches.

In section 6.2 your example for using assert() is not a very good one.
You state:

> Use assert for programmer errors and preconditions.

but the example is neither an error or a precondition. As I don't really
use assert() in Lua, the only good example of a precondition I can come up
with is an example from C (from my Lua CBOR implementation):

static void cbor_cL_pushvalueN(
lua_State *L,
int typeinfo,
unsigned long long int value,
size_t len
)
{
buffer__u result;
size_t idx = sizeof(buffer__u);

assert(L != NULL);
assert(
((len == 1) && ((typeinfo & 0x1F) == 24))
|| ((len == 2) && ((typeinfo & 0x1F) == 25))
|| ((len == 4) && ((typeinfo & 0x1F) == 26))
|| ((len == 8) && ((typeinfo & 0x1F) == 27))
);

for (uint8_t b = (uint8_t)value ; len > 0 ; b = (uint8_t)(value >>= 8) , len--)
result.b[--idx] = b;

result.b[--idx] = typeinfo;
lua_pushlstring(L,&result.c[idx],sizeof(buffer__u) - idx);
}

and an example of checking for a programming error comes from some other C
code I have:

assert((size_t)(buff - obuff) < size);

This to ensure that the loop that preceeds this statement didn't overrun
the buffer. Your example is checking the result of io.open(). To me,
that's an incorrect usage for assert(). But as I said, I don't use it in
Lua (preferring for the system to detect errors) but in C (where the system
generally can't).

In section 9, you state:

> Keep metatable usage minimal and explicit.

What do you mean by this? The use of metatables is anything but explicit,
as it's used to overload common operations. And I don't get the point of
your example. First, because a point doesn't *have* a length, so defining
such a function for a Point object is meaningless. Second, you seem to want
people to avoid metatables entirely given you bad example of a bad example.

The rest of the guide seems to be fairly generic advice. Not much to say
about it.

-spc

[1] https://github.com/spc476/CBOR

Sean Conner

unread,
Sep 9, 2025, 9:31:56 PMSep 9
to lu...@googlegroups.com
It was thus said that the Great Javier Guerra Giraldez once stated:
>
> case in point: a big part of why i no longer like to program in Python, is
> because the last decade the worst ideas became almost "official", so now
> it's nearly impossible to consistently configure formatters and linters to
> an acceptable style. Go and Zig are far more strict in that aspect, the
> formats aren't configurable at all, but the styles are (mostly) sane, so
> they do a good job to improve readability. In both cases, there is a style
> guide defined before, and the formatters (which existed basically on day
> one of each language) are based on them.

To me, this reads as: the Python formatter goes against most of my
opinions of how Python should be formatted, so I don't like it; Go and Zip
formatters match my opinions better, so I like tham.

> [[ and of course, no formatter can make slop code readable, so the
> existence of "AI agents" in no way makes them antiquated ]]

No, a formatter can format such code; whether it's understandable is a
different matter. Well formatted code can still be unreadable.

-spc

Sean Conner

unread,
Sep 9, 2025, 9:53:46 PMSep 9
to lu...@googlegroups.com
It was thus said that the Great Sean Conner once stated:
>
> In section 6.2 your example for using assert() is not a very good one.
> You state:
>
> > Use assert for programmer errors and preconditions.
>
> but the example is neither an error or a precondition. As I don't really
> use assert() in Lua, the only good example of a precondition I can come up
> with is an example from C (from my Lua CBOR implementation):

I finally did find a (in my opinion) a decent example of checking for an
error with assert() in Lua. In the code I have to drive network connections
via coroutines [1], I have this bit of code:

function timeout(when,...)
local co = coroutine.running()

if when == 0 then
if TOQUEUE[co] then
assert(TOQUEUE[co].co == co)
TOQUEUE[co].trigger = false
end
else
TOQUEUE:insert(when,co,...)
end
end

The assert() there is to ensure the table entry in the timeout queue not
only has a reference to the coroutine, but that it's the correct coroutine.
If it's not, it's a bug. And one other example:

...
elseif status == 'suspended' then
local ret = { coroutine.resume(unpack(co)) } -- [2]

...

if coroutine.status(co[1]) == 'dead' then
assert(REFQUEUE._n > 0)
REFQUEUE[co[1]] = nil
REFQUEUE._n = REFQUEUE._n - 1
end
...

Here, when a coroutine finally finishes and returns 'dead', we ensure the
reference queue count isn't already 0; if it is, again, there's a bug
somewhere in the code.

Both asserts were added to help debug the codebase, and left in. The
conditions tested *must* be true (again, to reiterate) otherwise there's a
bug in the code.

My stance on how to use assert() has been colored by C.

-spc

[1] https://github.com/spc476/lua-conmanorg/blob/master/lua/nfl.lua

[2] This variable could be named better. It's not a coroutine, it's an
entry from the runqueue that contains not only the coroutine
reference, but parameters to be passed (back) to it.

bil til

unread,
Sep 10, 2025, 2:44:31 AMSep 10
to lu...@googlegroups.com
Am Mi., 10. Sept. 2025 um 02:20 Uhr schrieb sur-behoffski
<sur-be...@grouse.com.au>:
>
> So a readable, consistent style is valuable.

What OS do you dominantly use for editing your Lua files?

If you use Windows, you could check "Visual Studio Code" - nicely
free. Use Sumneko Lua addon.

This does such identing automatically (I think even without Sumneko
add on, but the SUmneko addon adds automtic code checking during
entry... very useful). I think the only condition is, that your file
ending is ".lua".

Sainan

unread,
Sep 10, 2025, 2:53:58 AMSep 10
to lu...@googlegroups.com
Indentation is usually handled by increaseIndentPattern / decreaseIndentPattern set in the tmPreferences of a TextMate 'syntax highlighting' bundle (or language-config.json in the case of VS Code because who cares about industry standards).

You will probably find a 'reindent' feature in any editor supporting TextMate bundles.

-- Sainan

sur-behoffski

unread,
Sep 10, 2025, 3:29:02 AMSep 10
to lu...@googlegroups.com
On 2025-09-10 16:14, bil til wrote:
> Am Mi., 10. Sept. 2025 um 02:20 Uhr schrieb sur-behoffski
> <sur-be...@grouse.com.au>:
>>
>> So a readable, consistent style is valuable.
>
> What OS do you dominantly use for editing your Lua files?

Heh -- If you've tracked my (infrequent) messages over time, you
would know that I only use GNU/Linux machines -- I don't have a
Windows machine at all (not even via virtualization).

I use Emacs as my main work editor, and it has a Lua mode for
syntax highlighting and automated indenting. (I occasionally
use nano where I'm bootstrapping a machine, and Emacs isn't
installed yet.)

Emacs is very, very highly programmable, and contains variables
(in Emacs-LISP) for greater levels of customisation. For
example, I despise multiple (more than 3-4?) levels of nesting,
and so use 8-column tab stops to force me to review/revise code
when it's running off the page past column 72 (yeah, I know,
COBOL punch cards and all that). I also manage whitespace to
refuse tab characters and also refuse trailing whitespace, so
that, when using content-based source code control systems
such as Git, two printouts with an identical appearance will
have an identical checksum (sha1sum, sha256sum, CRC-32, etc.).
[Unix-y systems have LF ('\n') line terminators, not CR-LF
("\r\n") that Windows/DOS inherited from CP/M.]

--------

Just a thought: There's possibly going to be a revolution in
the LuaRocks package database, as Lua 5.5 proceeds from its
current Beta status, through Release Candidates to the final
release. Potentially many packages may need changes to fit in
to Lua's API (perhaps C code more than Lua code).

Where a package does require modification, perhaps add a soft
request to "please consider tailoring coding style
(indentation, name, upper/lower case, documentation (LDoc?),
etc.) to conform to the current PiL example layout, plus add
extra fields such as @Author, @Copyright and @License to the
header, and document @Param-eters, @Return values and
@Exceptions for each function".

I believe that the above is too much to ask for, but I'm
throwing it out as an idea anyway.

--

cheers, s-b etc etc etc

Marc Balmer

unread,
Sep 10, 2025, 5:37:46 AMSep 10
to lu...@googlegroups.com
does it need one?

What next? code of conduct?

Spar

unread,
Sep 10, 2025, 9:57:16 AMSep 10
to lu...@googlegroups.com
Well, believe it or not, but code style is important, but it's up to you what style you want to use for your codebase.
A single document about code format is useful to quickly teach new colleagues at work how to be consistent.
Clean and predictable code helps to develop faster.
 
Luarocks has its own code style and it mentions 5 more of them: https://github.com/luarocks/lua-style-guide
 
I would personally love to see C++ google style guide adapted for Lua
 
Leave your demotivation on other websites, there is a enough of it on the internet
Reply all
Reply to author
Forward
0 new messages