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.