Some thoughts on commenting and documenting code. Yes comments are important, particularly for complex code, but I generally fall into the camp that code should be self documenting. I’ll explain what that means with an example.
I’ve been working on some physics stuff for a top seceret ROBLOX game idea I have (It’s a fully 3D team based remake of the old Joust game. I’m terrible at keeping secrets). In my research I found some useful JavaScript code that is almost incomprehensible. It looked, in part, like this:
function a6(a){
vv0 = fh.v0;
fh.vf = vv = Math.sqrt(vv0 * vv0 + 2 * a * fh.x);
fh.t = (vv - vv0) / a;
fh.va = .5 * vv0 + .5 * vv;
fh.vf = v
}
That nearly incomprehensible chunk of code works, and it sets the acceleration variable on an object and adjusts other values to reflect the change so that you can tell, for example, how long the object will move and how fast it’s going to be going over a set amount of distance. At first you’d think this code needs a ton of comments, and that would certainly help. But here’s my first pass at converting this mess to Lua:
function PHYTool:calculateNewAcceleration(newAcceleration)
local initialVelocity= self.initialVelocity
local finalVelocity = math.sqrt(initialVelocity* initialVelocity+ 2 * newAcceleration * self.distance)
self.finalVelocity = finalVelocity
self.time = (finalVelocity - initialVelocity) / newAcceleration
self.averageVelocity = .5 * initialVelocity+ .5 * finalVelocity
self.finalVelocity = finalVelocity
end
As you can see just by naming the variables sensibly and not even modifying the logic the function is much more understandable. This is the version that I used while verifying that the code was working and through testing of my object. Once I was satisfied that it was working, I rewrote it. Here’s the same chunk of code functionality (the ‘set’ functions live further up in the file so they can be used by other functions):
function PHYTool:setTime(acceleration, finalVelocity, initialVelocity)
self.time = (finalVelocity - initialVelocity) / acceleration
end
function PHYTool:setAverageVelocity(initalVelocity, finalVelocity)
self.averageVelocity = .5 * initalVelocity + .5 * finalVelocity
end
function PHYTool:setFinalVelocity(acceleration, initalVelocity, distance)
self.finalVelocity = math.sqrt(initalVelocity * initalVelocity + 2 * acceleration * distance)
end
-- Sets a new constant acceleration and recalculates a new finalVelocity, time, and averageVelocity accordingly
-- sets:
-- acceleration
-- modifies:
-- time
-- finalVelocity
-- averageVelocity
function PHYTool:calculateNewAcceleration(acceleration)
self:setFinalVelocity(acceleration, self.initialVelocity, self.distance)
self:setTime(acceleration, self.finalVelocity, self.initialVelocity)
self:setAverageVelocity(self.initialVelocity, self.finalVelocity)
self.acceleration = acceleration
end
Much better. The ‘set’ functions don’t really need comments because what they do is self evident given their names and parameters. The code in the ‘calculate’ function is also pretty self explanatory. The ‘calculate’ function itself needs documentation because it has side-effects, that is it modifies variables outside of it’s own scope which isn’t obvious by the functions name. By the rules of self documenting code the function would need to be called something like ‘calculateAccelerationAndUpdateTimeFinalVelocityAverageVelocity’ but that was just too wordy for me.
BUT, if I did use that long name then the code calling it could tell exactly what was going on. Compare what I have now:
local tool = libs.PHYTool.new(targetHeight - pos, velocity, acceleration)
[...]
tool:calculateNewAcceleration(newAcceleration)
if (tool.time < 0) then
-- do stuff
end
with
local tool = libs.PHYTool.new(targetHeight - pos, velocity, acceleration)
[...]
tool:calculateAccelerationAndUpdateTimeFinalVelocityAverageVelocity(newAcceleration)
if (tool.time < 0) then
-- do stuff
end
In the first example it isn’t obvious that tool.time is updated by calculateNewAcceleration(), but in the second one you know exactly whats going on. The important take-away being that information given in comments isn’t readily available in the code that’s using your functions. I should probably rename those functions to the longer form after all. (but I wont remove the comments even if I do, because of those side effects which need to be called out).
This doesn’t mean to not comment code. Complex code, code with side effects, and code that is doing things in non obvious ways should always be commented, if not for the benefit of other people then for your own good when you come back months (or even days) later and forget what you did and why.