Future Proofing: Protecting your code from the unknown

A sort of follow up on my first guide which I recommend reading as well as it teaches you how to make your code coherent

When people are asked “What is Future Proofing?”, they’ll most likely be confused as they may have never heard of that word before or never even thought about it before, or may even give you a correct definition but don’t show it when you see their code. With that out of that way, you may be asking yourself “Embat what are you even going on about?”

Future Proofing is a topic in programming that describe the process of making your code “loose” in order to make adding future code/features easy as pie without breaking existing functionality that the code already has. Now with a definition you may probably not understand immediately, so let’s go into further detail with some examples

Say you want to make it so you and other specific users have 150 MaxHealth instead of 100 on spawn. You say:

“Simple enough”

And in the end you have this

local players = game:GetService("Players")

players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(function(char)
		local human = char:WaitForChild("Humanoid")
		if char.Name == "EmbatTheHybrid" or char.Name == "StrongBigeMan9" then
			human.MaxHealth = 150
			human.Health = 150
		end
	end)
end)

It does what you wanted, but there’s 2 issues

Firstly, if anyone does this, don’t try to do something for a specific player by checking their Name, always use their UserId. It may be obvious now, but there may be others who still use the old method. The reason this is bad is what if me or StrongBigeMan9 change our usernames? The code wont work for either of us anymore, so we’d have to update it with our new name, which is time consuming than if you used UserId

Secondly, another issue may stem in the future where you may want to give another player this benefit, seems simple enough, just add another condition into the if statement. The issue is the statement will look like this by the time you add 5 players

local players = game:GetService("Players")

players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(function(char)
		local human = char:WaitForChild("Humanoid")
		if char.Name == "EmbatTheHybrid" or char.Name == "StrongBigeMan9" or char.Name == "ROBLOX" or char.Name == "Shedletsky" or char.Name == "CrazyBlox" then
			human.MaxHealth = 150
			human.Health = 150
		end
	end)
end)

This is not good practice as all this does is impact readability since now we have a very long if statement that’ll get even longer once we add even more players

“So how do I make it better?”

It’s simple. First we use UserId instead of the name to avoid conflicts in the future, and we put all the Ids in a table outside of the event and check if the player’s UserId is in that table, indicating they’re one of the special players we want to gie an advantage to

local players = game:GetService("Players")

local playerIds = {
	674847948,
	638789589,
	1,
	684497890,
	56746878
}

players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(function(char)
		local human = char:WaitForChild("Humanoid")
		if table.find(playerIds, plr.UserId) then
			human.MaxHealth = 150
			human.Health = 150
		end
	end)
end)

With a simple change, we have significantly improved the readability of our script, and also made our code simpler to add onto since if we want to add another player to earn this bonus, we just add a new id into playerIds, effectively Future Proofing it

Let’s go back to the example of giving players a health benefit, what if you decide to give yourself and a friend of yours 200 hp but the others get 150?

“Can’t I just make 2 tables for that?”

local players = game:GetService("Players")

local playerIds200 = {
	674847948,
	638789589
}

local playerIds150 = {
	1,
	684497890,
	56746878
}

players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(function(char)
		local human = char:WaitForChild("Humanoid")
		if table.find(playerIds200, plr.UserId) then
			human.MaxHealth = 200
			human.Health = 200
		elseif table.find(playerIds150, plr.UserId) then
			human.MaxHealth = 150
			human.Health = 150
		end
	end)
end)

This will work as well, but it complicates it more than we need it to be.

“So if this is not the best method, how do I do this?”

A simple way of doing this is with a dictionary that contains the UserId as a key, and the health we want to give them as the value

local players = game:GetService("Players")

local playerHealthIds = {
	[674847948] = 200,
	[638789589] = 200,
	[1] = 150,
	[684497890] = 150,
	[56746878] = 150
}

players.PlayerAdded:Connect(function(plr)
	local idHealth = playerHealthIds[plr.UserId]
	
	if not idHealth then
		return
	end

	plr.CharacterAdded:Connect(function(char)
		local human = char:WaitForChild("Humanoid")
		human.MaxHealth = idHealth
		human.Health = human.MaxHealth
	end)
end)

This will do exactly as the previous code, but is even simpler since we don’t need to specify numbers after the if statement, they’re already there in the dictionary. This also has another Future Proofing benefit of being very easy to add another id to this code as we can just add another entry in the dictionary and is extremely easy to change the health one will get by changing their entry’s value.

This works because if it doesn’t find the UserId in the dictionary, it will return nil, which won’t run the code in the if statement if it is, if it isn’t, then it will run it

Now I want to make it so we each get a tool except ROBLOX, do I just make another dictionary and change my code around?

You could, but there’s a better way to do that by setting the Value as a table with the contents you want to give to that player, which will require changing up the dictionary a bit, but in the end, you’d have something like this

local players = game:GetService("Players")

local playerIds = {
	[674847948] = {Health = 200, ToolName = "GravityCoil"},
	[638789589] = {Health = 200, ToolName = "GravityCoil"},
	[1] = {Health = 150},
	[684497890] = {Health = 150, ToolName = "Sword"},
	[56746878] = {Health = 150, ToolName = "SpeedCoil"}
}

players.PlayerAdded:Connect(function(plr)

    local idtable = playerIds[plr.UserId]
    
	if not idtable then
		return
	end
	
	local toolname = idtable["ToolName"]
	if toolname then
		local tool = game.ServerStorage.Tools[toolname]
		tool:Clone().Parent = plr.Backpack
		tool:Clone().Parent = plr.StarterGear
	end
	
	local health = idtable["Health"]
	if health then
		plr.CharacterAdded:Connect(function(char)
			local human = char:WaitForChild("Humanoid")
			human.MaxHealth = health
			human.Health = human.MaxHealth  
		end)
	end	
end)

But wait, won’t it error since ROBLOX doesn’t have ToolName in their table?

It won’t since it checks first if something exists there and if it doesn’t, then it wont try to give a tool, meaning nothing will go wrong unless one of the values in the dictionary is not a table, in that case it will error, OR if you don’t have a tool with that name in the folder, which is probably unlikely since you’d be sure to have tools with those names in the folder

And this also has another benefit since it will only connect the event if they are in the table! Improving it significantly depending on the player amount to reduce the amount of unneeded connections, but if you need to do something for everyone regarldess if they’re in the list or not, then just put the CharacterAdded event outside of the check and just check if idtable for the userid exists.

With this you now should be able to understand how and when you should Future Proof your code. If you’re still confused on when you should, just think to yourself

If I ever have to add a new condition to this code, would it be difficult and would it affect readability?

“add a new condition” meaning adding something that will cause something to happen, like adding a new UserId like how I did in this example.

If you think about that question and assess the methods of adding a new condition and you answer “Difficult” or “Affects readability”, then it’s a clear indication you need to loosen your code a bit for that situation where you may want to add something in your code in the future to help future you out

Tip: Use :GetService() when trying to get a Service rather than indexing it, such as using game:GetService("Players") rather than game.Players so it can be more consistent with obtaining other Services and will help in the event that there’s something else called Players, which would error

Thank you for reading my second guide and please reply if you find anything you think it’s wrong so I could fix it

52 Likes

This is a great tutorial! Although you might want to also inform people that you should use game:GetService() when getting a service instead, as directly indexing it would break in the event the service’s name wasn’t what you expected.

4 Likes

Thanks for the catch! I did it like that since I still have that habit of indexing it directly. I’ll fix that up!

Edit: All done!

1 Like

You could just check the Player instance’s name instead of just connecting an event to every single player’s character to check the name. Small performance boost!

Overall, I think I’ve known future proofing already before this tutorial. This is good.

You could do that, but I don’t think it would make that much of a difference since you’d still have to connect an Event to change the health of their Humanoid as the example was trying to do

Using plr.Name or char.Name don’t really make a different for what the code is wanting to do

One thing is you’re connecting an extra event to the characters. I know it wouldn’t give much performance boost but still worth a tiny boost.

Wait where are you seeing an extra event? I think I may be missing an obvious thing, but all the code is doing

  • Connect an event to the player
  • Connect an event to their character
  • Get their humanoid
  • Is their character’s name EmbatTheHybrid or StrongBigeMan9?
  • If yes, give them 150 hp

Pardon me if I’m actually missing something with what you’re trying to say, using plr.Name over char.Name wouldn’t really give that much of a boost to be worth doing as they’re both checking a property

Edit: I understand what you mean now, you could do like that or how I did it, but that’s just personal preference anyways

I think he means that you should check the player name BEFORE you connect the character event, like this:

players.PlayerAdded:Connect(function(plr)
if char.Name == "EmbatTheHybrid" or char.Name == "StrongBigeMan9" then
	plr.CharacterAdded:Connect(function(char)
		local human = char:WaitForChild("Humanoid")
			human.MaxHealth = 150
			human.Health = 150
		end)
	end
end)

instead of:

players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(function(char)
		local human = char:WaitForChild("Humanoid")
		if char.Name == "EmbatTheHybrid" or char.Name == "StrongBigeMan9" then
			human.MaxHealth = 150
			human.Health = 150
		end
	end)
end)

You do the same work, but now you dont have to connect the character event for players who aren’t wanted.

Your code wouldn’t work since Char would be nil. But I could understand what you mean by that regardless. Yes you could do it like that as well actually, but adding that/replacing the code in my guide to suit that instead would be a bit of a hassle, plus it wouldn’t give a huge performance boost if you did it like that compared to what I used, it’s basically just personal preference.

There’s a better word than “Future Proofing”, it is called “backward compatibility” and “forward compatibility”.

This describes compatibility for newer versions ahead. This applies to the game design in progress.

This is the opposite of forward compatibility. However, it is more for the old games to remain functioning.

5 Likes

I know I am a bit late, but only connecting the event if they meet the requirement could give a significant boost if you say, had over 100 or more players in the server. In general though I think tutorials should promote better practices like only connecting an event if it is needed, because it wastes memory if it isn’t needed and this can be vital when it comes to optimising for large player servers.

1 Like

I guess that’s fair enough, I fixed it up a bit to suit that in the last code block, I’ll keep it in mind when I work on other tutorials

2 Likes

ROBLOX scripts are gone really crazy at it.

I ever make a ninja animation script but it don’t work.

I don’t see what is the issue. the issue is unknown

1 Like