Is using shared to simplify code good practice?

I am creating a framework which has folders: Client, Server, and Data. Instead of having to do game.ReplicatedStorage.Client or game.ReplicatedStorage.Server, I can do shared.Client or shared.Server. In addition, I could add getters and setter functions to help me. This is being set before the modules are required (I have a modular system). The question is: Is it right to do it this way? I’ve heard that shared, like _G, is not good practice, so is it correct to do so?


shared and _G are basically the exact same thing stored in separate tables. Both global tables. You should avoid using either as they, like any global variable, bring along significant performance issues compared to using a ModuleScript.


I use _G extensively to store references to services, and it works fine for me. The main advantage here that I don’t need to do like a :WaitForChild() call whenever i’m referencing services (to make sure they load first).
Also, I wrote my own service loader which enforces certain rules about services (i.e adding dependency ordering to services), and while this is not strictly impossible without _G, it would be a bit more annoying to write.

NOTE: Just be sure to only use _G AFTER loading all the services


I don’t think _G or shared should ever be used in modern scripts. It’s like using a global variable, it has no scope to limit access to it, increasing the chances of another script causing a conflict.
If you really need to keep everything together, why not make a ModuleScript that returns a table? That way, you are adding scope (the variable that stores the table), reducing the chances of a conflict.


This is completely wrong. If you define a reference to a module’s table as a global it has the same performance as a global variable; likewise if you define a global as a local it’ll perform as a local.


I was referring to referencing a module’s table as a local variable vs referencing _G or shared as global.


ModuleScripts can still be used as globals, so that last part is at best misleading.
x = require(thing) is still a global variable.


I’m not really sure what the whole argument behind “don’t use _G/shared” is or why it’s so “bad”, when you can still localise those variables to a scope. There’s nothing inherently wrong with using _G/shared or accessing your repositories directly from services. There are still people who do that. I’m quite sure an actively updated framework by @Crazyman32 (cc) uses _G (Aero).

I wouldn’t really say that it’s good or practice, just a preference that you’ve chosen. You’d rather store your repositories under _G or shared instead of having to repeatedly write services. There’s nothing wrong with that. Just be mindful of when you still need to include references to services or that you’re assigning parts of _G/shared to local variables.

On the other hand; an alternative method I’ve seen some developers use or some frameworks possess is having a ModuleScript in ReplicatedStorage that effectively acts as this bridge, instead of _G/shared. That module can be used by either the client or the server for lazy-loading or fetching modules from repositories. An alternate method, fine as well.

It’s completely your choice how you wish to proceed in this matter.


Just to clarify, this is done just so developers can handle weird edge cases where they need to use the framework but aren’t within the actual structure (e.g. maybe a script that only exists per certain maps in a game).

It is typically considered very bad practice to use the global environment in any language. Use it sparingly and only when absolutely necessary.

Read this article: “Global Variables Are Bad”


I don’t think global variables are bad. It depends on your usage. math, string, table etc are global variables. They’re not “bad” because they’re read-only and always set before your code runs. The concept of bad globals I think is an old one that goes far back to data races, but besides potential data races for all-access global variables, the negatives are mostly language-dependent (such as performance at the lower level because of cpu caching and such) or just often subjective user preference.
Sure, there are other instances you could poke at which in truth are bad practice, but there’s nothing taboo about global variables if you use them right.