You and Your Code: A guide on Script Coherency

Code is one of the most important things when making a game, almost anything you play on Roblox wouldn’t have existed without good ol’ coding. From making a part change color to advanced frameworks, code helps us create things we otherwise couldn’t do. But with all that power comes a problem some developers face, coherency. Coherency is a term used to describe something that is intelligible, meaning something that people can easily understand. In some scripts I have seen by seen by Developers and or other scripters in other places, I’ve seen that they were unintelligible, making it hard to figure out issues with the script when it had to be debugged.

This guide/tutorial will serve as a way to teach developers suffering with issues due to incoherency in their scripts, teaching them ways on how to improve legibility and help out other developers when they have to look at their scripts so they can easily understand everything that the script is meant to do

With the introduction out of the way, let’s begin with the guide

Proper naming of Variables and Functions

Note: A lot of this can be boiled down to personal preference. If you already have code that can be understood without following the naming conventions in this section, you can skip over it

It’s begin with a simple thing that some developers make when they script, when naming a variable or a function, it’s best to make the name of it match the use case that it will use for. Proper naming can also help get rid of certain comments that explain what it does when proper naming can act as a comment itself. This same principle can also work for naming Instances as well

Take this example

local x = {"Apples","Pears","Bananas"}

Here we see an array of fruit, we understand it through reading the contents of the variable, but not from the name of variable itself. The issue comes when you have to use that table and see x[1] in your code for example, what does the variable x contain? To remedy this, as a rule of thumb, the names should be what the variable contains or its use case, the names should be self-documenting

local fruits = {"Apples","Pears","Bananas"}

Tip: If you have 2 or more words in your variable, camelCase can help to make those words be understood easily. canthroworjump is not as legible as canThrowOrJump.

With this simple change, we can now easily know what the variable contains inside it as it is named accordingly. There are other naming conventions you should try when naming variables, examples

In pairs loop

This is something I see many people do, even I myself is victim of this. The thing I’m referring to is when we use the standard i and v for in pairs loops, this can be confusing, especially if you have many in pairs loops or when you have one nested inside of another, this for example

for i,v in pairs(workspace:GetChildren()) do
	if v:IsA("BasePart") then
		v.Transparency = 1
	end
end

Sure this may not be difficult to understand, but what if you have this,

for i,v in pairs(workspace:GetChildren()) do
	if v:IsA("BasePart") then
		for i,vv in pairs(v:GetChildren()) do
			print(vv.Name)
		end
	end
end

This is even more confusing as we’re not properly naming our variables, meaning we are not sure as to what is going on in those two loops. But if we properly name them,

for _,part in pairs(workspace:GetChildren()) do
	if part:IsA("BasePart") then
		for _,partChild in pairs(part:GetChildren()) do
			print(partChild.Name)
		end
	end
end

We have already improve the code without even adding more code into our script. Proper naming is key.

Tip: If you have a variable that you get by force (such as the index in pairs loop), it’s good t o set that as an underscore _ to help show that it’s an unused variable, meaning you didn’t use it for what you needed

Functions

Typically when naming a function, its name should reflect on what it is used for, and should typically be constructed by a Verb followed by a Noun, such as addNums or setSpeed. Here’s an example

local function Calculate(x,y)
	return x + y
end

Technically we’re calculating 2 numbers, but it’s a bit misleading since its only function is to add 2 numbers, a more descriptive name would be

local function Add2Numbers(x,y)
	return x + y
end

Already we can understand what the function does just by the name alone.

There are many more ways to use naming conventions but I talked about it longer it would make this post longer than it should be, I recommend reading The Art Of Naming Variables as it describes naming conventions not touched upon in this post

Commenting

Sometimes there can be cases where proper naming won’t or barely helps readers understand the code, this is where comments come in. Comments can be made by typing -- followed by words that describe what you need to describe. Here’s an example

local fruits = {"Apples","Pears","Bananas"}

print(fruits[math.random(#fruits)])

For an intermediate to experienced developer, this code is simple, print a random value from the fruits table, but what if someone doesn’t know that’s what it does? Sure they can just test and see that it prints a random value, but to help our reader even more, a comment should be placed

local fruits = {"Apples","Pears","Bananas"}

--Get a random value/fruit from the fruits table and print it
print(fruits[math.random(#fruits)])

With that simple print, first time readers will be able to understand that line of code if they have never seen it before. It’s a bit of a silly example as most people already know how getting a random value from a table works, but it’s to show the power of commenting. I could mention more examples, but from one example you probably already know what I mean by commenting your code, just remember not to write comments on obvious code that you can understand by reading it, even if you’re a new scripter

Guard Clauses

Now some of you may be in this section and be confused as to what a guard clause is, I don’t blame you, first time I saw it used I had to google it to know what it meant. A guard clause is a condition that must be true before continuing on with the next lines. Now you may ask “Isn’t that an if statement?”, guard clauses do use if statements, but the way they’re structured is different and should generally be used when if statements can harm readability. Here’s an example of code without guard clauses

Part.Touched:Connect(function(hit)
	local hum = hit.Parent:FindFirstChild("Humanoid")
	if hum then
		if not deb then
			if hit.Name ~= "Left Leg" then
				deb = true
				print("Conditions met!")
				wait(1)
				deb = false
			end
		end
	end
end)

This code will work as it should, but its readability is a bit flawed since there’s 3 if statements and a variable that doesn’t nothing but check a condition. Here is the same code with guard clauses

Part.Touched:Connect(function(hit)
	local hum = hit.Parent:FindFirstChild("Humanoid")
	if not hum and hit.Name == "Left Leg" and deb then return end
	
	deb = true
	print("Conditions met!")
	wait(1)
	deb = false
end)

With guard clauses, the code is much shorter and easy to understand what is going on because each statement is now condensed into a single if statement that has more readability, showing easily what stops the function from continuing rather than the if statements as shown before

The same principle can apply in loops as well, but instead of return, you write continue, example

Before guard clause

for _,part in pairs(workspace:GetChildren()) do
	if part:IsA("BasePart") then
		print(part.Name .. " is a basepart!")
	end
end

After guard clause

for _,part in pairs(workspace:GetChildren()) do
	if not part:IsA("BasePart") then continue end
	print(part.Name .. " is a basepart!")
end

You can also use this to break a loop if needed, but the most common are either going to be return clauses and continue clauses

Indentation

This one is going to be a short section since Roblox automatically indents code when needed, but sometimes mess ups can occur and accidentally mess up the indentation. Indentation is when a line is tabbed in a certain amount of times

			This line is indented by 3 tabs!

“But Embat, why are you making this section if Roblox does it for us!” - I’m making this section to highlight the importance indentation has if you accidentally have some lines not indented properly

Here’s a huge example of that

part.BrickColor = BrickColor.Random()
if part.BrickColor = BrickColor.Red() then
print("Red!")
part.Transparency = 0.5
end
part.CanCollide = not part.CanCollide
part.Anchored = not part.Anchored
print("Done!")

As you can see, without indentation, it can be quite a hassle to figure out what is happening, what lines are in the if statement for example. Sure it’s not difficult to notice, but it takes more time to find it without indentation. With indentation on the other hand

part.BrickColor = BrickColor.Random()
if part.BrickColor = BrickColor.Red() then
	print("Red!")
	part.Transparency = 0.5
end
part.CanCollide = not part.CanCollide
part.Anchored = not part.Anchored
print("Done!")

It becomes much clearer to us that those two lines are in the if statement, because they stand out more than without.

Or alternatively to fix indentation quickly, just press Format Document
image

Conclusion

There definititely more ways to improve code coherency, such as the D.R.Y (Don’t Repeat Yourself) and K.Y.S.S (Keep Your Statements Simple) principles, but they should be easy to understand just by their name alone, but if you’re still confused, you can read the articles I posted below or you could do your own research to figure out these principles

Here are some articles related to the topic of coherency of code, I recommend you also read these as they feature principles that I did not touch over in my post. Be warned that some points in some articles may not work for Roblox

Five Rules to Improve Code Readability by RayRay, features the DRY principle
Three ways to make your code more readable, I used this for some of the points

If there’s something you would like point out or add, please send a reply on this topic!

38 Likes

I think you did, not sure where it would go.

1 Like

Nice post! Might be worth it to mention this button in the indentation section.
image

Also—I think it would be helpful to elaborate on the commonplace use of underscores in pairs loops. It would be beneficial to new scripters.

2 Likes

Hello! Thank you for your feedback, I’ll definitely include that and the use of underscores!

Edit: They are now included!

2 Likes

I was honestly just unsure if this should be in #resources:community-resources or here, but it felt more like a tutorial so I just chose here

What if I told you I don’t use for in pair loops I use this

local table = {}
for i=1, #table do
  if table[i]:IsA("Basepart") then
    table[i]:Destroy()
  end
end

Now I do use some of the enumeration methods, like checking if a table is empty with the next() method. But in general I don’t really use their form of enumeration.

1 Like

I mean if it works either ways, there’s no real problem with using that instead if it can be understandable, which what matters in the end, coherency

I only name my pcalls weirdly, usually becomes criticism, but for me it makes sense:

local s, x = pcall(function()
    --// cool stuff here
end)

For me personally, “s” means wucess, and “X” as an error, when I’m returning something from a pcall, then I don’t use “x”.

S also is a short for “sim” which is yes in portuguese, and also “sucesso” which is exactly what you think.

I should stop using these though.

2 Likes

Honestly yea if I were you, I’d try to change the naming convention you have for pcalls as it could confuse people reading your scripts or you if you ever have trouble what s or x are supposed to do, since you don’t have the name to go by

1 Like

I enjoyed reading this Article Especially the section About
Guard Clauses

Great Read

2 Likes

This was a really useful post, I need to incorporate some of these techniques into my work. This is especially helpful for those learning to script.

In the commenting section, near the end of the 2nd paragraph, you misspelt “even” you wrote “evne” :grinning:

1 Like

I think using Ipairs is more optimised now, with the new changes. I believe this script would work.

local table = {}
for Index, Value in ipairs(table) do
  if not Value:IsA("Basepart") then continue end
  Value:Destroy()
end

It’s not that I think its unoptimized (in fact they function the exact same just with a bit more abstraction) its that its easier for me to understand since I code in c more often than lua

Yeah, If its easier for you stick to the way you want to. :smiley: I was just sharing the information incase someone isn’t aware of this change.

Thank you for your feedback! I’m glad you enjoyed the post!

Thanks for noting the typo, should be gone by the time you read this

1 Like

I’m glad you enjoyed reading it! I may make more of these guides in the future so stay tuned for that!

I think you forgot to put curly-parenthesis instead of square ones for the first two code blocks. Nice tutorial by the way.

1 Like

Thank you for your feedback! I have fixed the issue with the curly brackets so now the code blocks should be good now! I’m glad you enjoy the guide!

Interesting section on the guard clause, tho there still are 3 if statements, imo I think utilizing “and” could be useful to condense this a bit

local hum = hit.Parent:FindFirstChild("Humanoid");
if (not hum) and hit.Name == "Left Leg" and deb then return end; 

You talked about the continue clause, but you could also talk about the “break” clause as well in the guard clause section.


That’s all, pretty good guide :smiley:

2 Likes

Thank you for your criticism

I originally did it like that cause I thought it would be too long of a line at first, but in all honestly I just forgot that I could just do what the former example did and put it in a variable to make it shorter, I’ll definitely change that part to reflect a better example!

I held off from mentioning that since the most commonly used clauses are return and continue, I never really saw break being used like that. But, if needed, I could make a section on that as well

I’m glad you enjoyed the guide!

Edit: They’re now included!

2 Likes