dbshift

Type:

Open source

Type:

Open source

Status:

Active

Status:

Active

Role:

Author

Role:

Author

Stack:

Node.js, TypeScript, PostgreSQL

Stack:

Node.js, TypeScript, PostgreSQL

Year:

2023–present

Year:

2023–present

Why it exists

In early 2023 I was working on Caspian and needed to add a column to a table. I reached for the migration tool we were using and spent twenty minutes reading documentation before I'd written a single line of SQL. The tool had opinions about folder structure, a config file I didn't have, and a migration DSL I didn't know.

I wrote the SQL migration by hand and ran it manually. Then again for staging. Then forgot to run it in one environment and spent an afternoon debugging something that wasn't an issue. There had to be a better way. The better way was, apparently, something I had to build myself.

How it works

The constraints I set: one command to create a migration file, one to apply pending migrations, one to check status. Plain SQL files — no DSL. State tracked in the database itself, not a separate lockfile. Zero config.

$ npx dbshift create add-user-roles
✓ Created: migrations/20250312_add-user-roles.sql

$ npx dbshift up
✓ Applied: 20250312_add-user-roles.sql

$ npx dbshift status
✓ 3 applied · 0

$ npx dbshift create add-user-roles
✓ Created: migrations/20250312_add-user-roles.sql

$ npx dbshift up
✓ Applied: 20250312_add-user-roles.sql

$ npx dbshift status
✓ 3 applied · 0

$ npx dbshift create add-user-roles
✓ Created: migrations/20250312_add-user-roles.sql

$ npx dbshift up
✓ Applied: 20250312_add-user-roles.sql

$ npx dbshift status
✓ 3 applied · 0


Migration state is tracked in a single table dbshift creates in your database on first run. Each migration is a plain .sql file in a /migrations directory. No metadata files, no lock files, no daemon.

What I didn't expect

I posted it on a few forums expecting nothing. The use case I hadn't designed for — running it in CI pipelines against ephemeral test databases — turned out to be the most common one. Three contributors I've never met submitted PRs that are now in production. That's the version of success I care about.

A decision I'd revisit

The configuration API is slightly inconsistent between CLI flags and config file options. I shipped v1 without a config file at all — pure CLI — and added config file support in v1.2 in a way that doesn't quite mirror the CLI surface. It's a small thing that becomes load-bearing when someone has 200 migration files and needs to change their database URL. I'd design the config surface first next time.

Outcome

1.4k GitHub stars. Consistent npm download numbers week over week — not a spike-and-drop pattern, which means people are actually using it rather than starring and forgetting. A team at a company I'd never heard of filed an issue referencing dbshift in their production deployment. That's the number I actually care about.

Create a free website with Framer, the website builder loved by startups, designers and agencies.