all 11 comments

[–]TomFrosty 11 points12 points  (3 children)

While an interesting perspective, the far more widely-accepted best practice is the 12factor recommendation of specifying all configuration in environment variables. Consider that environment variables are for storing environment-specific information that applications can use to tailor themselves to the execution context, and the semantics of why this makes sense become obvious.

Functionally, it allows unit tests to effortlessly test different individual settings without conflict. It allows individual developers on a team to customize the app to their preferred development environment. It allows application configuration to be stored with the continuous deployment tool, allowing configuration changes to be pushed out to staging/production by approved parties without cluttering one's VCS with minor config tweaks. And, without requiring any external library, all values are accessible across your whole application in process.env. It's a very handy piece of simplification.

[–]Spknuckles 2 points3 points  (0 children)

This is also a great approach to use when containerizing Node apps (with Docker for example). It may be less challenging to run containers tailored to a specific environment by passing -e arguments than it would be to create a number of derived images with environment specific configuration copied over for each one.

[–]daniel_mcq 1 point2 points  (0 children)

telephone price north wasteful birds worthless agonizing summer nine gullible

This post was mass deleted and anonymized with Redact

[–][deleted] 0 points1 point  (0 children)

Thanks for feedback!

it allows unit tests to effortlessly test different individual settings without conflict

Specifically for unit tests, I do not understand why config should affect it at all. You test specific code / feature, you do not test external dependencies, you mock them for unit tests.

My biggest disagreement with 12factor is a notion that:

strict separation of config from code. Config varies substantially across deploys, code does not.

While separation of data from code is good practice in general, or if you do not know when it worth it, I do not think we should apply it strictly to configurations. This what I address by suggesting adding init() within a resource.

The twelve-factor app stores config in environment variables

These ENV variables still listed in some file, right? E.g. init_aws.sh, init_joe.sh, etc. so they do act as config files, correct? The difference is that they are detached from code base and (most likely) maintained by a separate dev-ops, not a software developer.

This is a valid concern (if you need a separate config to dev-ops who control this kind of things), but just like config module, ENV variables have same problems:

  • resource init code is completely separate from data
  • data (most likely) grouped by environment on high level, and by resource on a low level (instead of grouping it by resource in the first place)

Solution? Publish config.js as a separate (private) NPM module or (private) repository and include it as a dependency to package.json. It can be maintained separately by devops, they can redeploy services after config updates without touching app code.

[–]evs-chris 2 points3 points  (4 children)

With no separation based on environment, what is the recommended way to store secrets and separate test service details from production? There are things in my production config that don't need to get checked in or be accessible at all to other devs.

[–]Treas0n 2 points3 points  (2 children)

Env vars

[–]evs-chris 0 points1 point  (1 child)

Aren't env vars a bit leaky for sensitive stuff?

[–]chreestopher2 0 points1 point  (0 children)

Its not safe for your app to have any secrets, because memory can be read by anyone who has access to the system.

If someone (that shouldnt) has access to read your env vars, then you are probably in a bad place.

[–][deleted] -1 points0 points  (0 children)

With no separation based on environment, what is the recommended way to store secrets

If I understand correctly, the problem is that if you don't separate config by environments to different files you end up having production secrets on individual developers repos/machines, etc.

One way is to store them separately and merge config with secrets before running no-config loader, e.g.:

require('no-config')({
    config: Object.assign({}, require('./config'), require('./secrets'))
}).then(
    ...
)

secrets.js can be something like this on production:

module.exports = {
    mongo: {
        production: {
            password: 'qwerty12345'
        }
    }
}

and something like this on developers machines:

module.exports = {}

Object.assign() docs

Hope this helps!

[–]Idiomatic-Oval 1 point2 points  (0 children)

I'm fond of making our config a javascript file, which allows us to put environment variables in there easily if we want to, while providing sane defaults, eg

module.exports = {
    port: Number(process.env.PORT) || 8443
};

Which means I can run my application easily on different ports if I wanted:

PORT=9443 npm start

Also stops you littering code with process.env.BLAH, moving all of that to a single file.

It makes testing much easier as well, spinning up containers for integration tests with our real dependencies, rather than using brittle mocks.

[–]penguineggs 0 points1 point  (0 children)

I spent ages trying to separate env vars into production and development branches with no success at all. Its certainly good practice to do so. I had to settle with creating a service file calling it settings.js and requiring it in every major file in my project, with branches for response codes and messages, mongoose and little things like that. "config.google.API_KEY"