Dynamic variables in CI/CD: how the pipeline computes the environment itself

Manual environment variables are the most common cause of discrepancies between stages: an edit was forgotten, and the release went to the wrong place. We set up dynamic CI/CD variables turnkey — the pipeline determines the environment itself and substitutes the required values. We reduce release preparation time and eliminate a class of failures caused by human error.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

Hardcoded environments break the pipeline: dynamic CI/CD variables

The database connection string, image registry address, and logging mode are written directly in the build pipeline definition.

The build is green, the artifact is published, but after moving to the pre-production environment the application comes up against the test database and writes logs to a shared index.

The error does not reproduce on the development branch — there the values match by accident, because they were once copied by hand.

Such hardcoding of environments costs more than it looks.

Configuration desync surfaces not during the build but during deployment: separate sets of values for each environment live their own lives, a change in one is forgotten to be carried over, and rollback returns not only code but also incorrect parameters.

Each new environment is another layer of copies and another point where the environment and its description diverge.

Dynamic CI/CD variables remove this dilemma. Values are computed at launch time from context: the branch name and tag define the environment, the directory defines the image name, and the result of the previous step defines the dependency address.

Environment variables in the pipeline are inherited by levels — shared across all jobs, per-environment, per-step local — and overriding works by an explicit priority, so any value can be traced back to its source.

Secrets remain outside the repository and are injected only at job execution time. We look at how this is handled in practice and what to check before moving between environments.

What changes after implementation: the effect in numbers and failures

Dynamic variables are computed at job launch time — from the branch name, tag, changed paths, and environment name.

The same configuration file produces different values for the test and production environments, and entering them by hand before every release is no longer necessary.

The first measurable effect is repeated runs. The cache key and artifact addresses are assembled from computed values automatically, so matching jobs reuse the result instead of rebuilding the same thing again.

Build time drops proportionally to the share of repeated jobs — most noticeable on branches where changes come in batches.

The second effect is a whole class of failures that ceases to exist. Configuration-related failures are almost always a consequence of divergence: an address was changed in one place and forgotten in another.

Automatic value substitution removes the source itself — the value is computed once and arrives in all steps identically.

The third is pipeline downtime. When variables are not edited by hand, a release does not wait for an engineer with access to settings, and the window between “code is ready” and “code is in the production environment” shrinks to the run time.

So this does not remain just words, we take a snapshot before and after: the share of manual edits in configuration, the number of failures due to mismatched values, and the median re-run time. On GitLab CI/CD such metrics are collected with built-in tools, without external tooling.

Methods for computing variables: file, template, launch time

A variable value can be taken from a static environment file, assembled from a configuration template, or computed at launch time.

The difference shows up during rollback and re-run: the later the value is computed, the fewer manual edits are needed when moving between environments — and the harder it is to reproduce the build exactly.

The priority of value sources is recorded separately. The usual chain is: default values from the repository, then project-wide ones, then overrides for a specific environment, then what the build process agent substituted at launch.

If the override rule is not described explicitly, the same key comes from two places, and the build behaves differently depending on load order.

Option When it fits Limitations
Static environment file Values are stable, there are few environments, the set of keys changes rarely Manual editing for each environment, copies diverge, sensitive values are stored in the repository
Configuration template One framework for many environments, differences are covered by substitution Need to validate substituted pairs: an error in the template breaks all environments at once
Computation at launch Values depend on the branch, tag, or environment name Need access to launch metadata; this behavior cannot be reproduced locally

The practice is to combine: keep the framework in a template, compute the volatile part at launch, and leave the static file only for what truly does not change.

How we implement it: from configuration audit to handover

Dynamic CI/CD variables break not during setup but during handover: the pipeline is left without the engineer who remembered why a value is overridden in one environment and taken from a secure vault in another.

That is why we start not by editing configs but with an inventory — what is already computed, what is hardcoded, where the same value lives in three places.

  1. CI/CD configuration audit. We collect all points where values are set: pipeline files, repository settings, variable groups, secret vaults. We mark duplicates, leftovers from debugging, and places where a value was set in several ways at once.

  2. Environment dependency map. We show what exactly depends on the branch, tag, directory, or stand name. This makes it visible where computation produces an incorrect result when the launch context changes.

  3. Priority scheme. We fix the resolution order: the launch level overrides the branch level, the branch overrides the environment level, and below that is the shared set. We separately describe who is allowed to override a value and what happens in a conflict.

  4. Computation setup. We describe substitution rules and computed variables, remove addresses and credentials from pipeline files, leaving references to vaults.

  5. Validation on an environment copy. We deploy a copy, run build, release, and rollback, and compare the resulting values with the expected ones — divergence is visible before reaching production stands.

  6. Handover of regulations. We provide documentation on variables, a “how to add a new one” instruction, ownership assignment, and the procedure for changing priorities.

What remains in the repository: schemes, roles, regulations

Automation lives not in scripts but in artifacts that can be read, verified, and handed over to another engineer.

Below is what remains in the repository and with the customer’s team: without these documents, the environment computation rule exists only in the author’s head and breaks at the first change in team composition.

  • Variable priority scheme: a resolution order table — a value from the environment configuration, from a linked group, from a rule by branch name, from the default value. With examples of which source wins and why.
  • Secret access policies: which roles read protected values, where they are masked in logs, which jobs get access only on protected branches.
  • Environment configurations: environment files with a complete list of values, marking of mandatory ones, and a ban on stubs in the production environment.
  • New environment regulation: request, environment creation, linking value groups, validation on a test run, enabling branch protection.
  • Pipeline documentation: stages, extension points, where the environment name is computed, and the procedure for adding new steps without editing logic.
  • Change regulation: who approves a priority change, how the history of rules is maintained, and how a value is rolled back.
  • Ownership map: variable, environment, responsible engineer, secret rotation period.

Such a set allows a new engineer to understand the logic in one pass through the repository, rather than through chat.

Case study: a monorepo with 40 services without manual variables

A monorepo with 40 services and three stands — dev, stage, prod. Environment values (registry address, cluster name, image tag, domain prefix) lived in manual project settings in GitLab CI — a separate copy for each service and each stand. Everything that did not match was discovered during deployment.

Before the transition, engineers spent around 30 settings edits per week: adding a service, renaming, moving to a new cluster.

About a fifth of build failures were not due to code but to value divergence between stands — a branch changed the tag, a neighboring service remained with the old prefix, and the chain failed at the deployment stage.

Now values are computed from the repository itself: the directory path and service manifest give the name and port, the branch and tag give the environment, and a shared template substitutes the rest.

There is a format check at the input, so an unknown template or name conflict stops the chain before the image build, not after.

Manual edits for a new service — zero: to add the forty-first, it is enough to create a directory from the template. There were no failures due to environment desync in a quarter, and adding a service dropped from forty minutes to five.

A whole class of failures is gone — a stand configuration that lagged behind the code after renaming or copying.

What to do if environments are already described manually?

Refusing a static description of environments during migration is not necessary — and is harmful. Even when computed values appear, the inventory remains a reference point: it shows what exactly changed, and you can return to it if a rule produces an incorrect result. Dynamic CI/CD variables do not cancel out static values; they override them where the rule applies.

The working scheme is two layers with explicit priority. The pipeline first tries to derive a variable from the build context, and if the rule did not work, it substitutes the value from the old environment file. The build either gets the computed value or behaves exactly as it did before the edits.

Compatibility with old builds rests on the fact that the manual manifest remains valid. We do not rewrite it entirely; instead, we mark fields as the default source and enable the new layer with a flag at the project or branch level.

Pipelines that have not been touched for a long time still build as before, until they are migrated deliberately.

The gradual transition goes one environment at a time. First, the rule is enabled in comparison mode: the computed value is written to the log next to the one specified by hand, and divergence is visible before it reaches a release.

When values match across several builds in a row, the manual entry is removed — and the rule becomes the only source.

This way releases do not stop: any environment can be migrated, rolled back, and migrated again without touching the others. After several iterations, no static files remain, and the change history shows when and why each value became computed.

Common technical doubts and our answers

Dynamic CI/CD variables raise three practical questions: how not to lose control over secrets, whether jobs will start interfering with each other during parallel runs, and what will remain with the team after handover. Below are answers without generalities.

Secrets and variables — where is the boundary?

A regular variable is part of the configuration: it is visible in settings, goes into change history, and is suitable for values computed from the launch context (branch name, tag, environment type).

A secret is stored separately, injected only at execution time, masked in the log, and not granted to jobs from repository copies or unprotected branches.

Dynamic computation and secrecy are not the same thing: a value can be computed on the fly and still remain sensitive, in which case its place is in a secure vault with limited visibility.

Will dynamic variables break parallel builds?

Each job gets its own environment and computes its values anew from its own context — there is no shared mutable state between jobs, so races and transferring values from one build to another are ruled out.

The exception is writing a result to an external shared resource: we cover such operations with separate rules and locks.

What will remain with the team after handover?

We hand over the configuration repository, a description of value computation rules, a set of environments, and instructions for adding new ones.

The customer’s team changes rules and expands the environment matrix without our involvement, while support follows an agreed regulation — with documentation updated whenever the logic changes.

Let’s discuss your environment scheme and come back with an estimate

For the estimate to be to the point and not “based on average figures,” we need a picture of your environments and the current build and deployment pipeline.

Dynamic variables in CI/CD are computed from facts about the environment — branch, release label, environment name, contents of manifests — so the quality of rules directly depends on how uniformly your environments are described. Below is what is worth sending or discussing before the estimate.

  • How many environments there are and how they differ: development, pre-production, production, separate sites for customers or branches.
  • Where variable values currently reside: in build system settings, in repository files, in a secret vault — and what part is set manually before launch.
  • How the environment is determined: by branch name, by release label, by selection at launch, by manifest directory.
  • Which secrets and accesses are tied to each environment and which teams should see them and which should not.
  • How many manual steps remain in the pipeline and at which stage deployment most often breaks.
  • Environment limitations: a closed environment with no external access, custom certificates, a separate image registry, a regulation on artifact retention periods.
  • Which checks should block deployment: tests, static analysis, security policies, approval before production.

It is useful to add access to the repository, configuration files, and the log of one run — it shows where variables are substituted implicitly.

Describe the task: we will come back with an estimate and a work plan indicating which variable computation rules we set up, what remains on your side, and how we validate the result on your own pipeline.