I still dont see whats wrong with _G

I have been wanting to use a _G variable so that I can store a function for spawning monsters and put them into a table, which is also stored under _G.

For example:

_G.Enemies = {}

_G.SummonEnemy = function(Name, Position, Modifier)
spawn(function()
local Enemy = game.ServerStorage.Monsters[Name]:Clone()
table.insert(_G.Enemies, Enemy)
print(_G.Enemies)
Enemy:PivotTo(Position)

    Enemy.Humanoid:SetStateEnabled(stateType.FallingDown, false)
    Enemy.Humanoid:SetStateEnabled(stateType.Ragdoll, false)
    
    Enemy.Parent = workspace.Alive

    HitModule(Enemy)
end)

end

I’ve heard concerns about using _G because it can make programs “unpredictable,” but I still don’t understand the logic behind it. I’ve used it, and it seems perfectly fine to me. I don’t see anything wrong with it, and I know there are other ways to achieve the same effect. However, I don’t understand how using _G makes it worse, especially if this is the only thing I’m using it for. I’m not changing the table with any other script, and I exclusively use #_G.Enemies to get the number of enemies in the game.

I am self-taught, so I have a lot of bad habits, such as not setting values properly, among others. However, I just don’t see why _G is considered so “forbidden.”

Global scope is commonly a bad practice to use for most languages. The thing is that the variables get declared globally are easily liabilities and concerns a counter-quality to coupling. It makes it very hard to keep up with its cohesion. Pretend that _G is like working with a ball of papers, as opposed to origami when using proper variable scoping.

It doesn’t mean that _G is not out of its purposes. Sometimes it has its use cases.

Would you say my case is one of them?

Not really. You could just use a module script and write it like one. _G is rarely a common case.

I don’t use _G myself, but the only negative aspect of using shared or _G is that it tends to have “race conditions”
What this means is that multiple threads (scripts in this case) are trying to get the same value, but the main script that makes the value exist isn’t ran, causing other scripts to simply error.
Sure, you can make it so it waits until it exists, but at this point it’s making the code somewhat messy.

Any other reasons that it can break is because:

  • Scripts can overlap with each other, assuming that you’re using free models/3rd party scripts that makes use of the _G or shared globals.
  • _G and shared can be setmetatable()'ed.

While it’s not useless, it’s generally discouraged to keep a clean code environment.

1 Like

As @Operatik stated, the use of global variables is a bad practice.

If you want to have the ability to use “SummonEnemy”, I would recommend you use either a BindEvent or a RemoteEvent to accomplish this task.

So it would look something like this:

-- Server Script

local enemies = {}
local bind: BindableEvent = game:GetService("ReplicatedStorage").SummonEnemies

-- // Function

local function spawnEnemy(name, position, modifier)

   local Enemy = game.ServerStorage.Monsters[Name]:Clone()
   table.insert(enemies, Enemy)
   Enemy:PivotTo(Position)
   Enemy.Humanoid:SetStateEnabled(stateType.FallingDown, false)
   Enemy.Humanoid:SetStateEnabled(stateType.Ragdoll, false)
    
   Enemy.Parent = workspace.Alive

   HitModule(Enemy)
end


-- // Event

bind.Event:Connect(spawnEnemy)

As a side note: Try to always keep the scope of your variables as local as possible (have the shortest scope). It’s good practice and helps with code maintenance.

sorry if this is frustrating but I still dont see how thats strictly superior. Could you give an example of when its like the perfect time to use _G?

what if I need to get the table of enemies from any script whenever I need?

This is concerning “normal” global variables and functions, but i guess it is same for “general” global variables and functions, maybe even worse in term of performance and memory usage.
Capture

Again. The use of BindableEvents or RemoteFunctions (only if on Client) can help there.
Rather than focus on that problem, I’ll focus on teaching you why the use of global variables can be bad.

In general, _G variables have a much larger scope than necessary. You should only be able to access a variable if you need it, and not give every script the ability to read/write into it.

Let’s assume you have numerous scripts writing and deleting to the same global variable. If at any moment you read that global variable, and find data that shouldn’t be in it, or should be in it but isn’t, then you will have a very hard time to find the correct script which is making this mistake.

Another example would be using free models, which uses global variables. If by chance they would have the same name… Well, imagine the pain that would create. Constant errors, and worst of all: You wouldn’t even know where they come from, since you don’t know what’s doing the write, your scripts or the free model one?

One last example: Using global variables would defeat the purpose of Modularity. Modularity is a form of organizing your code (or environment) in a way that would give a specific scripts (or tasks) access to ONLY the things the script would need for it to function.

Again: Make sure only the necessary scripts get access to the necessary values. In other words, keep your variables to the smallest scope possible. According to Phil Koopman’s video on Global Variables, a good compiler would be capable of creating more efficient code with smaller scope variables.

3 Likes

I think I understand, its just that it doesn’t have many uses where something else isn’t just more resource optimal? I still don’t understand why some devs I’ve interacted with insist its so bad to the point of insulting me for using it.

personally I don’t understand how the organization stuff is an issue outside of possibly working with multiple people on one project

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.