Skip to content

Instantly share code, notes, and snippets.

@MaximumADHD
Last active November 4, 2026 18:57
Show Gist options
  • Select an option

  • Save MaximumADHD/d1e52ca3423b59bb25fb2882aebec6a2 to your computer and use it in GitHub Desktop.

Select an option

Save MaximumADHD/d1e52ca3423b59bb25fb2882aebec6a2 to your computer and use it in GitHub Desktop.

MaximumADHD's "Smooth-Flow" Luau Style

Smooth-Flow is my nitpicky Luau coding style that encases various patterns that I've observed with how I've gone about writing and cleaning up Luau code over the past few years.

It is 99% compatible with StyLua and Roblox's Lua style guide, it just introduces some additional nitpicking and aesthetic changes to help with the readability and overall presentability of the Luau code.

Style Goals

  • Write code that groups related actions and assignments.
  • Produce aesthetically consistent code through simple rules.
  • Favor pre-declared roots to tighten redundant/opaque traversal through nested paths.
  • Make code more pleasing to read at a glance, horizontally, vertically, and even diagonally if necessary.

Type checking

In Luau's new type solver, using --!strict can tend to be more of a hinderance than a necessity, but it is still recommended that you follow the type checking hygiene outlined in this devforum post:

https://devforum.roblox.com/t/type-annotations-a-guide-to-writing-luau-code-that-is-actually-good™/2843221

Dependencies

Dependencies will always come first. This is where we route out what modules and services we need.

Some good rules of thumb to follow:

1. Don't repeatedly access object.Parent or object.Child more than once.

Not only is it redundant to write script.Parent.X over and over again, Roblox has to do (an admittedly negligable amount of) work each time you walk through the DataModel. But more importantly, it's unnecessary noise and clutter. It is much cleaner and less verbose to get references to the roots of each child you intend to access more than once.

Bad:

local Guard = require(script.Parent.Packages.Guard)
local Example = require(script.Parent.Example)
local Backend = require(script.Parent.Backend)
local Signal = require(script.Parent.Packages.Signal)

Good:

local Shared = script.Parent
local Packages = Shared.Packages

local Backend = require(Shared.Backend)
local Example = require(Shared.Example)
local Signal = require(Packages.Signal)
local Guard = require(Packages.Guard)

2. Name variables after modules/services consistently.

It's a good practice to make sure the dependencies you are including are imported as they are named in your codebase, so it's apparent at a glance what is being referred to.

Bad:

local Guard = require(script.Parent.Packages.Guard)
local sig = require(game.ReplicatedStorage.Library.Packages.Signal)

local OwnerSHIP = require(script.Parent.Ownership)
local emote = require(script.Parent.Emotes)
local Gamepass = require(script.Parent.GamePasses)
local Settings = require(script.Parent.Settings)

local troveLib = require(script.Parent.Packages.Trove)

Good:

local Shared = script.Parent
local Packages = Shared.Packages

local Guard = require(Packages.Guard)
local Trove = require(Packages.Trove)
local Signal = require(Packages.Signal)

local Settings = require(Shared.Settings)
local Ownership = require(Shared.Ownership)
local GamePasses = require(Shared.GamePasses)

3. Prioritize line length over alphabetical, with an emphasis on "smoothing" the shape of "require blocks"

This can appear ascending, descending, or even a mix of the two, but ideally you want to be able to read the list of requires like they are stairs or pyramid shaped.

Bad:

local Shared = script.Parent
local Packages = Shared.Packages

local Auras = require(Shared.Auras)
local Backend = require(Shared.Backend)
local Emotes = require(Shared.Emotes)
local GamePasses = require(Shared.GamePasses)
local Guard = require(Packages.Guard)
local Marketplace = require(Shared.Marketplace)
local Migration = require(Shared.Migration)
local MusicData = require(Shared.MusicData)
local Mutex = require(Packages.Mutex)
local Network = require(Packages.Network)
local Ownership = require(Shared.Ownership)
local PlayerData = require(Shared.PlayerData)
local Promise = require(Packages.Promise)
local Recents = require(Shared.Recents)
local RunContext = require(Packages.RunContext)
local ServerAudio = require(Shared.ServerAudio)
local Settings = require(Shared.Settings)
local Signal = require(Packages.Signal)
local Tags = require(Packages.Tags)
local Trove = require(Packages.Trove)

Good:

local Shared = script.Parent
local Packages = Shared.Packages

local Tags = require(Packages.Tags)
local Trove = require(Packages.Trove)
local Guard = require(Packages.Guard)
local Mutex = require(Packages.Mutex)
local Signal = require(Packages.Signal)
local Promise = require(Packages.Promise)
local Network = require(Packages.Network)
local RunContext = require(Packages.RunContext)

local Auras = require(Shared.Auras)
local Emotes = require(Shared.Emotes)
local Backend = require(Shared.Backend)
local Recents = require(Shared.Recents)
local Settings = require(Shared.Settings)
local Migration = require(Shared.Migration)
local MusicData = require(Shared.MusicData)
local Ownership = require(Shared.Ownership)
local GamePasses = require(Shared.GamePasses)
local PlayerData = require(Shared.PlayerData)
local Marketplace = require(Shared.Marketplace)
local ServerAudio = require(Shared.ServerAudio)

Function spacing

  • Write anonymous functions as function (...).
    • Rationale: function is a keyword, whereas function(...) looks like you are calling function(...) as though function is a function that can be called.
    • Keep this consistent in callbacks, hooks, and event handlers.

Whitespace cleanliness

Vertical spacing by statement type

  • Add a blank line when switching between statement kinds, especially:
    • assignment/declaration -> if/for/while
    • control flow block -> side-effect call sequence
    • local extraction -> guard clause
  • Use blank lines to mark semantic steps inside a block: extraction, guard, derivation, validation, branching, side effects, async setup, loop body phases, and cleanup.
  • Multiline assignments/table fields should usually have a blank line before the next distinct sibling entry, especially before event handlers or large e(...) children.
  • Keep single-action return/break pairs and homogeneous scalar groups tight.
  • Prefer to declare local variables at the top of a group when possible/reasonable, even consider breaking groups up if needed.

Local grouping

  • Group related locals with blank lines between groups.
  • Group require locals by their root container (Packages, Shared, Components, Hooks, etc.). Within each group, order the lines so the block has a smooth visual shape by line length (ascending, descending, or pyramid is fine); avoid jagged blocks.
  • Typical grouping order:
    1. context/hooks
    2. derived values
    3. refs/state setters
    4. helper functions
  • When variable names are similar (minCell, maxCell, etc.), keep them together.

Guard-first flow

  • Prefer an early continue/break/return over deep nesting.
  • Use one guard per condition when readability improves.

Naming preference

  • Prefer explicit names over terse names (movedId over v).
  • Keep paired names symmetric (minCell/maxCell, songId/playlistName).

Scope and safety

  • Style passes are non-functional by default.
  • Avoid broad reformat churn when only a focused region needs cleanup.

Examples

- value.Changed:Connect(function(newValue)
+ value.Changed:Connect(function (newValue)
    -- ...
end)
local function depthForWidth(resolution: Vector2, fov: number, partWidth: number, visibleSize: number): number
	  local aspect = resolution.X / resolution.Y
+
	  if aspect ~= aspect then -- NaN guard
		    return 0
	  end
+
	  local resX = math.max(1, resolution.X)
	  local resY = math.max(1, visibleSize)
+
	  local hfactor = math.tan(math.rad(fov) / 2)
	  local wfactor = aspect * hfactor
+
	  if wfactor <= 0 then
		    return 0
	  end
+
	  return 0.5 * resX * partWidth / (resY * wfactor)
end
React.useEffect(function (): any
    local frame = frameRef.current
+
    if not frame then
        return
    end
local function isAdmin(player: Player): boolean
    if not isAllowlistEnabled() then
        return true
    end
+
    if ADMIN_USER_IDS[player.UserId] then
        return true
    end
+
    if ADMIN_GROUP_ID > 0 then
        local ok, rank = pcall(function ()
            return player:GetRankInGroupAsync(ADMIN_GROUP_ID)
        end)
+
        if ok and type(rank) == "number" and rank >= ADMIN_MIN_RANK then
            return true
        end
    end
+
    return false
end
	local clone = template:Clone()
-
	local model: Model
+
	if clone:IsA("Model") then
		model = clone
	else
		-- BasePart / Folder / anything else: wrap it so we have a Model to PivotTo + Scale.
		model = Instance.new("Model")
		clone.Parent = model
	end
+
+   local primary = model:FindFirstChildWhichIsA("BasePart", true)
-   model.Name = "ShopAuraPreview"

-   local primary = model:FindFirstChildWhichIsA("BasePart", true)
	if not primary then
		model:Destroy()
		return nil, nil
	end
+
    model.PrimaryPart = primary
+   model.Name = "ShopAuraPreview"
  -- ...
  -- Correct for horizontal FOV distortion off-centre (a wide screen stretches
  -- the apparent width away from the middle). Mirrors View3D's `scaleDown`.
  local aspect = worldView.X / math.max(1, worldView.Y)
  local xScale = center.X / math.max(1, worldView.X)
+  
  local distortion = 2 * math.abs(xScale - 0.5)
  local scaleDown = 1 + (aspect - 1) * distortion
- 
  local depth = depthForWidth(worldView, cam.FieldOfView, width, visibleSize) * zoom * scaleDown
+
  if depth ~= depth or depth <= 0 then
      return
  end

  local ray = cam:ScreenPointToRay(center.X, center.Y)
  local frameCenter = ray.Origin + ray.Direction * depth
+
  local cf = CFrame.lookAt(frameCenter, frameCenter + ray.Direction, cam.CFrame.UpVector) * FACE_CAMERA
  model:PivotTo(cf)
@prizmdev1

Copy link
Copy Markdown

Thanks!

@kiaseh

kiaseh commented Oct 19, 2026

Copy link
Copy Markdown

Fair enough, been having trouble with trivial stuff like this; following rules helps a lot!

@antitheists

Copy link
Copy Markdown

<3

@dooory

dooory commented Oct 22, 2026

Copy link
Copy Markdown

Goated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment