The shape of the problem
Every non-trivial application has a connectionString that is one thing in Dev, another in Staging, and a third in Production. Multiply that by API keys, feature flags, capacity hints, region-specific endpoints, and per-tenant overrides, and you end up with a configuration matrix that nobody owns.
Jaws Deploy variables are designed for exactly that matrix. One variable name. Many values. The right one resolved at deploy time based on where the work is running.
How a value is selected
role:db), region (region:eu), tier (size:large), or any tag you've assigned.
Variable references
Variables can reference each other using #{Name} syntax. That means you can compose a connection string from parts (#{Server}, #{Database}, #{User}) and let the resolver assemble it for the current environment. No code in your deployment scripts knows that Server differs between Staging and Production - the variable layer handles it.
Composing without hard-coding
The connection string here is one variable. Its value uses references that themselves resolve per environment. The application config never sees the parts.
ConnectionString : Server=#{Db.Server};Database=#{Db.Name};User=#{Db.User};Password=#{Db.Password};
Db.Server : (different per environment)
Db.Name : checkout
Db.User : svc_checkout
Db.Password : (secret, encrypted, scoped to environment)
Secrets, properly handled
A secret in Jaws Deploy is a variable with one extra promise: the value is encrypted at rest, never echoed in logs, and never exposed in the API. When a deployment step uses it, the agent receives the value, uses it, and the log shows ******** where the value would have appeared.
That last detail matters more than it sounds. The default failure mode of "set the password in an environment variable" is that it eventually ends up in a debug log somebody pasted into a chat. Jaws Deploy redacts it before the log line is even persisted.
Config file transforms
Resolving variables is half the job. The other half is getting them into the right file format on the target. Jaws Deploy ships transforms for the common cases:
Variables -> config files, without templating gymnastics
- JSON variable replacement - dotted-path keys like
Logging.LogLevel.Defaultmap to nested JSON structure. - XML / XDT transforms - for legacy
web.configand friends, with merge/replace semantics. - Structured variable substitution - for
appsettings.json, environment-specific overrides land in the right place. #{Name}token replacement - for the long tail of config formats that nobody else supports.
// What lives in source
Defaults and shape
Your appsettings.json in source contains the shape, default values, and placeholders for what changes per environment. It is committed, reviewed, and stable.
// What lives in Jaws Deploy
Per-environment values
Connection strings, secrets, feature flags, capacity hints - the things that genuinely differ between environments. They live in variables, scoped appropriately.
A small but important opinion
The goal is not "every value is a variable." It is "every value that varies between environments is a variable, and every value that does not is in source." Pushing static config into Jaws Deploy variables turns the platform into a key-value store and your application config into a mystery. Resist.
The healthiest deployments have a relatively small variable set, all of which clearly answer the question "why is this not in source?" If you cannot answer that for a variable, it probably should be in source.