The main things that used to cause lag in my games were while true do loops and high mesh/voxel count.
You can easily fix the infinite loops by playing wait() in the middle: while wait() do
I also trimmed a bit of terrain off the bottom of the map and reduced the amount of meshes to fix the high voxel and mesh count.
This fixes a loop that almost everytime should be replaced by listening to events for example. Then the code runs only when it should be instead of checking. This drastically improves a performance. I say it because the next step of using while wait() do is the repeat wait() until that has no sense at all and it’s laggy. Why should you use repeat wait() until player.Character when you can use player.CharacterAdded:Wait() as a yield or player.CharacterAdded:Connect(function()...
Even if someone don’t cares about that still should place wait() inside a loop.
Putting a wait() insted of true is a bad habbit that is well described here.