The Art of Coding


When discussing game development, especially in the WTCC program, there tends to be this idea of "artists vs programmers." This is a false dichotomy for two reasons: the first is that many more disciplines are necessary for a successful development cycle for a game. Game designers are required to help guide the ideas and balancing of mechanics. Producers are essential to help facilitate many different administrative and documentarian tasks. Level designers fall somewhere between the two. The list goes on to the number of positions that extend beyond this default umbrella.

The second and more important reason this is a false dichotomy is that it ignores the fact that coding is an art. Sure, it might not be in line with the idea of traditional art, and the visual beauty is rarely front-facing, but there is an art to how to write code. Identical to how you can tell different artists apart by their brush strokes, you can decipher different programmers by their coding styles.

Once you have read enough code, it can become obvious when one coder writes a line of code and when another coder has come back through and edited that code. There any many factors to how one writes code that can tip you off to the authorship, but today I want to discuss what practices are necessary to write comment-free code that will simultaneously increase the readability and efficiency of your code, ironically, by eliminating comments.

Issues with Comments

To steal the phrasing from a fantastic YouTube channel, CodeAesthetic, "Comments, like code, can develop errors." This means that, as your codebase develops and evolves, your comments can become outdated and incorrect, especially if you don't update them as you update your code (which, let's be honest, never happens). They can increase the length, be misleading, and, most of the time, are either unnecessary or signs of poorly maintained code. That being said, there are two major exceptions.

Complicated Equations

Sometimes there is a complicated equation or a really convoluted piece of logic that is required in order to achieve something. In the cases where simplicity is impossible, it can be worth it to write the equation in not code forms, link to a graph with the equation, or leave the logic that is written in the code in plain English to help parse why the complexity exists.

//TODO

In my opinion, this is the best use of comments. When developing code for games, it is common to have polish items you want to implement but don't have assets for yet. For example, if I want to play a particle and sound effect when I hit a certain point of code but have not been provided the assets yet, I might write "//TODO: Add sound and particles" in my code. This is useful to remember what you want to do when you eventually get assets, earmarks the location, and allows you to reaccess it quickly. Most major IDEs have a task list where you can view all of your TODO comments, and it is a valuable resource to utilize that does not incentivize dirty code like normal comments.

Commenting out code

You, of course, can use comments to comment out a line of code while testing and while pseudo-coding. I'm focused more on a production level and feature completion code. Sure, nothing is ever complete, but by the time the task is considered done enough and you're moving on, that is the time frame I'm focusing on. Using temporary comments in any other way as part of the code-writing process is fine; I am focused on the documentation process.

How to Write Comment-Free Code

In order to write comment-free code, you need to follow certain principles so that it is readable and can be understood without comments. It might feel weird at first, but it is going to make the life of you and everyone else who views your code so much better.

Naming Conventions

Naming conventions are crucial to writing comment-free code. Your variables should be accurately and clearly named. With today's modern IDEs, there is no reason we can't have longer named variables, especially if increases readability. Don't use pos; use position. It will not increase keystrokes thanks to the autofill of IDEs. Even Notepad++ has this ability, so there is no excuse for laziness when naming variables.

The same thing goes for classes and functions. To pull examples from our game, we have a class/script titled CameraAnimationWithMessageTriggers.cs. Without reading the code, you know what this script does. It animates the camera and provides a message trigger. Within this class is a function named ChangeCameraModeToPlayer. Again, the point of this function is evident without seeing any of the code of the function. Using clear names is the most important step and should be applied to all the other concepts discussed here.

Single-Action Functions

Along with the idea of clear names for functions is that functions should have a very simple objective. If a function does more than one thing, it should likely be split into two separate functions. As an example, let's take the player controller we are using. Update calls the function HandleMovement, which handles the movement input of the player. HandleMovement then calls the Move function (which only gets called by HandleMovement), which actually moves the player. HandleMovement then also calls the DoViewBob function, which handles view bobbing. All of these are part of the movement, but since view bobbing, moving the player, and handling the player's movement input are all separate, they should get separate functions. Suppose your function is getting long enough to the point you need to separate it into sections or explain what one section does because it's separate from another. In that case, chances are you can extract a new function from it and increase readability (while decreasing comments).

Avoid Magic Numbers and Increase Constants

Magic numbers are a simple concept. Simply put, that is when a number that is not a variable appears somewhere in your code. This can sometimes occur with strings as well. The problem with magic numbers is that without either a comment or a put of code interpretation, there doesn't seem to be any reason why THAT number was chosen. If you have a number that you know is not going to change, so it isn't worth a variable, declare a constant.

For example, if I have a transition that I want to last 1 second every time, no matter what, I might have something like "WaitForSeconds(1);" written in the code. Okay, cool, so I am waiting for 1 second, but why? If we declare a constant FADE_TIME = 1, then we would have the line "WaitForSeconds(FADE_TIME);" This code is much clearer on what it is achieving, and if I decide that 1 second is too short for the transition, I'm able to easily find where it's declared (in the constant) and change it without digging through my code.

We can apply this concept to expressions too. If I'm going to make the same check over and over again, for example, "distance < AGRO_RANGE," I can assign that to a bool property such as "bool IsInRange => distance < AGRO_RANGE;" and then call that property whenever I need to make that check as opposed to the repeated and less clear expression. Again, it increased readability and decreased commenting.

Invert If Statements

My final suggestion for increasing code readability is to invert if statements. Let's say you have a part of your code where you need to validate that input is positive and handle an error if it's negative. Initially, you might be inclined to write a line of code like this:


if(input > 0)
{
    //Do thing
}
else
{
    //Handle error
}

However, that increases the amount of nesting, which hurts readability. It can also become confusing if the thing you're doing takes a lot of lines of code, needs to have more loops nested inside, and generally sets up a lot of potential issues.

The idea behind inverting ifs is that instead of checking for the good condition first and then handling the errors, you check for errors first and then handle the good condition last in order to reduce nesting. The way this could look in the code before would be this:


if(input <= 0)
{
    //Handle error
    return;
}
//Do thing

Doing it like this allows us to see what error condition is being caught, and we can skip past that and all other errors to find what the function does. Or, we can look through and see what edge cases are and are not accounted for right at the top of the function, splitting it nicely into sections instead of digging through an insane amount of nested if statements just to make sure we have the right condition. The more nesting you have, the more convoluted your code and the more likely you are to want or need comments to understand code.

Final Thoughts

While I'm not against ever commenting, well-written, clean, well-maintained code does not necessitate comments. What is essential, even if following these practices, is code documentation. Using XML comments, or an addition, such as Doxygen, to document what a class does, what each function does (in addition to having a clear name and single-action functions), what is returned, the parameters, etc., is still highly important. While it is possible for this to go out-of-date like comments, it is less likely to fall to this issue if the above principles get followed.

It is also easier to keep documentation up to date, as they are always right at the top of the class/functions as opposed to scattered throughout the code. The other advantage of user documentation is that if you build into an assembly, or even if you are just working with someone else, the documentation will automatically compile to help others utilize the assembly and can be stored in a database that can be searched through and looked up and integrates into other programs such as hovering over functions in an IDE, showing up in the tooltips, and (depending on build settings) help black-box debuggers figure out where an issue might exist.

Whether you intend to reduce comments in your own code or not, I hope that you will keep the above principles in mind when writing code in the future. Everyone has their own balance of code to comments, different ways they like to implement certain principles and other small ways that contribute to their code styles and make coding their own unique art. There is nothing prettier to me than well-documented, well-maintained, clean, comment-less code that I can read and understand without needing to try to piece it together. This is the type of code I strive to make, and I love to be a part of my team, and I'm glad to say that this is the code standard that Snapback has been developed with.

Get Snapback

Leave a comment

Log in with itch.io to leave a comment.