Writing tight OOP code in studios where iteration time is important and year on year you're rewriting large portions of code is common because of new platforms and new titles is a complete waste of time and it is important that you make simple architectural decisions that:
Except every time I hear the 'new code because of new platforms' arguement is I can't help but think it is garbage.
Consoles have been stable for many years now and it's only recently that a nod towards the new ones has happened. We've had ~7 years of stability in that domain.
Windows is an even bigger pile of fun as DX9 was stable for the better part of a decade and again it is only in the last few years that DX11 has become 'a thing'.
Other platforms come along at a much lower frequency and, with correct abstraction, are not a case of 'rewrite large portions of code' but rework layers which are needed to get them up and running.
I would argue that by NOT taking time to design things properly up front people are wasting more time with rewrites and rebuilds of systems which don't need it. The 'get it done' mindset has its uses but hacking on things is not, in itself, a good way to go unless you want to waste time over the course of years rebuilding the problem.
a) Allow new programmers to get on with things easily.
b) Make it easy to add features to.
Except none of this is an arguement against designing things properly nor, more importantly when it comes to this thread, is it an arguement in favour of singletons which, in my experiance, have only served to cause more harm than good.
A correctly designed system can allow for BOTH of those things IFF it is correctly designed.
And by that I don't mean the classic 'just out of university' OOP method of 'zomg! classes for everyone!' but a sensible division of code and responcibility. People get too hung up on 'OOP = class' and lose track of the real meaning of the design methodology. More to the point they focus purely on 'put everything in a class and give it methods' than correctly splitting things up into classes, structures and free functions as required and, more importantly, ignore data flows and how information needs to move around the system.
In short; most people in the world write utterly shit OOP but that doesn't mean it isn't useful to design larger subsystems for it.
It is argued that you can screw things up if you start touching global variables that you shouldn't. If you hire programmers with an ounce of common sense they generally won't do that and no matter what "barriers" you put in place to protect programmers from themselves, if a shortcut is really desired then it will be found. The friend keyword comes out in no time.
The problem is that most people AREN'T good enough to not make mistakes and do it wrong.
I have seen some wonderful things where people fresh out of university have commited crimes against code so bad it makes me wonder why they are allowed to even write code in the first place. (My favorite being the guy who decided to, in his branch without telling anyone, change the interface to our lua subsystem from function(luaObject, ....) syntax to luaObject->function(....) syntax while at the same time weaving his work around the changes. It took me 4 HOURS to get his code back into mainline for a merge and I no longer trust fresh faced programmers not to fuck up in some massive manner)
So, yes, people make mistakes and yes there are ways to hack around the 'protection' BUT with a properly designed system the immediate solution is often cleaner, easier to maintain and, more importantly, does NOT lead them down the path of thinking 'hey, others have done it so it's probably ok for me to do so...' because they learn by example.
Truth of the matter is that most people in the industry can't design for crap - unfortunately if we tried to staff an industry with people who were good at it we'd soon run out of people so in order to save time later its best to discourage bad code to start with.
And to that end... a story!
In our engine we were lacking a sane method of the game team to transfer values from the logic thread to the renderer thread (and thus the GPU).
We took a day to hash out WHAT we wanted the system to do (I'll spare you the details, because frankly they are a tad confusing at times), roughly how we wanted it to interact and then I got cracking on the design and implementation details.
The two senior members of the team feedback into the process every few days as we discussed things and by the end of a two month period we had a working solution (with some of the most complicated multi-pass binding/patch table setup code I've written in my life BUT...) which was created when the renderer started up and cleanly passed, by reference, to the subsystems which required it.
However we weren't finished there because the aim for this system was to replace ALL the variables which could be passed to shaders; this included built in tokens for things like shadow map textures as well as the post-processing system's frame buffer textures and variables.
The 'global texture cache' (as I believe it was called) had been written partly by a senior and finished off by a junior. Frankly the code was overly complex for what it did BUT more importantly it had been implemented as a global system and because of this it had inflitrated multiple levels of code with no clarity as to the flow. It took LONGER for me to remove the old code and clean up the mess it had made than it took me to write the addition to the Parameter System which had been written; Clean OO intergration 1 v Global Code 0.
(The new system also expanded the abilities of the GTC functionality bring it in line with the general parameters affording the game team more control).
Post Processing was the same story; the functionality added to the Parameter System took maybe 2 days to add with no major interface changes aside from some special casing due to the stupid way the Post Processing was implemented.
Finally a few months back we needed a feature adding to the Parameter System at the request of the game team. The conversation as to if I should even add the feature took LONGER than the time it took me to write the feature, integrate it, test it and push it into the publish queue, the system was that extendable.
The system itself was built on both OOD and DOD principles to use classes when it made sense and pay attention to the data when it didn't.
The system was written in Jan/Feb of this year with a couple of weeks in March to integrate the extra functionality and remove the old.
This system is a lynch pin in the game F1 All Stars which is coming out soon; if it breaks then we lose logic->renderer data flow so it's one of the most critical systems in the whole engine right now.
The system has had ZERO time spent on bug fixing; not a single bug has been filed against it and when suspicion has been cast it has been easy to prove (despite the mad binding code which while it looks complex does work flawlessly) it wasn't a problem.
Now, we could have hacked together a system in a month, thrown it in as a global and watched as everyone abused it and dealt with bugs left, right and centre BUT instead we decided to do it right and because of that the integration is clean, the code is robust and its freed me up to work on other bits of genius instead.