Supabase branching is good. If you build on Supabase and you’re not using it, you should be. It gives you a real preview database per branch, applies your migrations to it, and merges those migrations back when you’re done. For the develop-a-feature-against-a-safe-copy workflow, it does exactly what you want.

This post isn’t an argument against branching. It’s about the jobs branching doesn’t do — the ones that still bite you after your branch merges cleanly. LeafSQL is built for those jobs, and the honest framing is that the two are complementary, not competing. Branching gives you isolated environments to build in. LeafSQL watches and compares those environments over time.

What branching does well

Give branching its due, because the comparison only makes sense if you’re fair about it:

  • It provisions preview databases. Every branch gets its own Postgres instance, seeded and isolated, so you can develop and test without touching production.
  • It applies migrations. Your migration files run against the branch the same way they’ll run against prod, so you catch migration errors in the branch instead of the deploy.
  • It merges cleanly. When the branch is ready, the migrations promote forward as part of the normal Supabase flow.

That’s a real workflow and a genuinely good one. The gaps aren’t failures of branching — they’re a different problem branching was never designed to solve.

What branching doesn’t solve

It doesn’t catch out-of-band drift. Branching governs changes that go through migrations. It has nothing to say about changes that don’t. When someone opens the SQL editor and runs this against production to unblock a ticket:

-- Run directly in the SQL editor on prod. No branch, no migration file.
alter table public.orders add column refunded_at timestamptz;

that column now exists in prod and in no migration, no branch, and no other environment. Branching can’t flag it, because the change never entered the flow branching governs. This is the most common way production diverges, and it’s invisible to a branch-based model by construction. We wrote about detecting exactly this in how to detect schema drift in Postgres.

It doesn’t diff arbitrary environments with stored history. A preview branch tells you the branch is a copy of production at the moment you branched. It doesn’t let you ask “how does staging differ from prod right now,” and it doesn’t keep a versioned history of each environment’s shape that you can compare across time. Once a branch is merged or discarded, its schema state is gone.

It doesn’t push a migration PR from a diff. When two environments have drifted apart, branching gives you a place to develop the fix, but it doesn’t hand you the reconciling migration. You still write the forward and rollback SQL yourself, then open the PR yourself.

How LeafSQL fills those gaps

LeafSQL takes a versioned snapshot of each environment’s schema — a leaflet — and keeps it per environment: dev, staging, prod. Because those snapshots are stored and hashed rather than thrown away, three things become possible that a preview branch doesn’t offer:

  • Drift detection. LeafSQL re-snapshots each environment on a schedule and compares the new snapshot’s hash against the last known-good leaflet. When they diverge, that’s an out-of-band change, and it surfaces as a drift event in the app instead of waiting for a failed deploy to reveal it.
  • Environment comparison with history. You can diff the latest leaflets of any two environments and see how their tables and columns differ — a stored comparison, not a one-time export you have to re-run.
  • Migration PRs from a diff. Once you can see the difference, LeafSQL can turn it into forward and rollback SQL pushed to your GitHub repo as a pull request you review and merge.

Branching vs LeafSQL, side by side

JobSupabase branchingLeafSQL
Spin up an isolated preview databaseYes — core featureNo — not what it does
Apply migration files to an environmentYesNo — it proposes them
Catch schema changes made outside migrations (drift)NoYes — drift events per environment
Diff two arbitrary environments with stored historyNoYes — table and column level today
Push a reconciling migration PR from a diffNoYes — forward + rollback SQL to GitHub

The honest boundaries

Two things worth stating plainly, because the table above would be dishonest without them.

LeafSQL’s environment comparison and migration generation are table-and-column level today — tables, columns, types, nullability, defaults, and views. Diffing indexes, constraints, foreign keys, triggers, and functions is on the roadmap, not shipped. If your divergence is in an index or a check constraint, LeafSQL won’t surface it yet, and you’ll want the pg_catalog queries from our environment-diff guide instead.

And to be clear about the relationship: none of this replaces branching. If you use Supabase branching to build features, keep using it. LeafSQL sits alongside it, watching the environments branching creates and promotes, and catching the changes that happen between and outside those branches. The generated migrations, incidentally, are proposed and never applied silently — the same propose-don’t-execute posture we hold for AI-written SQL.

Which one you need

If your only question is “how do I develop against a realistic copy of my database,” branching answers it and you may not need anything else. If your questions are “did prod drift,” “how do these two environments actually differ,” and “can I get the reconciling migration as a reviewable PR,” those are the ones branching leaves open — and the ones LeafSQL was built to close. Most teams that ship on Supabase eventually have both kinds of question. Using both tools is the point.