According to ROBLOX developer wiki, you can write all of your functions in one script and use it everywhere.
“ModuleScripts are essential objects for adhering to the don’t-repeat-yourself (DRY) principle. When you write a function, write it only once and use it everywhere.”
This begs the question, why do we need regular scripts then?
I understand that ModuleScripts require a normal script to run them, but then why aren’t all scripts able to share code?
Regular scripts are usually functioning like a bootstrapper, while the modules are the libraries that are being used. To avoid ‘reinventing the wheel’, you simply use the modules necessary for a certain functionality, that you don’t want to rewrite every single time.
Because each script is running their own ‘code environment’, they cannot share variables across unless they used the true global scope of _G(which I don’t really use).
Some common repetitive practice that is avoided is the ‘one script per model’ concept. You have a model that works in a certain way, which includes a script inside. However, you want to change or update the code and that would be tedious. That is why modules should be considered. Alternatively, a different paradigm should be considered which works equally well.
Lack of module scripts only means difficulty in organization, especially when it comes to debugging. Also if you used multiple regular scripts, race conditions may occur, meaning one script may be ahead of the other.
You still need a bootstrapper to execute those ModuleScripts. It is advisable to treat modules as libraries for specific functionalities. That said, it is perfectly fine but you need to convert some part of the original code to work with that.
Also some of my previous frameworks utilized the concept of ‘one script’ only, backed by multiple modules.
In all my scripts, I tween values. If I wanted a part’s transparency to gradually decrease, instead of making a tween function, I have a ModuleScript that has this function:
function module.tweenValue(value1, value2, changeTime)
local tweenService = game:GetService("TweenService")
local tweenInfo = TweenInfo.new(changeTime)
local tween = tweenService:Create(value1, tweenInfo, value2)
tween:Play()
end
This specific ModuleScript function is super useful because I don’t have to rewrite the function whenever I use it, and can be used in multiple scripts! It’s not necessarily bad if you don’t use Modules but it helps to keep your code organized without violating the DRY (Do not Repeat Yourself) principle.
If you don’t use ModuleScripts I would highly recommend it as it saves you on time and effort!
I can provide an example of a previous module that I have been working on, its version is currently 2.22.1, and still needs some extra work put into it. This time, it’s metatable-based.
-- Functions
function Clock.new(length, clockMethod, clockDisplayType, clockDisplayTypeProperties)
return setmetatable({
Completed = true;
CurrentTime = 0;
StartTime = 0;
Length = type(length) == "number" and length or 10;
ClockMethod = findClockMethod(clockMethod);
ClockDisplayType = findDisplayType(clockDisplayType);
ClockDisplayTypeProperties = getDigitalSettings(clockDisplayTypeProperties);
}, {__index = Clock})
end
function Clock:Read()
return rawget(self, "Completed")
end
function Clock:GetCurrentTime(displayType, overrideProperties)
return getDisplayFromDisplayType(
rawget(self, "CurrentTime"),
displayType or rawget(self, "ClockDisplayType"),
overrideProperties or rawget(self, "ClockDisplayTypeProperties")
)
end
Note: Clock is a table that is returned in the end of the code, I cut this snippet to demonstrate that modules can house objects.
ModuleScripts are important in a game loop action, when using a server script with requiring a module script, the module will have like functions to help you the game loop so for better readability.
Module scripts are important so other scripts can require it easily, and if you’re just making big functions in a server script, other scripts can’t get the functions, besides copy and pasting to make your code unprofessional.
A more simple example in my opinion would be, let’s say a permissions module responsible for returning whether or not the player you pass is above a specific rank in the group. We’re also going to assume that that the required rank is predefined in your module.
You might be asking, why can’t you just check their rank in the script instead of going through all this module nonsense? Well, let’s say you want to change the required rank in the future. You don’t want to have to go through every single script that checks a player’s rank, and instead you can just change it in your module.