4 ms·
I think Carmack is basically using feature flags - but that's just the platonic ideal polished version of this, not the fundamental goal. The less AAA/polished
by MaulingMonkey 7y ago
I think Carmack is basically using feature flags - but that's just the platonic ideal polished version of this, not the fundamental goal.
The less AAA/polished version is to do this in code instead of in data. Maybe you comment out old.h in favor of new.h, or have a #if 0 ... #else ... #endif chunk in your .cpp files. This can be a lot quicker than adding conditional logic to hundreds of call sites, or implementing hundreds of forwarding stubs, if you don't have a nice dynamic module boundary already. You can make a define, two build variants, and side-by-side compare things that way too. Not quite as nice as runtime switching, but potentially a lot easier, if you've kept things from spilling into common headers too much and have reasonable link times.
Another alternative is to do this on your second computer, carefully synced to the same code/config/debug settings/feature flags/???, and hopefully with the same specs. This manages to work even if things spilled out into headers, but can require some painful coordination to keep in sync.
- dwohnitmok 7y agoI don't think so. I think he's suggesting something fundamentally different. Note that he specifically calls out keeping around a reference implementation indefinitely that he implies will never be called by a code path (but is continuously maintained) as a subcase. That's kind of hard to square with feature flags.
- MaulingMonkey 7y agoThere's nothing fundamentally temporary about feature flags, even if that's the popular use case. You can opt into using the reference implementation of a renderer with a flag. You can opt into using a debug implementation of an allocator with a flag. You can opt into your experimental hopefully faster renderer with a flag. There's very little that's fundamentally different in these cases - maybe some get removed from some builds at compile time - and maybe your plans for the future are a little different. But if plans change - perhaps maintenance of the reference implementation ends up being more costly and less beneficial than expected - you may very well end up deprecating, and then deleting, your reference implementation. To an outside observer not privy to your plans, this is identical to classic "I'm making my new work opt-in, then opt-out, then the only choice as development progresses" temporary feature flags. EDIT: Heck, https://martinfowler.com/articles/feature-toggles.html https://martinfowler.com/articles/feature-toggles.html even lists plenty of long-term and even static build-time configuration stuff under the category of feature toggles, so I really can't see what's hard to square, unless you're using a much narrower definition of "feature toggles" than is being used there.
- dwohnitmok 7y agoI think first off yes I do have a narrower definition of feature toggles; if you are using a configuration file to perform arbitrary swapping of code modules in and out I would argue you've turned feature toggles into a synonym for a dependency injection framework, especially if you're allowing for two implementations to run at the same time (e.g. and then compare their results for correctness otherwise fail with an assert). But arguing over definitions is not particularly enlightening or fun, so let's take feature toggles in their full generality as DI frameworks. I still don't think that's what Carmack is advocating for. Indeed Carmack specifically calls out: Code fearlessly on the copy, while the original remains fully functional and unmolested. It is often tempting to shortcut this by passing in some kind of option flag to existing code, rather than enabling a full parallel implementation. It is a grey area, but I have been tending to find the extra path complexity with the flag approach often leads to messing up both versions as you work, and you usually compromise both implementations to some degree. Now Carmack isn't talking about feature flags quite in the same way you are (I think the short cut he's talking about here is not doing a full re-implementation and interleaving parts of the old code with new code at runtime). Nonetheless in one important way he is talking about feature flags in the more general case, which is that his approach should lead to ideally no change to your original codebase (the only exception he lists is if your original code relies heavily on global state). This is not true for feature flags (or DI). You need to write new code just to support it. Indeed your link calls this out as the "carrying cost" of feature flags because of the additional "abstractions or conditional logic" they require. Hence feature flags are not the AAA form of what Carmack is talking about here (although they are one choice of implementation for the particular functionality Carmack is getting at if not necessarily his philosophy of do no violence to the original code). There are several other candidates, but the one I personally gravitate towards is hot code reloading. You write your separate code, then hot swap it in. And hot swap back your old code as necessary (either for educational purposes or for testing purposes). You don't need to decide up-front which parts of your code need to be designed around feature flags; it's all hot-swappable. You don't need to write extra logic to enable feature flags, the hot-swapping makes it all come for free. You don't need even need to shut down your process and restart it to read a new configuration!