Recently, someone asked about how their code should work if a certain configuration setting was missing from the registry. The responses were mixed. One suggested checking the value of RegQueryValue. Another suggested that the missing value should cause the app to fail. The right answer is "it should use the default value." "But hark!" you say - "the question was about how to deal with the missing default value!" And the answer doesn't change - have a fallback strategy that can still pull a default value out of your ear at runtime.
Ok then, let's suppose you've done this and now have code that looks like "For this configuration widget, read the current value; if there is no current value, read the default; if there is no default, use <mystery value> instead." That's three levels of decision making, three independent code paths for your test team to validate. What if you could just cut out that middle piece and go back to two - a default in-code and a possible per-user override?
This leads me to the following broad statement - Defaults should be shipped in code, user configurations saved on the device in stable storage. Three reasons:
1. Setting a value during setup is a point of failure. Yes it almost never happens. But it happens. The best setup you can imagine is one done by (logical) xcopy. Imagine your app being run from a network share. The vast majority of applications - web apps, for instance - have no "install" step. They just start up and use default settings until overridden by a user-initiated configuration step.
2. "Reset to known good state" should be "delete all overridden settings and start again." Apps that have "repair" fail mightily when they try to distinguish important things to keep from unimportant things to keep. If the app is able to start up and run correctly from a completely clean state "repair" can be "delete this key, restart the app."
3. Servicing of user-defaults is impossible without an override model. Imagine a default color value like "blue". The user changes her mind and picks "orange" instead. Later they decide they like "blue" again. Now imagine an update coming down for your application that changes the default to "white". A naïve updater might look at the old default (blue) and the current setting (blue) and say "aha! They're still on the default, change to white!" which is not what the customer intended. They consciously chose "blue". Granted, this means your settings model needs a "use default" button, but that's a small price to pay for customer sat.
Every interesting app has some form of configuration the user or the environment can supply it. Well-designed apps work correctly even when there are no configurations provided at all.
1. "But I don't want to recompile to change my defaults!" Have your default values in a text file in your app deployment unit. Managed code provides an app configuration model based on yourexe.exe.config, which is reasonably OK. Others use a .txt file structured like an INF.
2. "But I can't possibly change all my code to use an override system!" Have you checked? I bet there's one or two interesting functions in your code that read those settings that you could change. If there are many functions that all do configuration checks, refactor them into a single point of access where you can do this.
3. "But having defaults in code means image bloat!" A dword in code is maximally 4 bytes in the const data section, less-so if the optimizer inlines uses of it. A reg_dword is those four bytes plus the size of the registry value, the key that references it, the parent key of that key, and so on. You're trading a small fixed overhead for a larger unknown overhead.
Note: this is orthogonal to registering with the system (such as creating services, registering COM goo, etc.). Apps that integrate with the system do need an "announce" step (call it 'install' if you prefer). The announcement-management should be owned, debugged, and tested by a central team. Windows, CE, and others all do this wrong today for legacy reasons. MSI tries to make this better by requiring you to register COM objects through their tables, but it's a stopgap because they still allow calling into DllRegisterServer. One can imagine a system where a data-driven “announcement” mechanism is the only way to get integrated with the system, and the announcing application gets to run no actual code of its own.