> ## Documentation Index
> Fetch the complete documentation index at: https://xanots.docs.xano.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Development workflows

> How XanoTS fits a solo developer, a team using git, an automated pipeline, and a team where the Xano workspace itself is the source of truth.

There are two places the truth about your backend can live, and the choice shapes everything
else:

* **Your repo.** TypeScript is authoritative; the workspace is a deployment target. Changes
  arrive through pull requests. This is flows 1–3.
* **Your Xano workspace.** The workspace is authoritative; code is pulled out, worked on, and
  pushed back. Changes arrive through **Xano branches**, and git may not be involved at all.
  This is flow 4.

Pick one per workspace. Both directions work, but only one of them is a merge, so a workspace
that is authoritative on Mondays and a deployment target on Tuesdays will lose someone's work.

## The three places XanoTS writes

| Target                         | Command                           | What it does                                                                             |
| ------------------------------ | --------------------------------- | ---------------------------------------------------------------------------------------- |
| An **ephemeral** environment   | `xanots deploy`                   | Disposable, auto-expiring, its own URL. A full replace every time. Where you try things. |
| A **branch** of your workspace | `xanots release --branch <label>` | Stages your logic beside the live branch without serving it.                             |
| The **live** branch            | `xanots release`                  | Production. Merges — adds and updates, never deletes unless you ask.                     |

Reading back out is `xanots init <dir> --from workspace`, which turns the live branch into
TypeScript. See [Pulling an existing workspace](/guides/codegen).

## What goes in git

For the flows where the repo is authoritative:

<Columns cols={2}>
  <Card title="Commit" icon="check">
    `xano/**` (your defs), **`xano.lock`**, `package.json`, and your frontend source.
    The lock pins every object's identity — without it a release can duplicate objects
    instead of updating them.
  </Card>

  <Card title="Never commit" icon="ban">
    `.xano/auth.json` (`xanots login --local` gitignores it for you), `.env`,
    `node_modules`, `frontend/dist`, and any exported bundle — a bundle carries your
    workspace's environment variable **values**.
  </Card>
</Columns>

<Warning>
  A pulled `xano/workspace.ts` carries environment variable values inline, because that is
  what a deploy sends. Read it before the first commit and move real secrets out.
</Warning>

## 1. Solo developer, repo is the truth

One person, `main`, one workspace. Branches in git are optional; the ephemeral is where you
try things.

<Steps>
  <Step title="Sign in once">
    ```bash theme={null}
    xanots login
    ```
  </Step>

  <Step title="Work, and deploy as often as you like">
    ```bash theme={null}
    git switch -c add-orders-api
    # edit xano/…
    xanots deploy ./xano/index.ts --static ./frontend/dist
    ```

    The ephemeral is disposable and expires on its own, so a bad deploy costs you nothing.
  </Step>

  <Step title="Prove it, then merge">
    ```bash theme={null}
    xanots test run-all          # against the ephemeral you just deployed
    git switch main && git merge add-orders-api
    ```
  </Step>

  <Step title="Release to production">
    ```bash theme={null}
    xanots release ./xano/index.ts --dry-run   # read the plan first
    xanots release ./xano/index.ts
    git add xano.lock && git commit -m "release"
    ```
  </Step>
</Steps>

<Tip>
  Always `--dry-run` first. It prints exactly which objects would be created, updated, or
  dropped — including any table column your schema no longer defines, which takes its data
  with it.
</Tip>

## 2. A team, all on XanoTS, repo is the truth

Same shape, plus review. What makes it work is that **everybody's `deploy` goes to their own
ephemeral**, so two people can be mid-change at once without touching each other or
production.

<Steps>
  <Step title="Everyone signs in to the same workspace">
    ```bash theme={null}
    xanots login          # pick the same instance + workspace at consent
    ```

    There is no `--workspace` flag — one credential addresses one workspace. `xanots
            workspace details` says which.
  </Step>

  <Step title="Branch, deploy, share a URL">
    ```bash theme={null}
    git switch -c justin/orders-api
    xanots deploy ./xano/index.ts --static ./frontend/dist
    ```

    Paste the printed URL into the PR. A reviewer gets a running backend *and* frontend to
    click through, not just a diff. `xanots ephemeral list` shows what everyone has up;
    `xanots ephemeral delete <tenant> --yes` cleans one up early.
  </Step>

  <Step title="Open the PR">
    Review the TypeScript diff like any other code. Two things a normal code review misses:

    * a **removed table column** or removed table — that is data loss on release;
    * a change to **`xano.lock`** that isn't a rename you expected.
  </Step>

  <Step title="Merge to main, then release">
    One person (or the pipeline in flow 3) releases from `main`:

    ```bash theme={null}
    git switch main && git pull
    xanots release ./xano/index.ts --dry-run
    xanots release ./xano/index.ts
    ```
  </Step>
</Steps>

**Renames need one extra step.** Renaming an object in code looks like a delete plus a create
unless you move its lock entry:

```bash theme={null}
xanots lock rename --entry=xano/index.ts function signup register
```

Commit the resulting `xano.lock` in the same PR as the rename.

**Merge conflicts in `xano.lock`** are normal — two branches each added objects. Take both
sides, then re-run `xanots export ./xano/index.ts` to rewrite the file cleanly and commit
that.

## 3. Automated: main is what production runs

Same commands, run by a machine. Any runner works — the requirement is three environment
variables, the credential shape that never expires or rotates:

```bash theme={null}
export XANO_INSTANCE_URL=https://your-instance.xano.io
export XANO_WORKSPACE_ID=3
export XANO_META_TOKEN=your-meta-api-token
```

<Warning>
  Never run `xanots login` in a pipeline — it blocks on browser consent. And set all three
  variables together; setting some is a hard error, not a quiet fallback.
</Warning>

**On every pull request** — build it, prove it, leave a live URL behind:

```bash theme={null}
npm ci
npx xanots export ./xano/index.ts --strict --frozen-lock   # warnings fail; lock must be committed
npm run build
npx xanots deploy ./xano/index.ts --static ./frontend/dist --test
```

`--strict` turns build warnings into failures. `--frozen-lock` fails if the export would
*change* `xano.lock`, which catches someone who forgot to commit it. `--test` runs the
deployed tests; a failing suite exits **5**.

**On merge to main** — reconcile production:

```bash theme={null}
npm ci
npx xanots release ./xano/index.ts --yes
```

Safe to run on every merge, and safe to re-run. When the workspace already matches the code,
nothing is sent at all — the summary reports `"upToDate": true` and no `updated_at` moves.
`--yes` skips the confirmation prompt but never skips the printed preview, so the log still
shows what changed.

**A staged pipeline** puts the merge on a branch and makes promotion a separate, human
button:

```bash theme={null}
npx xanots release ./xano/index.ts --branch staging --backup-branch --yes
# …someone looks at it…
npx xanots workspace branch set-live staging --yes
```

<Note>
  `--prune` (delete objects the project no longer defines) is deliberately absent above.
  Deletions are worth a human reading the plan: `xanots release ./xano/index.ts --prune --dry-run`.
</Note>

**Nightly, or before a release** — check the code against a real engine without touching
anything you own:

```bash theme={null}
XANO_VALIDATE_INSTANCE=… XANO_VALIDATE_TOKEN=… npx xanots preflight ./xano/index.ts --runtime
```

## 4. The workspace is the truth, and branches are the merge

This is the flow for a team that lives in Xano. Nobody keeps a long-lived repo. Everyone —
whether they write TypeScript, read XanoScript, or build in the no-code builder — does the
same four things:

1. **Take a copy** of what is live.
2. **Work on it**, trying it out on a disposable ephemeral.
3. **Push it back as a branch**, which stages it without serving it.
4. **Promote the branch**, which is the merge.

### First, what a Xano branch actually isolates

<Warning>
  **Branches scope logic, not data.** API groups, queries, functions, tasks, triggers,
  middleware, tools, toolsets, channels, realtime servers, knowledge, addons and messages each
  belong to a branch. **Tables and microservices do not** — one set is shared by every branch of
  the workspace.

  So a release that adds a column reaches production the moment it lands, even on a branch,
  and the plan describes it as routine. A branch stages your logic and shares your schema.
</Warning>

The CLI refuses rather than letting that happen quietly. `release --branch` reads the live
workspace, diffs the shared objects, and stops with the tables named:

```
--branch orders cannot stage 1 change to this workspace's SHARED schema:
  alter table "order" — added column: status
```

Your three answers: take the schema change out of this release; drop `--branch` because
applying it is what you wanted; or pass `--allow-shared-schema-changes` to stage the logic and
apply the schema knowing both happen. If the live workspace **could not be read**, that is
also a refusal — a failed read is not evidence that there is no schema change.

### The TypeScript developer, and the XanoScript developer

One flow, two artifacts. No repo — the working copy is a folder you delete when you are done.

<Steps>
  <Step title="Pull the live branch">
    ```bash theme={null}
    xanots init orders-work --from workspace
    cd orders-work
    ```

    The pull preserves each object's `guid`, so what you push back matches in place rather
    than duplicating. `xano/README.md` lists anything that did not translate cleanly — read
    it before you start editing.
  </Step>

  <Step title="Work on it against an ephemeral">
    ```bash theme={null}
    # edit xano/…
    xanots deploy ./xano/index.ts        # → a disposable environment with its own URL
    xanots test run-all
    ```

    Nothing here can reach production. Deploy as many times as you like.

    Reading XanoScript rather than TypeScript? The deployed ephemeral exports as a
    multidoc:

    ```bash theme={null}
    xanots ephemeral export <tenant> --format multidoc --name backend   # → backend.xs
    ```
  </Step>

  <Step title="Push it back as a branch">
    ```bash theme={null}
    xanots release ./xano/index.ts --branch orders --backup-branch
    ```

    Nothing serves `orders` yet — the live branch is untouched. `--backup-branch` snapshots
    the live branch's logic first, so the promote in the next step has something to go back
    to. The release refuses outright unless the instance confirms it planned against the
    branch you named, so an instance without branch support fails closed instead of
    overwriting production.
  </Step>

  <Step title="Review it in the builder">
    Open the branch in Xano and click through it. This is the review step — there is no diff
    to read, so someone has to look.
  </Step>

  <Step title="Promote — this is the merge">
    ```bash theme={null}
    xanots workspace branch list                    # which one is live right now?
    xanots workspace branch set-live orders
    ```

    A production cutover: the runtime stops serving the old branch and starts serving this
    one. Table data is unaffected either way, because it was always shared. The CLI names the
    outgoing branch and prompts before doing it (`--yes` in a script).
  </Step>

  <Step title="Clean up">
    ```bash theme={null}
    cd .. && rm -rf orders-work
    xanots workspace branch delete <old-branch> --yes
    ```

    Keep the branch you just replaced until you are confident — it is your rollback. The live
    branch cannot be deleted; promote another one first.
  </Step>
</Steps>

### The builder developer

Someone building in the Xano no-code UI does steps 1, 2 and 3 in the builder itself: they
work on a branch in Xano rather than on the live one, and click through it there. Then the
last step is identical, from the builder or the CLI:

```bash theme={null}
xanots workspace branch list
xanots workspace branch set-live their-branch
```

They never pull, never install anything, and never touch an ephemeral. The whole team meets
at the same place — a branch, promoted to live — which is why this arrangement works and the
mixed git/UI one does not.

### Rolling back

The branch you replaced is still there, so undoing a promote is one command:

```bash theme={null}
xanots workspace branch set-live <previous>
```

That restores **logic**. It does not restore tables, because tables were never on a branch —
a dropped column is gone from every branch at once. Read the shared-schema report before you
release, not after.

<Note>
  `v1` is the label every workspace starts with, and it is reserved — `--branch v1` is refused
  rather than silently writing to whatever is live. Pick your own labels.
</Note>

## Other arrangements

<AccordionGroup>
  <Accordion title="Adopting a workspace into a repo">
    Going the other way — a workspace that exists, becoming a repo that is authoritative.
    Pull it, commit it, and capture its identities before you ever release:

    ```bash theme={null}
    xanots init my-app --from workspace
    cd my-app && git init && git add -A && git commit -m "adopt workspace"
    xanots workspace export --path live.json
    xanots lock import live.json --entry=xano/index.ts --yes
    git add xano.lock && git commit -m "pin live identities"
    ```

    The engine assigned those objects random guids. Nothing in your code can re-derive them,
    so this `xano.lock` is irreplaceable — losing it means the next release duplicates every
    object instead of updating it. From here you are in flow 2, and the workspace stops being
    a place people edit.
  </Accordion>

  <Accordion title="A preview environment per pull request">
    Nothing extra to configure: a PR job already deploys to an ephemeral and prints the URL.
    Post it as a PR comment and let it expire on its own, or tear it down on merge:

    ```bash theme={null}
    xanots ephemeral list
    xanots ephemeral delete <tenant> --yes
    ```
  </Accordion>

  <Accordion title="A hotfix someone made in production by hand">
    In a repo-is-the-truth workspace this is a divergence: it exists live and not in your
    code, so the next release does not know about it and a `--prune` would remove it. Get it
    into git the same day:

    ```bash theme={null}
    git switch -c hotfix-from-builder
    xanots init . --from workspace
    git diff                     # should show only the hotfix
    ```

    Then PR it. If the diff shows more than the hotfix, someone else has been in there too.
  </Accordion>

  <Accordion title="An AI builder (Bolt, Lovable) driving the backend">
    The agent authors TypeScript and deploys to ephemerals, so it slots into flow 1 or 2 as a
    contributor: it works on a branch, you read the diff, you merge. Keep `xanots release` off
    the agent's path — let it deploy freely and let a human release. See
    [Bolt](/connectors/bolt) and [Lovable](/connectors/lovable).
  </Accordion>

  <Accordion title="Several backends in one repo">
    Each entry file is its own workspace and gets its own credential, its own `xano.lock`
    beside it, and its own release. Point every command at the entry explicitly:

    ```bash theme={null}
    xanots deploy ./services/orders/index.ts
    xanots release ./services/billing/index.ts --dry-run
    ```

    `lock rename` and friends look for `xano.lock` in the *current directory* unless you pass
    `--entry=<path>`, so always pass it in a multi-backend repo.
  </Accordion>
</AccordionGroup>

## The rules that survive every flow

1. **Pick one source of truth per workspace** and say so out loud. The repo, or the workspace.
2. **Ephemerals are free; production is not.** Iterate on `deploy`, promote with `release`.
3. **`--dry-run` before a release**, and read the column drops.
4. **A branch stages logic and shares schema.** If the shared-schema check refuses, it is
   telling you something true.
5. **`xano.lock` is committed, always** — wherever a repo is authoritative. It is what makes a
   release update instead of duplicate.
6. **Credentials never enter git.** People use `xanots login`; machines use the three
   environment variables.
