Wednesday, February 25, 2009

Jumping into unfamiliar waters - approaching a new code base (step 1)

Jumping into a new codebase is always a bit of a challenge, even when you understand the tech. Starting at a new company complicates matters. Still there are a lot of very good strategies for dealing with this and many of these are good for the code besides. Just jumping into code usually omits important subtleties of the code and you can almost never work effectively or properly if you don't have a clear understanding of how the code was designed and organized. Even poorly written code has a design and should be 'rescued' if you can.

Unfamiliar code can be approached in several ways. I use a hybrid model of refactoring, commenting, and unit testing. One other thing that can be helpful is to remove dead code, large swaths of commented-out code, and general reorganization. Lastly, drawing diagrams to help understand hierarchies, patterns, and design is a way to understand the code organization. Let's look at the first of these.

Basic function... what does it do?

float Dist (Vector v1, Vector v2, Vector v3)
{
Vector l = v2 - v1,
e1 = v3 - v1,
e2 = v3 - v2;

float t = e1.Dot (l);
float l_2 = l.MagSq ();

return e1.MagSq () - t*t / l_2;
}


Refactoring function names and parameters
This simple act can take terrible code and make it usable. Rename function names so that they're readable and that the parameters each have a useful name. Look at how much of a difference this can make.


float ShortestDistPointToLine (Vector v1, Vector v2, Vector pt)
{
Vector l = v2 - v1,
e1 = pt - v1,
e2 = pt - v2;

float t = e1.Dot (l);
float l_2 = l.MagSq ();

return e1.MagSq () - t*t / l_2;
}



Refactoring function internals
This strikes at the heart of refactoring and can be quite time-consuming.


float GetSquareDistanceFromPointToLine (Vector v1, Vector v2, Vector pt)// shortest
{
Vector line = v2 - v1,
edge1 = pt - v1,
edge2 = pt - v2;

float SquareMagnitudeOfProjection = edge1.Dot (line);

float SquareMagnitudeOfLine = line.SquareMagnitude ();
return edge1.SquareMagnitude() - SquareMagnitudeOfProjection * SquareMagnitudeOfProjection / SquareMagnitudeOfLine;
}


Refactoring for optimization
This is a complex process, but this almost the final step of refactoring. By attempting to optimize the code, you really force yourself to understand what the code is doing and possibly find ways to making it function better, faster, or even more naturally. In this case, I have added two "early fails" allowing the code to exit early if we don't need to perform any further calculation.


float GetSquareDistanceFromPointToLine (Vector v1, Vector v2, Vector pt)// shortest
{
Vector line = v2 - v1, edge1 = pt - v1, edge2 = pt - v2;

float SquareMagnitudeOfProjection = edge1.Dot (line);
if (SquareMagnitudeOfProjection <= 0.0f) return edge1.SquareMagnitude ();
float SquareMagnitudeOfLine = line.SquareMagnitude ();
if (SquareMagnitudeOfProjection >= SquareMagnitudeOfLine) return edge2.SquareMagnitude();

return edge1.SquareMagnitude() - SquareMagnitudeOfProjection * SquareMagnitudeOfProjection / SquareMagnitudeOfLine;
}


Refactoring with comments
This step is crucial even though comment should be kept to a minimum. Focus on readable code and you can make the code self-documenting. Still, a few extra comments in a tricky section of code helps tremendously.

// this is highly optimal and very fast.
float GetSquareDistanceFromPointToLine (Vector v1, Vector v2, Vector pt)// shortest
{
Vector line = v2 - v1, edge1 = pt - v1, edge2 = pt - v2;

float SquareMagnitudeOfProjection = edge1.Dot (line);
// the projection may put out point beyond the ends of the line.
if (SquareMagnitudeOfProjection <= 0.0f) return edge1.SquareMagnitude (); // distance to the point
float SquareMagnitudeOfLine = line.SquareMagnitude (); // try the other edge, note how we use the already computed length
if (SquareMagnitudeOfProjection >= SquareMagnitudeOfLine) return edge2.SquareMagnitude(); // distance to the point

// project perp vector onto the 'height' of the point
return edge1.SquareMagnitude() - SquareMagnitudeOfProjection*SquareMagnitudeOfProjection / SquareMagnitudeOfLine;
}

No comments: