I just read an answer to a question about structuring game code. It made me wonder about the ubiquitous GameManager
class, and how it often becomes an issue in a production environment. Let me describe this.
First, there's prototyping. Nobody cares about writing great code, we just try to get something running to see if the gameplay adds up.
Then there's a greenlight, and in an effort to clean things up, somebody writes a GameManager
. Probably to hold a bunch of GameStates
, maybe to store a few GameObjects
, nothing big, really. A cute, little, manager.
In the peaceful realm of pre-production, the game is shaping up nicely. Coders have proper nights of sleep and plenty of ideas to architecture the thing with Great Design Patterns.
Then production starts and soon, of course, there is crunch time. Balanced diet is long gone, the bug tracker is cracking with issues, people are stressed and the game has to be released yesterday.
At that point, usually, the GameManager
is a real big mess (to stay polite).
The reason for that is simple. After all, when writing a game, well... all the source code is actually here to manage the game. It's easy to just add this little extra feature or bugfix in the GameManager
, where everything else is already stored anyway. When time becomes an issue, no way to write a separate class, or to split this giant manager into sub-managers.
Of course this is a classical anti-pattern: the god object. It's a bad thing, a pain to merge, a pain to maintain, a pain to understand, a pain to transform.
What would you suggest to prevent this from happening?
EDIT
I know it's tempting to blame the naming. Of course creating a GameManager
class ain't such a great idea. But the same thing can happen to a clean GameApplication
or GameStateMachine
or GameSystem
that ends up being the duct-tape-favorite by the end of a project. No matter the naming, you have that class somewhere in your game code, it's just an embryo for now: you just don't know yet what monster it will become. So I'm not expecting "blame the naming" answers. I want a way to prevent this from happening, a coding architure and/or a process a team can follow knowing that this will happen at some point in production. It's just too bad to throw away say, one month of bugfixes and last-minute features, just because all of them are now in one huge unmaintainable manager.
Answer
Then there's a greenlight, and in an effort to clean things up, somebody writes a GameManager. Probably to hold a bunch of GameStates, maybe to store a few GameObjects, nothing big, really. A cute, little, manager.
You know, as I was reading this, I had little alarms going off in my head. An object with the name "GameManager" is never going to be cute, or little. And someone did this to clean up the code? What did it look like before? OK, jokes aside: a class's name should be a clear indication of what the class does, and this should be one thing (aka: single responsibility principle).
Also, you may still end up with an object like GameManager, but clearly, it exists at a very high level, and it should concern itself with high level tasks. Maintaining a catalogue of game objects? Perhaps. Facilitating communication between game objects? Sure. Calculating collisions between objects? No! This is also why the name Manager is frowned upon - it's too broad, and allows for much abuse under that banner.
A quick rule of thumb on class sizes: if you are running into several hundred lines of code per class, something is starting to go wrong. Without being overzealous, anything over, say, 300 LOC is a code smell to me, and if you are going over 1000, warning bells should be going off. By believing that somehow that 1000 lines of code is simpler to understand than 4 well structured classes of 250 each, you are deluding yourself.
When time becomes an issue, no way to write a separate class, or to split this giant manager into sub-managers.
I think this is the case only because the problem is allowed to propagate to the point where everything is a complete mess. The practice of refactoring is really what you are looking for - you need to continuously improve the design of the code in tiny increments.
What would you suggest to prevent this from happening?
The problem isn't a technological one, so you shouldn't look for technological fixes for it. The problem is this: there is a tendency in your team to create monolithic pieces of code, and the belief that it's somehow beneficial in the medium / long term to work like this. It also seems that the team is lacking a strong architectural lead, who would steer the architecture of the game (or at least, this person is too busy to perform this task). Basically, the only way out is to have team members recognise that this thinking is wrong. It does nobody favours. The quality of the product will worsen, and the team will only spend even more nights fixing things.
The good news is that the immediate, tangible benefits of writing clean code are so great, that almost all developers realise their benefits very quickly. Convince the team to work this way for a while, the results will do the rest.
The difficult part is that developing a feel for what constitutes bad code (and a talent for quickly coming up with a better design) is one of the more difficult skills to learn in development. My suggestion hinges around the hope that you have someone senior enough in the team who can do this - it is much easier to convince people that way.
Edit - A bit more info:
In general, I don't think your problem is limited to game development. At its core, it's a software engineering problem, hence my comments in that direction. What may be different is the nature of the game development industry, whether its more results and deadline oriented than other types of development, I am not sure.
Specifically for game development though, the accepted answer to this question on StackOverflow regarding "especially game architecture" tips, says:
Follow the Solid principles of object oriented design....
This is essentially exactly what I am saying. When I'm under pressure, I also find myself writing large pieces of code, but I've drilled it into my head that that is technical debt. What tends to work well for me, is to spend the first half (or three-quarters) of the day producing a large amount of medium-quality code, and then to take a sit back, and think about it for a while; do a bit of design in my head, or on paper / whiteboard, about how to improve the code a bit. Often, I notice repetitive code, and am able to actually reduce the total lines of code by breaking things up, all the while improving readability. This time invested pays for itself so quickly, that calling it an "investment" sounds silly - quite often I'll pick up bugs that might have wasted half my day (a week later), had I allowed it to go on. What I am saying is very simply:
- Fix things on the same day you code them.
- You will be glad you did within hours.
Coming to actually believe the above is difficult; I've managed to do it for my own work only because I've experienced, over and over again, the effects. Even so, it's still difficult for me to justify fixing up code when I could be churning out more... So I definitely understand where you're coming from. Unfortunately, this advice is perhaps too generic, and not easy to do. I strongly believe in it, however! :)
To answer your specific example:
Uncle Bob's Clean Code does an amazing job of summarising what good quality code is like. I happen to agree with almost all of its contents. So, when I think of your example of a 30 000 LOC manager class, I can't really agree with the "good reason" part. I don't want to sound offensive, but thinking that way will lead to the problem. There is no good reason for having that much code in a single file - it's almost 1000 pages of text! Any benefit of locality (execution speed, or design "simplicity") will immediately be nullified by the developers being completely bogged down trying to navigate that monstrosity, and that's even before we discuss merging, etc.
If you aren't convinced, my best suggestion would be to grab a copy of the above book, and have a look through it. Applying that type of thinking to leads to people voluntarily creating clean, self-explanatory code, which is nicely structured.
No comments:
Post a Comment