Hacker News new | ask | show | jobs
by guggle 1090 days ago
All the software I write is configured through env vars... why use anything else ?
5 comments

Sometimes configuration is fairly complex (e.g. nested map data structures) which gets pretty complex with simple key/value env variables.

I'm thinking of things like config for nginx, Apache, Kubernetes, etc.

I've had rare cases of needing nested data structures in the environment. My solution was to specify them as JSON strings and load the value in code. It works.

    USER='{
        "name": "joe",
        "email":"joe@example.com"
    }'
Indeed, I only had "simple" cases in mind. But hey... shouldn't the complex config files paths be specified through env vars ? ;)
- If your configuration has more than 5-10 options then env vars become a mess while a configuration file is more maintainable - Nested configuration / scoping is a mature advantage of configuration files - You can reload configuration files whereas you can't reload environment variables during runtime - A configuration file is a transparent record of your configuration and easier to backup and restore than env variables. Env variables can be loaded from many different locations depending on your os. - In configuration files you can have typed values that are parsed automatically with env variables you need to parse them yourself. This is just a difference not that bad for env variables per se.
You can’t change env vars after a program is running, but I can change a file and send a SIGHUP.
Programs reconfigurable at runtime are by far rarity, as that's generally pretty hard thing to do. But yes, just have a config file, ENVs are nice as a backup for small apps, but never as only way
Anything running as a daemon/service should support this capability IMHO, especially if it is servicing active traffic.
It's definitely nice when it does but it's also a whole lot of complexity to add to the code. You have to re-create all structures relying on configs, reconnect if needed, clear all previous ones, but only after any ongoing processing of requests received before the reload signal finishes.

Some go half-way, like HAProxy where it does spawn a new process but that process just binds with SO_REUSEPORT and signal old one to close the socket and finish all remaining connections. So you effectively get hitless reload, it's just that old process is the one finishing in-flight requests

Yeah, it def adds complexity but if you want it used in real world applications, it’s a must-have. With only env vars, it’s an impossibility to ever have (without supporting infrastructure and processes), which was the point I was trying to make.
Hardly. "Real world applications" need to tolerate single node failure anyway so reload functionality doesn't add all that much. Some system daemons, sure, them not working for a second can be disruptive so it is worthy endeavor there, but the amount of added complexity is nontrivial and the added complexity scales with apps' own complexity.

It's a bit different for desktop apps, as restarting app every time you change some config options would be far bigger PITA than some server app.

Coz it's entirely miserable experience when you get like... more than 4 config options
Yes. Env vars beat constants but have stunted our thinking because we know deep down it's not a great idea to have too too many.

Config files are better, but can lead to the mess described here and typically only give us crude axes for overriding.

It's time for us to embrace & tame the complexity. If you use a feature flag tool already, you know what it's like to target different values based on rules.

If I want to experiment with changing the http timeout for specific calls from a single service in a single region I should be able to roll that out.

It's time to expand our brains and bring this feature flag style thinking to the rest of our configuration.

> If I want to experiment with changing the http timeout for specific calls from a single service in a single region I should be able to roll that out.

...why ? how often you do it ? What actual app feature it fills ? Why would you throw permanent code at something that you need to do once or twice?

That kind of thing is FAR better served as customizable hooks (loadable .so/.dll, maybe even attached Lua interpreter) rather than bazillion random flags for everything and checks cluttering code.

May need to agree to disagree :) I'm saying that we should write:

httpClient.get("my/url", timeout: config.get("http.timeout"))

Where config is a library smart enough to do some rule evaluation over a live updated backing store to get the right value.

I'm not sure how .so/dll or lua is going to help here.

For anything more than trivial configurations it can be nice to commit them to source control, giving some reversion capabilities, and easy replication on other projects.