DEV-14Recommended
Read settings in one documented order from standard folders
Take each setting from the first source that sets it. The order is flag, environment variable, project file, user file, system file and built-in default. On Linux and BSD, find files through the XDG base directory variables.
How to do it
A user who cannot tell why a value applies loses an afternoon, and an agent guesses. Write the order in the help text and keep it fixed. The XDG Base Directory Specification puts user settings in $XDG_CONFIG_HOME, which defaults to ~/.config. System settings go in $XDG_CONFIG_DIRS, which defaults to /etc/xdg. Caches go in $XDG_CACHE_HOME and history or logs in $XDG_STATE_HOME. The specification says to ignore a relative path in these variables. On Windows, use %APPDATA% for settings and %LOCALAPPDATA% for caches. On macOS, follow the XDG rules or use ~/Library/Application Support, and document which. Never write a dotfile straight into the home folder. Offer a command such as example config list --show-origin that prints each value and the source it came from. Secrets never come from flags, as Take secrets from the environment or a file instead of a flag says.
Examples
Python example
import os
from pathlib import Path
def user_config_dir(app: str = "example") -> Path:
if os.name == "nt":
return Path(os.environ["APPDATA"]) / app
xdg = os.environ.get("XDG_CONFIG_HOME", "")
base = Path(xdg) if os.path.isabs(xdg) else Path.home() / ".config"
return base / app
def resolve(name, flags, env, project, user, system, defaults):
for source in (flags, env, project, user, system, defaults):
if name in source:
return source[name], source.origin
raise KeyError(name)
Go example
func userConfigDir(app string) string {
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), app)
}
base := os.Getenv("XDG_CONFIG_HOME")
if !filepath.IsAbs(base) {
home, _ := os.UserHomeDir()
base = filepath.Join(home, ".config")
}
return filepath.Join(base, app)
}
How to check
A test sets one setting in all six sources, then removes them one at a time from the top. Each time the value comes from the next source in the order. With XDG_CONFIG_HOME=/tmp/x the tool reads /tmp/x/example/config.toml, and with XDG_CONFIG_HOME=relative it reads ~/.config/example/config.toml. A run with an empty home folder creates no file directly in it.
Sources
Related rules
- DEV-7 Take secrets from the environment or a file instead of a flag in Developer tools, SDKs and CLIs
- DEV-17 Check for new versions at most once a day and never in scripts in Developer tools, SDKs and CLIs
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/developer-tools/configuration-and-telemetry/config-precedence">Agent Wranglers developer tools, SDKs and CLIs playbook</a>. In code you write, add a comment next to it with the same link.