
Make hard coding your default choice - vkhorikov
http://enterprisecraftsmanship.com/2015/06/08/make-hard-coding-your-default-choice/
======
drig
I always use an in-between system. My configuration library allows me to set a
default, if the parameter is not found in the config file. I always set a
reasonable default, and then only override if necessary. This keeps the
configuration files small, keeps the default value with the code, but allows
you maximum flexibility, just in case.

~~~
jvoorhis
Another tactic that has worked well for me is to expose a flat namespace of
flags and tunable parameters in config while shunning instantiation of complex
object graphs. Complex nesting in configuration tends to be too "raw" for
users and sysops, and hurts usability.

------
taylodl
_" The possibility to change the minimum log level required to trigger the
rule seems sensible because we might want to adjust it on the fly for
debugging reasons. Nevertheless, odds are we never appeal to it in practice.
So, we are better off removing this setting as well."_

Everything was going fine until I got to this point. You can't tell when
you'll need to adjust the log level on the fly and he's right in stating most
likely you'll never do it, but when you need to do it you _need_ to do it -
and predicting that need is impossible. This is one of those rare things where
it's better to have it and not need it than to need it and not have it.

Otherwise this is a good article on addressing the issue of configuration
hell.

~~~
falcolas
I find the happy medium here is a command line parameter. Hard code the
default to something sensible, and have a command line parameter (-v) which
overrides the sensible default. No need to recompile, no need to worry about
the parameter 99% of the time.

~~~
reagency
This is how almost everything works now and so it's weird to even have he
discussion.

------
mpweiher
Yes. At the BBC project I've talked about before (µservices to monolith, 100x
- 1000x faster), I also removed all external configuration files. This turned
out great, and it made "deployment" as trivial as possible. Copy jar file to
directory, start (maybe with a command line argument, don't remember 100%).

Why no config files? Very simple: a change to config file was just as
"expensive" in terms of deployment as a code change, so around a day overhead.
Compared to that overhead, the recompile was not measurable, so there was no
benefit to config files.

On the other hand, apart from making everything simpler, configurations could
now be coded as Java classes, with all the subclassing/abstraction/composition
goodness.

~~~
tokenizerrr
The way I like to do things is mostly as you describe, but also to set up the
configuration to be (de)serializable by your favorite xml library. The
defaults are in the code, but they can be overridden on a per-deployment
basis.

------
serve_yay
The "configuration hell" described here is what working in Spring was like
(Spring.NET in my case). That style of coding takes the no-hard-coding mantra
to an impressively ridiculous degree. And it really was hell, partially
because your app is half "real code" and half XML, but also because you could
break everything at any time by making a mistake in the config. Easy enough to
check for that with smoke tests but it's still a terrible way to write apps. I
think I'm still recovering.

~~~
kedean
For me the issue with spring-style configuration isn't so much the sheer
volume, as almost everything has sensible defaults, it's the loss of type-
checking. When everything is configured via xml, you have to run the app
before you know if the configuration is even valid, and refactoring becomes
hell because settings are all referenced via strings instead of qualified
names.

------
AndrewDMcG
YES! No system is harder to maintain than one that consists of a huge mesh of
configuration files. No kidding, I have worked on a large company-wide
reporting system where the easiest changes to make were to the code. Code
changes could be made, tested and released in a fairly straightforward way.
The harder things to change were to configuration files (Spring, things like
that). Because they had global effect, and contained elements that were
specific to a dev/test/production environment, making and deploying changes to
one were more difficult. But the very hardest things to make changes in were
the report and process parameters stored in the database. They were impossible
to isolate the effects of since we had no easy way to trace to what functions
they might affect, and were generally treated as immutable legacy that you
worked around changing rather than touch. (I wouldn't claim this was a good
system)

When you put something in a configuration file, you need to define who is
going to change it, and how they are going to test their change. Otherwise
you're in danger of muddling together different levels of abstraction, and
breaking encapsulation all over the place.

------
nickysielicki
I believe this is absolutely awful advice. No offense to you OP, but if I took
a job somewhere and saw that they were doing this, I would cut my losses and
leave immediately.

While it sounds quite nice in theory to slim down your configuration to only
have runtime configuration for variables that you see yourself changing, what
I imagine ends up happening in practice is that you're left in a situation
where you're doing far more work and only superficially benefitting by having
an config that appears short and simple, but is in reality a complex system
with nooks and crannies.

Here are some problems that I see with this:

* You're now on the hook for maintaining this software. Security problem? Your problem. Update? Your date, canceled, because an upgrade to the program changed everything. The version distributed by your distro had a nice post-installation hook for the new layout, but you have no such benefit. Your distribution might even have a few distro-specific patches for the software. Is tracking the upstream version, tracking your distros patches, and compiling the software actually saving you effort?

* Potentially undefined behavior. A configuration option can be in undefined and taking a default value, defined at compile time, or defined at runtime. Are you intimate enough with this code to know what's going to happen when you define it in multiple places? Maybe you add something to your runtime options, only to find out a week later that the change never happened because it was already set at compile time and that takes preference. Maybe the program has a runtime exception and crashes 3 days later at 3:00AM because it doesn't know what to do when it finds it in multiple places.

* Who the hell is looking at their configuration options enough that this actually crosses their mind? I've configured exim4, so I know exactly the situation you're describing with regard to a configuration that is just downright ugly. But I don't have a visceral reaction because I know that once I've configured this, I'm done. Maybe a few months later I change an option.

Mixing compile time and runtime configuration means you not only lose the
advantages of both, but you gain their disadvantages as well.

~~~
AndrewDMcG
I would assume the author is talking about in-house software development. If
you are delivering software to a client, you don't want them to have to
recompile sources to make changes. If the developers of the software are also
responsible for its runtime configuration (which is very common in banking,
telecoms, etc.) then changing code can be as easy or easier than changing
config.

------
bryanlarsen
I'd go even further and disagree with this statement too:

"By hard coding, I don’t mean you should spread magic numbers and strings
across your project’s source code."

The first time you use a magic number or string, put it in directly. It's much
easier to find and change in its home environment, and it keeps your code
simpler and easier to understand.

Of course, once you use that magic number somewhere else, then you should
introduce a constant -- you don't want to get in a situation where somebody
changes it in one place but not the other. But until then, don't complicate
things.

If you feel that I'm a heretic, then please define the constant where you use
it (when it's only used in one place), rather than putting it in a header.

~~~
louthy
I think there's a difference between:

    
    
       5
    

embedded in the code, and

    
    
       const int maxRetries = 5;
    

at the top of a file. Both are still hardcoded, but there's at least context
for the number.

~~~
bryanlarsen
I'd far rather have the 5 embedded in the code. It should be obvious from the
code that the 5 is the number of retries, and it's often important to know at
least the magnitude of the number when reading the code. Don't make me hunt
for the number unless you have a good reason for doing so.

If the name is required to understand the code, then putting your "const int
maxRetries = 5;" in the code is better than a comment that says the same
thing. Just don't put it at the top of the file. Put it on the line before you
use it.

~~~
reagency
Factoring out constants has a nice effect of turning your header into a sort
of declarative program, a quick summary of the interesting behavior choices
the program makes, a guide to understanding the program at a glance.

With numRetries=5, you can see he program makes retries. Withjust 5, that fact
is hidden away in a method parameter somewhere.

And unless your IDE is ed+cat, there is no hunting needed to jump to a
definition of a method. Having layers of generality makes code easier to read.

~~~
Eridrus
> And unless your IDE is ed+cat, there is no hunting needed to jump to a
> definition of a method. Having layers of generality makes code easier to
> read.

I completely disagree; adding extra indirection/generality always makes your
code harder to understand. Adding meaningful/intuitive abstractions makes it
significantly easier though.

My pet peeve is Java interfaces that only exist because you need to be able to
mock the object in testing, but otherwise only have one real implementation.
Especially when the name gives no indication that it's an interface, so you
only find out after you pressed F3.

------
xg15
I don't know... I think whether this approach works or not really depends on
the kind of software you're developing.

I could see this being reasonable for cloud/backend stuff, where the only
people that deploy and run the software are next door to the developers, if
not identical with them. In that case it really doesn't matter much if you
change a line in a config file or a source file.

But for client-side software or anything else that actually gets run by
someone not affiliated with you, this seems bad advice to me. It's very hard
to anticipate all possible use cases and very easy to throw away features that
are still in wide use.

As examples, imagine this strategy were followed by IDEs ("Darcula theme and
Lucidia Console for everyone! If you want something else, file a bug and
convince us!"), Samba ("No more share definitions! We'll just turn every home
directory into a user share...") or Apache HTTP ("mod_mime_magic is good
enough anyway..."). I don't think this would go very smooth.

Of course you should always watch the complexity and scope of your config
files and avoid second system effect. If your find that your file's data model
has grown from a key-value map to a tree or a graph, you don't have a config
file anymore, you have a DSL. There are valid reasons for employing a DSL, but
people should be aware when they do so, as the usage patterns, tooling and
"audience" are different.

------
crazygringo
But many times the hard-coded values get used multiple times, have
relationships between each other, it's unclear where they are in the code,
etc.

Keeping them all at the top of the file (connectionTimeoutSeconds = 30,
maxConnectionRetries = 3, maxSimultaneousConnections = 5, etc.) lets you
clearly see, at a glance, what the current configuration is and how the
options relate to each other, instead of having them scattered across the code
nobody-knows-where.

You don't have to go all-out and create some separate monstrous configuration
file nobody will understand. Just separate out hard-coded values in constant
names at the top of each file, for a start.

------
makeitsuckless
I suspect that a lot of this has to do with simply having shitty configuration
loading from horrible (XML) config files. This small example already gives me
enterprisey nightmares.

The author also omits many of the arguments for config files other than
change, like being able to easily check what the current setting is without
having to dig into the code, or reading out the configurations from other
tools.

I you work on a project where "configuration hell" is an issue, maybe one
should focus more on addressing the "hell" part instead of taking the
configuration out of the equation.

------
AnEngineer
This thread may have already gone stale, but just in case not...

I've found that treating configuration settings as "first class abstractions"
in the Domain Model pays off quite nicely. By this, I mean grouping related
settings into an immutable class which is given the configuration settings
provider in one of its constructors. If there are reasonable defaults for
_all_ settings, then having a no-args constructor may be acceptable as well.

For example, in Scala I have types such as:

<pre> // Assume 'config' is what can read external configuration case class
NetworkSettings (config : SomeConfigReader) { val rootPath = "company-
name.project.network";

    
    
       host : String = config.stringEntryOrDefault (s"${rootPath}.host", "localhost");
       port : Int = config.intEntryOrDefault (s"{rootPath}.port", 1234);
    
       // etc.

} </pre>

Of course, this pattern could be used before or instead of having the ability
to externalize configuration parameters.

------
zamalek
I mostly agree with exception of: it heavily depends on where you work. The
exact logging configuration example is a prime example of what an ISV customer
would _want_ to change - many of our customers (and QA) go as far as writing
their own adapters so they need to be able to completely customize the
configuration.

You have to draw the line somewhere, though. I tend to err on the side of
"unconfigurable" until a request is made to make something configurable. E.g.
Someone had the smart idea of having all our logging _messages_ in a config
file all the years back when the logging component was written, which is an
incredibly frustrating anti-pattern. That was never needed and the messages
should have been stored elsewhere.

~~~
droffel
"Someone had the smart idea of having all our logging messages in a config
file all the years back when the logging component was written, which is an
incredibly frustrating anti-pattern. That was never needed and the messages
should have been stored elsewhere."

The company that i'm currently working for uses this pattern in their
software, so they can properly internationalize the messages. Unless I'm
misunderstanding the context of your comment, storing the messages in a
configuration file can potentially be a logical design decision.

~~~
zamalek
> The company that i'm currently working for uses this pattern in their
> software, so they can properly internationalize the messages.

It's .Net so the "correct" way to do that would be to use .resx, which have
built-in architecture for localized messages (even though they are still XML
files). There's more reasons for me disagreeing strongly with the specific
implementation, but it's a _tiny_ part of the stack that rarely gets used; I'm
making a mountain out of a molehill. It was just one example that I had on-
hand.

------
codazoda
I mostly agree with this article. Nitpicking a point, however, I don't
consider a constant to be a "magic number". If you've given it a clear
variable name, such as "defaultRetries" then it is no longer "magic".

~~~
Roodgorf
I believe you're actually agreeing with the author here. I took his comment to
mean "Don't have unnamed numbers/strings/etc., refactor any 'magic numbers'
into constants, that way they will be in one place with a sensible name."

------
andmarios
If you are not the one responsible for deploying your code, please avoid hard-
coding everything.

I had developers tell me “I expect mongodb at this (hardcoded) address”. Not
funny.

Another funny story with “log level” was when a scala app occasionally went
astray and returned more than 100GB of logs (level WARN) in less than an hour,
filling my disks.

Most of the time developers can't understand deployment. They think it is the
same as testing on their laptops or test VMs; run everything manually on the
same machine with root privileges, delete everything once you are done.

------
ajanuary
I read this as an interesting extension of "sane defaults" in library design:
adopt new sane defaults for your applications particular domain.

------
TazeTSchnitzel
Take the sensible middle-ground and use a constant. If you're using a good
language, you can later make the constant a configuration option, or vice-
versa.

