--set-live, and deleting the
Xano branch it landed on does not undo it. Switching live back to an earlier Xano branch restores
logic, never a dropped column or its values.
Plan every table change as a change to production data.
What lands, and what is refused
promote compares the release’s tables with your workspace before it lands anything:
A refusal exits
2 with SDK_SHARED_SCHEMA_CHANGE and lists every change, additions included,
under details.changes. --yes does not waive it.
deploy --to workspace --branch <label> refuses the same way, because a Xano branch stages
logic and not schema. A plain deploy --to workspace onto the live Xano branch previews the
change instead: it names each column it would drop and asks before it sends anything.
On a tenant there are no Xano branches, and a tenant deploy replaces what the tenant serves:
- A table the release does not carry is dropped with every row. The confirmation names each one with its row count.
- A dropped column loses its values. A retyped column’s values are converted (
"1.5"becomes2), and a column made required has its nulls rewritten to the type’s empty value.
--yes skips that confirmation. In CI nobody reads it, so review the change before the tag.
See the change before it lands
promote and tenant deploy run no local plan. The merge path does, and --dry-run sends
nothing:
droppedColumns in the plan (--json carries it). Every column listed there loses its
values when the release lands.
Rename or drop a column across two releases
A column dropped beside a column added is not a rename. Columns have no rename identity, so the old values are destroyed and the new column starts empty.promote calls the pair out under
details.pairedColumns.
To keep the data, spread the change over two releases: expand, backfill, contract.
Say users.name becomes users.display_name:
1
Expand: add the new column (release v1.4.0)
Add A new column is an addition, so
display_name beside name. Write both columns, and read display_name, falling back
to name while it is empty:promote v1.4.0 lands it without the flag, and a tenant
deploy keeps every existing value.2
Backfill: copy the values
With v1.4.0 serving, copy
name into display_name for every row where display_name is
empty, for example with a one-off task. Check the count of
rows still empty is zero before you go on.3
Contract: drop the old column (release v1.5.0)
Remove
name from the schema and stop reading it. The drop loses only values you already
copied:display_name, and the column keeps its
values. On a tenant, deploying v1.3.2 again is a replace, so it drops display_name with the
values you backfilled; name still holds them, and the backfill can run again. Once v1.5.0
lands, name and its values are gone whatever you roll back to.
The same three steps cover a retype (add a column of the new type, backfill it, drop the old one)
and a column made required (backfill the nulls first, then require it).
When to pass --allow-shared-schema-changes
Pass it on the run that lands the contract step, after someone has read droppedColumns and
checked that nothing still reads the column. Do not put it in a workflow that runs on every tag:
the flag is the only point in the release flow where a person confirms that live data changes.
CI/CD shows how the pipeline hands that run to a
person.