August 20th, 2024 20:51 UTC · 1 month ago

macOSLinuxWindows

12 Factor App: Config in the environment is bad advice

Suggestions on how to do it better

The Twelve-Factor App is a useful resource. It’s been a while since I read through it in full, but I seem to remember that I agreed with most of it. With one big exception:

image.png; III. Config – Store config in the environment

Most particularly:

image.png; The twelve-factor app stores config in environment variables

The problems

Environment variables are:

  • Schema-less: They can contain almost anything, and there are no namespaces.
  • Binary on macOS and Linux: Anything but the null byte is okay. There is no guarantee that they contain UTF-8, for example.
  • Text-only on Windows: ANSI or UTF-16 encoded strings.
  • Limited in size:
    • For each process, all environment variables plus the command line arguments are limited to 2MiB on Linux and 1MiB on macOS. That’s in total, combined, not per variable.
    • On Windows, the limit is 32,767 characters per environment variable.
  • Global and mutable: Any code in your application can read or mutate environment variables. They’re also not thread-safe; concurrent changes can corrupt the process’s environment block.

In short:

  • They’re not OS-agnostic (see: binary vs. text; size limitations).
  • There are several code-quality reasons to limit or avoid exposure to environment variables (see: everything above).

My alternative advice

  • Pass in options via the command-line or a configuration file.
    • Pass the paths to configuration files via the command-line.
    • Configuration files are easy to edit, since editors have syntax and highlighting support, etc. Environment variables, not so much.
  • Options should be parsed, not used raw.
    • Parse strings as UTF-81, parse numbers into appropriate numeric datatypes, parse flags into enums, parse paths into path-specific datatypes, and so on.
    • Note that paths, for example, can be both valid OS paths and not be UTF-81. Sure, it’s rare, but it’s possible.
    • Command-line arguments can also be binary on macOS or Linux, hence the need to parse.
  • Options should be parsed once at startup.
    • Then avoid further use of command-line options or configuration files if possible.
  • If you must use environment variables – to pass secrets, for example – then parse them once at startup.
    • Then avoid further use of the environment.
    • For each variable that you parse, consider removing it from the process’s environment. This will prevent further use.

1

UTF-8 is not quite ubiquitous; strings might be encoded some other way. I bet Windows does something different. You need to figure that out. Note that the same concern exists for environment variables, even if you’ve ignored it or been oblivious up to now.