Single Script Architecture and Modular Programming

Hello everyone.
I would like to show and explain some of the types of scripting organizational methods to newer folks joining Luau and Roblox!

What is a module? (For newer folks)

Modules are simply an instance that can be created within your game. Simply create a new instance and search for ModuleScript and boom! You have a module.

Modules are mainly used through a function called require, which takes a parameter require(path). E.g. require(game.ReplicatedStorage.ModuleScript) would grab the code from inside of the instance and import it into your current script allowing you to utilize it. (The code is ran, whatever is returned is used in your current script, hence why you return the value or table)

--// ModuleScript
--// Path: game.ReplicatedStorage.MyModule
local module = {}
module.Hello = "World"

function module.World()
  print("Hello")
end

return module --// We return our table.
-------------------------------------------------------------
--// Main Script
local RStorage = game:GetService("ReplicatedStorage")
local MyModule = require(RStorage.MyModule) --// The code is retrieved through a table that MyModule returns!

print(MyModule.Hello)
MyModule.World()

Modular Programming


Modular Programming simply utilizes both external and internal modules that assist with the main applications of your game/frameworks. This allows for a more preferred organizational method, which essentially splits your system into more “sub-systems”. The entire purpose of this method is for re-usability of code.

To explain this a bit more. Imagine you have all your functions and values inside your script, your script becomes a total of 500+ lines of code and is looking pretty messy. Turn your functions into modules and require them!

--// Module
return function(message)
  print(message)
end

--// Script
local PrintFunction = require(path.to.module)
PrintFunction("Hello World!")

Anything can be returned through a module!
I highly recommend not using this specifically for a ton of functions. Rather use this programming style to recycle your functions. E.g. creating utilities to use in multiple scripts.

Single Script Architecture


SSA(Single Script Architecture) is simply what the name implies. Everything is contained within one script. While this method is completely fine, it can become quite tedious at larger amounts of code.

local System1 = {}
local System2 = {}

do
  self = System1
  self.Cookie = "Chocolate"
end

do
  self = System2
  self.Chocolate = "Cookie"
end

print(System1.Cookie, System2.Chocolate)
--// Returns: Chocolate Cookie

This is one of the many ways to organize SSA. While I do prefer this one there are other very simple ones like splitting your code into functions. This is another type of programming called Functional Programming. Which is simply splitting your code into functions for a more clean system. There also is another form of highly used programming called Object Orientated Programming, which I won’t be covering, but take a look at it!.


For any advanced or beginner users who are curious about the differences between the following. See below

  • Procedural Orientated Programming
  • Object Orientated Programming
  • Functional Orientated Programming
  • Modular Programming

9 Likes

Look at all the resources available → Search results for 'Object Orientated Programming' - Developer Forum | Roblox

1 Like

While true, it’s not the most accurate application of the term. Single script architecture for Roblox development generally implies a high degree of modularity. It boasts a single point of entry for your server and client (one Script, one LocalScript) while 100% of the other work is done in ModuleScripts.

Containing absolutely everything in one script does technically classify as single script architecture but we refer to that as monolithic scripts. Monolithic scripts are unreadable, unmaintainable and present issues with the Script Editor (sometimes it can become very laggy).

5 Likes

Thank you for the correction. Ill fix this post once im online. Completely forgot to specify its application on server and client bounds. I will probably add in a bit more clarification about the script I wrote for SSA, including the term monolithic scripts.

I always had questions about single-script architecture.

What are the benefits of it?

Will it be good for me, if I want to load ‘‘this’’ script earlier than others? Like, I want to run my Data script earlier and then run the other scripts.

How my module scripts should look like, how can I handle my remote events connections in module scripts and other signal connections?

I saw many posts about SSA, but I’m still thinking about it. I have good experience in structuring and organizing games, but, keeping in server and local scripts makes me confused. Modular trees look beautiful for me in my opinion. So, still have the questions, mentioned in my message at start. I want to know better about Single-Script Architecture.

2 Likes

Hello, incredibly late response lol and this article is a bit outdated, but no matter. To answer some of your questions. In reality there are no performance benefits to these paradigms if they are overused, they offer help when it comes to designing systems by sort of creating a guideline/rules to follow when making a system.

Roblox does load scripts in a sort of random order, so there is no direct way to guarantee your system is loaded in the same order without using a SSA/Procedural format. However, in your instance, there can be advantages to loading something earlier than another. Especially if it contains a lot of data which could cause race conditions later on because it took to long to load. Which is another reason SSA can become useful for specific things.

Module scripts are a bit tricky because they push OOP onto you, my suggestion is to read about different design methodologies that utilize the nature of OOP. You can always use a module script like this (below is a rough idea) to handle remote event connections

local playerRedeemEvents = {}

function playerRedeemEvents.promocode(player, code)
  -- Extensive code that redeems promocode
end

function playerRedeemEvents.twitchdrops(player)
  -- Extensive code that handles a donation
end

function playerRedeemEvents.welcomeBonus(player)
  -- Extensive code someone redeeming a welcome bonus.
end

return gameSpecialEvents

-- specialRedeemEvents.luau
local myRemoteEvent : RemoteEvent

local redeemCallbacks = require(...)

local function request(name, ...)
  local event = redeemCallbacks[name]
  if event then 
    event(...) 
  end
end

myRemoteEvent.OnServerEvent:Connect(request)

Sorry for any typos in that, but that gives an example of a system that does not directly prioritize optimization, but should help get a better understanding of the idea.

In the end single script architecture doesn’t literally mean “I’m gonna keep everything in one file for the server and client”, while you can absolutely do that, it just means you house a system or framework in a procedural format (instructions one by one).

SSA does get messy, so it’s wise to only use SSA on much smaller things. Keep in mind a system is apart of a constant loop. (Not a literal loop), but they are always active in the games environment, of course there are conditions in which the systems are put into an idle state, but they remain active.

SORRY FOR TYPOS!