Finishing Your Game: Neat vs. Messy Code


Finishing Your Game: Neat vs. Messy Code

Gabe "lazyeye" Weiner is a Programmer / Producer who is active in the GameMaker community, developing a variety of content from tools, jam games, podcasts, and more. He works on the recent hit game Forager and more games that he can't talk about quite yet. To stay up to date with his work you can follow him on his Twitter.

The creation of video games is an act of juggling finite resources: skill, knowledge, connections, money, and, most importantly, time, from which all other resources are affected. Time grows your work, giving you space to practice your skills, increase your knowledge, and build your network. However, it also erodes as your burn rate (the amount of cash you need in order to eat, pay employees, and pay yourself) continues to sap.

Time management is a skill so desperately sought in the professional world, yet notably lacking in the indie scene. How many of us can relate to the idea of having countless unfinished projects? To developing that one game that we've been working on for months, perhaps years, with no end in sight? To closing the IDE of our project forever, hopelessly frustrated with bugs, telling ourselves, "the next project will be better"?

While these experiences are something we can bond over as game developers, we should not accept them as immutable flaws in ourselves. We can strive to do better.

DEBTS AND INVESTMENTS

Before we go any further, we must understand the concept of "debts" and “investments” (no, not the financial kind!). All actions we take in the production of our game have a price: the tangible, upfront cost of performing the action in the present (investment), and the more subtle, long-lasting requirement of maintaining that action in the future (debt). We can determine the value of the investment by weighing the benefits it could give immediately as well as the debt that such a feature could cost in the future. An example:

1. Supporting localization for five extra languages from the beginning of the development

a. This is a small investment for us personally. Since we are making this decision at the start of development, the only noticeable time investment upfront is waiting for whoever translates our text to get their task accomplished.

b. This creates a large debt for us. Now for every single update we release to the game moving forward we can't just simply write in new strings -- we have to wait for our localization team to translate them as well, which can add weeks onto our scope.

This is a trap many developers fall into right from the start. There is a whole slew of features that may feel within the grasp of the team to add, but they do not see the long term debt they are creating for themselves. Developers also have to consider the financial benefit of all these actions: yes, localization adds a maintenance debt, but do the sales to new users offset that cost? (Spoiler: it often doesn’t if you’re going to be releasing updates often).

We must additionally realize there are two sides to this coin. Investments can be an extremely beneficial choice for us as well by having the time we save in the long run to be greater than the work we do at the start. An example of this:

1. Creating a UI framework tailored to your project to make menu implementation much quicker

a. This is a large investment. It takes a lot of time to create a framework that can have all the flexibility and power we need to create menus in our games.

b. This has a low maintenance debt and saves lots of time in the long run. The maintenance required will be in fixing bugs in our system and adding new features, but creating all the different menus in our game will be expedited. Thus, the time this framework saves will pay off our investment, and eventually be a net-gain in time saved.

Finish Finish
An example of messy (or spaghetti) code vs. neat (or modular) code

FINDING THE BALANCE

There is an ongoing war between game developers (though not usually professionals) about whether or not it is better to write highly modular (clean and reusable) code, or spaghetti code (a hacky mess that focuses on getting the job done). Many who have spent a bit of time learning how to program have discovered the joy of wonderfully modular code and look down on those who write their code otherwise. On the flip side, there are those who argue that it doesn't matter what your code looks like under the hood -- if the game works, the game works. There is no correct answer. The closest thing to a right answer is a balance, and even then, it's impossible to find that perfect balance every time.

There are countless determinants to finding this balance, all dependent upon carefully managing your investments. Professionals with deadlines to hit probably need more spaghetti. Hobbyists trying to learn almost exclusively need modularity. But also consider team size, budget, scope, experience, the importance of the task to the core design, how many users will use it, how much time you'll have for QA, or even where in your codebase it is!

IN DEFENSE OF MODULARITY

When you write spaghetti code, you're lowering your upfront cost and raising your maintenance cost. While it may be the easy option to just bust out some functioning code, spaghetti code exponentially increases the time investment debugging requires. Bugs and crashes you'll encounter in QA will be esoteric and difficult to identify.

The most common issue a lack of modularity creates is having commonly used bits of code copied and pasted around the codebase (rule of thumb: if you're reusing any code, make it a script). On Forager, we ran into an issue like this recently with the code that displayed a fire particle effect over an instance that was copied and pasted into the various objects that needed the effect. When one of our programmers discovered a bug with this effect, they went into the object that was causing the bug and fixed the fire particle code there. However, they did not realize that there were other places this code existed, and so the bug remained unsolved on all those other objects. Had we put all that code into a script from the start, we would have only ever needed to touch that one script, and our code would have been more modular.

Modularity saves us many headaches, allowing us to create more expandable games where we can add content faster and solve bugs more efficiently. However, modularity does not always save time, as, like all other actions in game development, it has a debt -- a debt which may never be paid off.

IN DEFENSE OF SPAGHETTI

As I've mentioned before, time is the most valuable resource to a game developer. As a professional, any opportunity you have to save time is a treasure. The hard truth is that, more often than not, spaghetti code is smarter for indie professionals than modular code, especially in GameMaker. GameMaker is built to let you do things quickly. Don't always fight that -- embrace its nature! GameMaker's unique advantage to professionals is how quickly and easily you can achieve great things.

A methodology I personally like to adopt when possible is to "keep your systems clean and your implementations dirty." For example, Forager has a UI system that is immense, well documented, and extremely modular. Forager's actual menus, which use this system, are code dumpsters. The decision to take on the debt of this system was made specifically because of the time it would save us. Not only does it make menu creation take roughly 80% less time than previously, but its stability means that it will seldom ever break down. The implementations, on the other hand, are made and continuously edited when facing deadlines. If I write terrible code for the leaderboard menu, that's a manageable flaw. It will only ever affect that one menu, and I can trust the underlying system to still take care of itself. If issues arise, I know exactly where to look.

HOW TO FIND BALANCE

There are no clear cut answers for when to write modular code and when to write spaghetti code. Knowing when to do each is a skill you have to develop yourself. But as I mentioned at the start of this article, it's a skill that's severely lacking in indie developers -- why is that?

The very core benefit of being an indie developer is often our curse as well: freedom. The freedom to set your own schedules, scope, and deadlines is an immense privilege and responsibility. Not needing to conform to bureaucratic guidelines and practices gives us the creative liberty to create the product we want to make.

But... you must also accept that these practices exist for a reason. While you can still hold your liberty close to stay true to your own visions, a complete lack of organization and structure will sink your project.

You must set deadlines for yourself. Deadlines you actually follow. At some point in your career, you won't have the option to delay a deadline, and you must prepare yourself for that situation. When a deadline is approaching, and you're not ready, don't default to pushing away the date, even if there are no tangible consequences for doing so. Cut features. Bring in new resources. Do what you have to do to reach that deadline (short of placing your own health and well-being at risk).

Not sure where to start? Do game jams. Do lots of game jams. The benefits of a game jam spread far and wide, but above all else, they give you the most accurate simulation of real project management you will ever find outside the real deal.

As a game developer, you are an accountant -- not one who manages money, but time. You will fail, you will over scope, you will delay -- but you can get better.



Written by Ross Manthorp

Ross Manthorp handles all things community at the GameMaker team. When he’s not pulling the strings from behind the scenes he’s enjoying Nintendo games, indie games, and getting emotional over cartoons and comics.