Stop Fighting Your Database When Switching Git Branches

Database-per-branch workflow in Ruby on Rails using the BranchDb gem

Published

Published

Feb 4, 2026

Feb 4, 2026

Topic

Topic

Engineering

Engineering

Written by

Written by

Ali Fadel

Ali Fadel

Here's something that happens to every Rails developer eventually.

You're working on a feature branch. You add a migration, run it, everything works. Then your teammate asks you to review their PR. You switch branches. Suddenly your schema.rb shows changes you didn't make. Changes from your feature branch are now bleeding into their branch.

You know you shouldn't commit it. But now you're in a weird state. Your database has columns that don't exist in this branch's code. Things break in confusing ways.

Most developers just deal with it. They run db:migrate, hope for the best, maybe manually roll back migrations. Some teams have elaborate wiki pages explaining the "correct" way to switch branches. None of it works reliably.

The actual problem is simple: one database, many branches. That's a fundamental mismatch.

The Obvious Solution Nobody Uses

What if each branch had its own database?

main branch      myapp_development_main
feature-auth     myapp_development_feature_auth
feature-payments myapp_development_feature_payments

Switch branches, restart your server, you're using a completely isolated database. No schema bleed. No migration conflicts. No weird states.

This isn't a new idea. Developers have been manually creating separate databases for years. But it's tedious. You have to remember to create the database, clone the data, keep track of which branch uses which database. So people don't do it.

The friction kills the solution.

Feature

The "Old Way" (Single Shared DB)

The "BranchDb Way" (Isolated DBs)

Migration State

Bleeds between branches 

Fully isolated per branch 

Schema.rb Integrity

Shows ghost changes from other branches 

Always matches your current code 

Destructive Testing

Risky; affects all work 

Safe; stays within one branch 

Context Switching

High mental overhead 

Zero effort; "just works" 

Disk Usage

Minimal (one DB) 

Higher (clones for each branch) 

Tooling Required

Manual rollbacks & wiki pages 

Automated Ruby gem 

Making It Automatic

We built BranchDb at MilkStraw AI to remove that friction entirely.

Add it to your Gemfile. Run the generator. Update your database.yml with one line of ERB. That's the setup.

development:
  database: <%= BranchDb.database_name('myapp_development') rescue 'myapp_development'

The rescue guard matters. BranchDb only loads in development/test, so production needs a fallback. Without it, your app won't boot in production.

Now when you run rails db:prepare on a new branch, it automatically clones from your parent branch. Not from main (unless you branched from main). From wherever you actually branched from. This cloning only happens for development databases. Test databases use standard Rails schema loading.

This matters more than you'd think.

Say you're working on feature-auth. You've added users, set up test data, run several migrations. Now you need to branch off to fix a bug in that feature. You create feature-auth-hotfix.

With BranchDb, your new branch gets a clone of feature-auth's database. All your test data, all your migrations, ready to go. You're not starting from a bare main branch and re-running everything.

How It Actually Works

The core is simple. BranchDb hooks into Rails' db:prepare task. When that task runs, it checks:

  1. Does this branch's database exist?

  2. Does it have a schema_migrations table?

If no to either, it looks for a source to clone from. First it checks git reflog to find your parent branch. Reflog analysis isn't perfect. Shallow clones in CI or complex rebase histories can break detection. When that happens, or when the detected parent's database doesn't exist, it falls back to main. You can also force a specific parent with BRANCH_DB_PARENT=some-branch rails db:prepare.

Then it uses PostgreSQL's native tools pg_dump and psql (pg_dump | psql). Fast, reliable, streaming the output directly so it doesn't consume RAM. Because it clones the actual database (not your schema file), it captures everything: tables, indexes, custom types, extensions. Works the same whether you use schema.rb or structure.sql.

If you're on main, or if no source database exists, it just defers to Rails' normal behavior. Load schema, run migrations, seed. Nothing magical.

The whole thing is under 400 lines of Ruby.

The Cleanup Problem

Branches pile up. So do databases.

After a few weeks you might have twenty branch databases sitting in PostgreSQL. Most for branches you've already merged and deleted.

BranchDb gives you three commands:

rails db:branch:list lists all branch databases.

rails db:branch:prune removes databases for git branches that no longer exist. If you deleted the branch, it deletes the database.

rails db:branch:purge is more aggressive. It removes everything except your current branch and main.

Both prune and purge ask for confirmation. Both skip databases with active connections. Both protect your current work.

What This Changes

The shift is subtle but significant.

Before: switching branches is a context-loading exercise. You're thinking about database state, migration order, whether you need to reset anything.

After: switching branches is just switching branches. The database follows automatically.

You stop treating your development database as precious. Need to test something destructive? Just do it. Switch to another branch and you're back to clean state. Switch back and your destructive changes are still there, isolated.

It sounds small. But removing that mental overhead adds up. Every branch switch that doesn't require database thinking is a small win. Those wins compound.

The Tradeoffs

Nothing is free.

Disk space is the obvious one. Each branch database is a full copy. If your development database is 500MB, ten branches means 5GB. For most projects this is fine. Disk is cheap and you can prune regularly.

Server restarts are required. The database name is determined when Rails boots (ERB in database.yml evaluates once). Switch branches without restarting and you're still connected to the old database. This catches people at first. If you're using bin/dev or Overmind, the restart is quick. Some teams wire up a post-checkout git hook to restart automatically. You get used to it either way.

PostgreSQL only. The cloning uses pg_dump and psql. Supporting other databases would mean different tooling for each. PostgreSQL covers most Rails projects, so that's where we started. If you're running Postgres in Docker, it just works. BranchDb talks to whatever Postgres your database.yml points to. No special volume handling needed.

Getting Started

# Gemfile
group :development, :test do
  gem 'branch_db'
end
bundle install
rails generate branch_db:install

Update database.yml:

development:
  database: <%= BranchDb.database_name('myapp_development') rescue 'myapp_development' %>

test:
  database: <%= BranchDb.database_name('myapp_test') rescue 'myapp_test'

Run rails db:prepare. You now have a branch-specific database.

That's it. No configuration required for most projects. If your main branch is called master instead of main, there's a config option for that. If you need shorter database names, you can limit the branch suffix length. But the defaults work for most teams.

What's Next

BranchDb solves the core problem, but there's more we want to build.

SQLite and MySQL support. Right now it's PostgreSQL only. We want to abstract the cloning layer so the same workflow works regardless of your database. SQLite would be trivial (it's just a file copy). MySQL needs mysqldump integration.

Standalone clone task. Sometimes you want to manually clone from a specific branch: rails db:branch:clone FROM=feature-x. Useful when the automatic parent detection isn't what you need.

Database info task. rails db:branch:info to show your current branch, database name, size on disk, and detected parent. Quick sanity check without digging through configs.

Clone progress indicator. For large databases, pg_dump | psql can take a while. Visual feedback would help.

Disk usage report. rails db:branch:list --size to see how much space each branch database is consuming. Makes cleanup decisions easier.

If any of these would help your workflow, open an issue. We're building based on what teams actually need.

The Bigger Point

Developer tools should remove friction, not add process.

The database-per-branch pattern isn't new. What's new is making it automatic. Making it the default behavior rather than something you have to remember to do.

Most developers won't manually create branch databases. The friction is too high for a problem that feels minor (until it isn't). But if the right thing happens automatically? Everyone benefits.

That's the bar for developer tooling. Not "possible if you follow these steps." Automatic. Invisible. Just works.

BranchDb tries to meet that bar. You install it, update one config file, and forget about it. Branch databases just happen. Cleanup is one command away.

The best tools disappear into your workflow. You stop noticing them because they stop causing problems.

That's the goal.