One of the biggest barriers to me is getting over my need to make code that I think is clean. And every time I try to read about best practices in game code I come across the same mentality: Who cares, write code quick and dirty, as long as it works it's fine.
And that's fine, it's just intimidating for me to write code like that. Too much perfectionism or something. Maybe too much "What if I do it wrong and introduce a bug so entangled in everything that I can't unravel it".
I wish there were even some bare minimum best practices suggestions for game code.
If Google cared at all about the targets that game developers cared about, my Gmail wouldn't take 10 seconds to load and run at 2FPS once the page is there.
I've read some of the best code I've ever seen, written by programmers in the games space. Code with very few tests, heavy intertwined behavior, wide-reaching global effects, and lots and lots of state.
Ultimately, treat the code as an artifact of the project: make it as clean as you need to get the game done, but any extra scaffolding you add will only come back to bite you later once you want to change how something is wired. From that end, all the usual suspects apply: make code easy to delete, make it have clearly-defined boundaries, and depend on other code as little as possible. Push yourself to use copy/paste more than you think you should, you'll be surprised how easy it is to delete wild experiments if you don't have to untangle the web of dependencies.
You're correct that optimized code is generally harder to maintain. Optimization often requires punching through abstraction layers or calcifying certain constraints or assumptions in the code.
FYI, about a dozen years ago I comfortably used GMail on a 450 MHz PowerPC with 512 MiB of RAM. The javascript ran without a JIT, completely interpreted. It all ran just fine. Now here I am, on a multi-GHz multi-core box with gigabytes of RAM and a JIT, and I have to keep GMail in HTML-only mode. I simply can't run the javascript and still operate my computer. Mail is piling up because I can no longer select lots of email for deletion; it isn't practical to click every single item.
This is definitely speculation on my part but maybe he didn't become a coder focused on his craft because of the finances afforded to him from Braid, maybe he created Braid a quality game because he was a coder focused on his craft?
I've never been in the gaming industry, but what I've heard from some developers is that writing very dirty throwaway code is the norm, since most of it is thrown away anyway when the game is done. Which leads to the buggy mess we get even from many triple A games, and a never ending cycle of re-writing the same things for each game, even within the same studio.
Simple, contrived example: if I create an open-world game with open-world design, I can complete quests in any order. Maybe I can start doing a quest, and then decide to stop doing it halfway through, or maybe I can switch quests halfway through.
The test case matrix for this is now: every quest x every other quest. If some quest spawns a timer that will do something in 3 minutes, and something else forgets to stop it when I switch quests, that's a bug. Maybe the timer spawns some NPC, and it should stop when I switch quests. Or maybe the timer will reset some world state, and should fire immediately once I switch quests. But maybe not always -- if I complete the quest but the game internally 'switches quests' to the next one in the cycle, I don't want to despawn the NPC.
Is this code? Not really engine code, it's more like data, or scripting? It's not really the code that's reusable between games, and between engines; it's custom-built for the game's flow itself. That's where, in my experience, a good majority of "AAA bugginess" happens.
Every engineer knows interacting mutable state tends to make code buggier. Well, a game simulating a virtual world is essentially a huge ball of deeply interacting mutable state by design.
The real hell comes when you think you're working on one and it's actually the other; you either burn time on an overcomplicated solution or invent a maintenance nightmare.
Just like any other development, you need to make guesses about what will change and what will stay the same and your efficiency depends on the accuracy of those guesses.