Hacker News new | past | comments | ask | show | jobs | submit login

I was taught many moons ago that configuration, like ogres and onions, is best considered in layers:

1. default values: What will most users in most places find most useful/least infuriating?

2. configuration files (system-wide, then user): What will most users on this system want most of the time? What will this particular user want most of the time?

3. environment variables: How should this session (i.e., a potentially large series of related executions) be tailored?

4. command line options: What is most useful for this particular run?

I was also taught that:

- figuring out how to go from an option to the name of a corresponding environment variable to a line in a config file should be both straightforward and well documented; and

- sometimes you need a more complex configuration than is cleanly supportable through any other method than a file. In such a case, the location of that file can itself be passed through options and the environment.




This is precisely how I setup my utilities. I have found following practices useful:

1. Print the path of the system and user level configurations that the utility honours in the help text (-h/-?)

2. For an option that can be set interactively or via environment variable, specify the environment variable name in the help text itself to provide maximum choice to the user.

3. Provide a -viewconfig option that prints out the final resolved configuration state so that the user can see the actual configuration that is in effect. Combined with a -dryrun option, this can provide a lot of confidence to the user to try out things without breaking anything.


My guess for this is that some people have not had the good fortune of seeing software that followed this pattern and how nice it is.

I thought it was common knowlegse that if really wanted to do configuration right on a given project, you do all 4 (with some library support) and you write your code to gracefully handle the right piece of configuration from the appropriate "override level" (again usually with the support of a good library).

See also: Domain Driven Design[0] which (if you ignore the consultant-fodder and jargon that comes with it) is probably one of the best written guides of how you should abstract systems, just like the gang of four book is a good introduction to structures in program/algorithm implementation you're likely to see in real life.

[0]: https://en.wikipedia.org/wiki/Domain-driven_design


Yeah, the article is just confused:

> Envvars have some legitimate usages (such as enabling debug logging) but they should never, ever be used for configuring core functionality of programs.

As though logging weren't core functionality!

The actual thing that is bad is grabbing an environment variable in the middle of your program. You should grab all the configuration in one place and use it to configure local state that is transparently passed around. Furthermore, flags, env vars, and config files are all just maps from strings to configuration, so you should use some system that can transparently layer them on top of one another. All of my new CLIs use flags first and fall back to ENV vars if the flag wasn't set.


I like layered config as well. It really should be the default way of thinking about config. Our custom application framework handles that exact sort of layering and it's wonderful. I give it an annotated class representing the config that I need and it handles populating the fields from the config and generating the help message if something is missing.


This echos nicely the traditional wisdom, also described here: http://www.catb.org/~esr/writings/taoup/html/ch10s02.html


(for default values, make sure you consider safety too! for example a debug option that might show PII is likely to be most useful to most people using the program, but shouldn't default to on because if it were on in prod the consequences would be serious)


yeah, i don't get too worked up about "how" config values enter the application as long as i can easily see "where" they are initialized/validated.

an immutable config object/class created on startup that reads files/env vars/whatever and has appropriate assertions to ensure good values were used and crashes the app for missing/bad values usually keeps things sane.

an app where each subcomponent has its own config that it gets in its own way usually leads to confusion and delay




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: