|
It's embarrassing to see a place like HN, which is supposed to be cutting-edge, continuing to use designs from 2005. *Configuration Values* Your laptop is not hosting your website (I presume), so .env is not going to be enough to run your app somewhere other than your laptop. I get it. You only want to run your app locally, and .env is convenient. But your production server probably isn't going to load your .env file directly, and it will probably need extra or different variables. This disconnect between "the main development environment variables" and "the extra stuff in production" will lead to inconsistencies that you have not tested/developed against. That will lead to production bugs. So keeping track of those differences in a uniform way is pretty useful. How do you specify configuration for development and production without running into inconsistency bugs? By splitting up your app's configuration into "static" and "dynamic", and version-controlling everything. 1) "Static" configuration is things like environment variables, which do not change from run to run, and are not environment-specific. So for example, an API URL prefix like "/api/routes" is pretty static and probably not going to change. But an IP address definitely will change at some point, so this configuration isn't static. (To think about it another way: on your computer, some environment variables are simply stored in a text file and read into your shell; these are static) 2) "Dynamic" configuration are values that may change, like hostnames, IP addresses, port numbers, usernames, passwords, etc. Secrets are also "dynamic", because they should never be hard-coded into a file or code, and you will want to rotate secrets in the future. All dynamic configuration should be loaded during a deployment process (for example, creating an ECS task definition, or Kubernetes yaml file), or at runtime (an ECS task definition that sources environments from secrets, or a Kubernetes yaml that sources environments from secrets, or a function in your code that calls an API to look up a secret from Hashicorp Vault or similar). In particular for secrets, you want to load those every time your program starts, as close to the application's execution environment as possible. (To think about it another way: some environment variables on your computer require executing a program and getting its output to set the variable - like your $HOSTNAME, $USER, $SHELL, and other variables) 3) Both static and dynamic configuration should be version-controlled, and any change to these should trigger a new deployment. If a value changes, and you don't then immediately make a new deployment, that change could be harboring a lurking bug that you won't find out about until someone makes a deployment much later on, and trying to find the cause will be very difficult. *Infrastructure Patterns* test, stage, and prod servers are like pets. You have individual relationships with them, change them in unique ways, until eventually they have their own individual personalities. They become silos that pick up peculiarities that will not be reflected in other environments, and will be hard to replicate or rebuild later. Instead, use ephemeral infrastructure (the "cattle" in "pets vs cattle"). There should be a "production" infrastructure, which is built with Infrastructure-as-Code, to create an immutable artifact that can simply be deleted and re-created automatically. That same code that builds production should build any other server, for example for testing or staging. When the testing or staging is done, the ephemeral copy should be shut down. They should all be rebuilt frequently to prevent infrastructure rot from setting in. This pattern does a lot of things, like making sure you have automation for disaster recovery, using automation to prevent inconsistencies, using automation to detect when your infrastructure-as-code has stopped working, saving money by turning off unneeded resources, and the ability to spin up a unique copy of your infrastructure with unique changes in order to test them in parallel to your other infrastructure/changes. It also makes it trivial to test upgrades, patch security holes, or destroy and recreate compromised infrastructure. And of course it saves you time in the long run, because you only expend effort to set it up once. *This Is Not About Scaling* I know the first thing everyone's going to complain about is something like "I'm not Facebook, I don't need all that!" or "It works fine for me!". There's a lot of things we do today that are better for us than what we did before, even though we don't have to. You brush your teeth and wash your hands, right? Well we didn't used to do those things. And you can still live your life without doing them! So why do them at all? Because we've learned about the downsides of not doing them, and the benefits outweigh the downsides. Getting into the habit of doing things differently may be annoying or painful at first, but then they will become second nature, and you won't even think about it. |
I'm not sure exactly what parts of the comment are about secrets rather than how infrastructure should be done, but I see that secrets and configuration have very different lifetimes so they should be provisioned separately. The config can for example be in the git if it's free of secrets.
Secrets are provisioned at runtime, while config is build time.
`secretspec.toml` is in the version control and it tells you all about what's going to happen at runtime.