Skip to content

Flow a Change

With two deployments downstream of one base, a change now has a path to travel: base → dev → prod. In this step you'll flow a change through the whole graph, verifying it in dev before it reaches production — and see what happens when a deployment has an opinion of its own.

A local tweak in dev

Real deployments accumulate local differences. Suppose someone on the dev team renamed the chat title in dev while testing, and wants it to stay that way no matter what the base says later. Make that edit directly on the dev deployment:

cub function set --variant cubbychat/dev --resource Deployment/backend --protect \
    set-env-var backend CHAT_TITLE "Cubby Chat (dev)"

--protect records the paths this change writes as protected: they are dev's now, and a merge from the base leaves them alone. Without it the edit still lands, but it claims nothing — a later change to the same field upstream would simply overwrite it.

Change the base

Now the team ships two changes to the base: a company-wide rebrand of the chat title, and a scale-up of the backend.

cub function set --variant cubbychat/base --resource Deployment/backend \
    set-env-var backend CHAT_TITLE "Cubby Chat 2.0"
cub function set --variant cubbychat/base --resource Deployment/backend set-replicas 3

Both deployments are now behind the base. Neither has changed yet — promotion is per-deployment and deliberate.

Promote and verify in dev

Preview what would flow down, then promote:

cub variant promote cubbychat/dev --dry-run -o mutations
cub variant promote cubbychat/dev

Look inside the deployment

Look at the backend Deployment in dev, by the same name you changed it under:

cub resource data --variant cubbychat/dev Deployment/backend | grep -E "replicas:|CHAT_TITLE" -A1

The replica count picked up the base's change — it flowed to 3. But CHAT_TITLE is still Cubby Chat (dev), not the base's Cubby Chat 2.0. Dev protected that path, so the promote left it alone while merging in everything else.

What the promote couldn't apply

The rebrand didn't vanish; it was withheld, and the merge said so. A change a merge can't make is recorded on the deployment as a conflict, where it waits until someone decides:

cub variant conflicts cubbychat/dev
REASON           RESOURCE                        PATH                                 CHANGE
ProtectedPath    Deployment cubbychat/backend    ...env.?name=CHAT_TITLE.value        Update Cubby Chat 2.0

ProtectedPath is protection reporting itself. Nothing is broken and nothing is pending in the cluster — dev's data is exactly what dev asked for. What the conflict adds is that you can see what you turned down, rather than discovering months later that dev never got the rebrand.

Two things you can do with it. See what taking the base's value would mean, without taking it:

cub variant conflicts cubbychat/dev --apply --dry-run -o mutations

That prints the per-path change it would make — Cubby Chat (dev) → Cubby Chat 2.0 — and writes nothing: no revision, and the conflict is still outstanding afterwards. Drop --dry-run and it would land for real, replacing dev's title.

Dev wants to keep its title, so accept the state as it is and stop reporting it:

cub variant conflicts cubbychat/dev --dismiss

Dismissing changes no configuration data and leaves the protection in place — it clears the report, not the decision. The next promote that touches CHAT_TITLE will raise it again, which is what you want: each release gets its own look at what it is not taking.

Release it and check dev is healthy before letting anything near prod:

cub release publish cubbychat/dev

Check health:

cub release list --variant cubbychat/dev

Promote to prod

Preview first — production deserves a look before you promote:

cub variant promote cubbychat/prod --dry-run -o mutations

Prod never claimed the chat title, so nothing there is protected — it takes both changes: CHAT_TITLE becomes Cubby Chat 2.0 and the backend scales to 3. Then:

cub variant promote cubbychat/prod
cub release publish cubbychat/prod

The rebrand is live in prod and in dev — but dev still shows its own title. Same base change, two outcomes, each correct for where it landed.

The rhythm

That's the whole workflow, and it's the same whether you have two deployments or two hundred:

  1. Change the base once, with functions.
  2. Promote each deployment when it's ready, previewing the merge — the paths a deployment has protected stay as they are.
  3. Release each deployment to its target when you're ready for it to be live.

Each step is inspectable and each deployment moves at its own pace — a canary region first, the rest after; dev continuously, prod on approval.

Beyond one component

--variant and --resource name one variant and one resource. The same function runs across many with a selector instead: --where picks the units by their attributes, and --where-resource the resources within them. Scaling every production backend in the organization is the same command with the scope widened, and a preview before it runs:

cub function set --where "Space.Labels.Variant = 'prod'" \
    --where-resource "kind = 'Deployment' AND metadata.name = 'backend'" \
    --dry-run -o mutations set-replicas 3

In this tutorial that matches one deployment. In a fleet it is the point. See filters and bulk operations.

Next

Make and Undo a Change

Further reading