Today we are talking about cleaning up code. First I will attempt to define clean code, and then I will discuss practices that can help reduce the amount of clutter in your codebase.
Clean code cannot be defined by any scientific means, there are often many solutions on the matter. Making good code almost more of an art of expression. There are a few categories to keep in mind when attempting to define clean code however:
Naming Conventions
This is more than just sticking a capitalization scheme, that’s only part of it.
BAD:
function add_numbers(a, b)
return a + b
numA = 1
NumB = 3
GOOD (Snake Case Version):
function add_numbers(a, b)
return a + b
num_a = 1
num_b = 2
For larger codebases, it’s very important to work with the notion of namespaces when naming a function or object. An example of good naming is OpenGL’s deprecated library for transformations.
glVertex3f(float, float, float)
glVertex4f(float, float, float, float)
glVertex4d(double, double, double, double)
The developers of this API decided to embed the notion of their namespace in each function name by simply adding a “gl” prefix. To push it further, they also indicate the number of args and the datatype of those args in the suffix. With a graphics API, having this level of specificity is incredibly valuable, and tells the API consumer exactly what they are getting into with a function call.
Documenting Code
BAD:
// :)
float powerf(float x, float p)
{
float result = x;
for (int i = 0; i < p; ++i)
{
result *= result;
}
return result;
}
ALSO BAD:
// I implemented my own function to calculate
// the nth power of a number which I called "x".
// The function uses a for loop to multiply the number
// with itself until we reach the nth power which I
// called "p". This function uses floats for the
// arguments and the return value.
float powerf(float x, float p)
{
// result starts as "x"
float result = x;
// for loop to calculate nth power
for (int i = 0; i < p; ++i)
{
result *= result;
}
// return the result
return result;
}
GOOD:
// takes a number |x| and returns the |p|th power
float powerf(float x, float p)
{
float result = x;
for (int i = 0; i < p; ++i)
{
result *= result;
}
return result;
}
There’s a balancing act here. Good documentation of code is not no comments, but it’s also not a lot of commenting. The question to ask yourself is “what would I want to reference quickly if I was thinking about using this function?”. Also, if the function is too complicated to describe in 2-3 sentences, you probably have multiple functions on your hand, time to encapsulate!
Vertical Structure
This is a bit of a personal on for me, but being consistent with vertical structure and spacing is a big game changer. A lot of this just has to do with determining exactly how many newlines you have between certain types of code, whether it be functions, classes, variable declarations, or comments. Ultimately, as long as you’re consistent, and do not have a ton of blank space, you’re doing it well!
Encapsulation
We learn about encapsulation early on in our coding journeys, starting with functions, and usually ending with structures and classes. Good encapsulation can keep a larger codebase organized, which is absolute key. A less talked about form of encapsulation I have recently become privy to is the use of namespaces for specifying a collection of objects. Consider the following code which retrieves a nuclear reactor object.
NuclearReactor reactor = OSIRIS();
Okay not bad, if you know that OSIRIS is a type of nuclear reactor. There is one simple change that can make this a little bit cleaner, and call out the notion of a collection of reactors, namespaces!
NuclearReactor reactor = NuclearReactors::OSIRIS();
In conclusion, clean code involves a number of healthy practices, and is a very expressive thing. Often times, just thinking ahead on how you’d like to organize your code can get the ball rolling. But also, don’t be afraid to obsessively refactor (on your own time) to practice your code organization. Happy cleaning!