schemachange applies versioned SQL to Snowflake like a Flyway migration runner
A Database Change Management tool for Snowflake
At a glance
- What is it?
- schemachange is a Python change management tool for Snowflake that takes an imperative approach to database migrations, running numbered SQL scripts once and tracking them in a change history table. It is community developed rather than an official Snowflake product, and its model is versioned scripts rather than a declarative desired-state diff.
- Who is it for?
- Adopt schemachange if you run Snowflake and want database changes reviewed in version control and applied through a pipeline, and if you are willing to write your migrations as ordered SQL scripts rather than as a declarative description of the schema you want.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
An imperative migration runner, not a declarative diff
schemachange is described as a simple python based tool to manage all of your Snowflake objects, and it follows an Imperative-style approach to Database Change Management. The inspiration is named explicitly: the Flyway database migration tool. The project also carries a rename notice, since snowchange has been renamed to schemachange, which tells you the lineage and the reason to search for one name and not the other.
The imperative versus declarative split is the design decision that matters most here. A declarative tool reads the current state of the schema, compares it to a desired state you declare, and computes the difference. An imperative tool runs the changes you wrote, in an order you chose, and remembers which ones have run. schemachange sits firmly in the second camp.
That choice has consequences. A declarative tool can tell you what has drifted. An imperative one cannot, because it has no model of the target state, only a ledger of executed scripts. In exchange, the imperative approach gives you exact control over the SQL that runs, which matters when a change involves a backfill, a data fix or a statement that has to be written a particular way for Snowflake specifically.
The project places itself in a delivery pipeline rather than in the terminal. Combined with a version control system and a CI/CD tool, the README says database changes can be approved and deployed through a pipeline using modern software delivery practices.
Three script kinds decide how each file is applied
The change script taxonomy is the core mechanism. The README's own next steps point at it first, calling it Change Scripts and describing versioned, repeatable and always scripts, and the table of contents lists the naming rules for each.
A versioned script runs exactly once. The versioned script naming convention carries a version, and the example the README gives creates a file named V1.0.0__initial_setup.sql containing a schema and table creation. Once that version is recorded in the change history table, the script is never executed again, even on a later deploy, which is the property that makes the runner safe to re-run.
A repeatable script runs on every deployment. This is the escape hatch for views, grants, functions and anything else that has to be refreshed rather than created once, and the naming convention distinguishes it from a versioned file.
An always script also runs on every deployment, and the two are differentiated by how they interact with the version ledger, which is exactly the kind of detail that matters when you are deciding which bucket a given statement belongs in.
The scripts are SQL files in a directory you point at with the -f flag, and the repository carries a demo directory with several worked examples, including a basics demo, a citibike demo, a citibike jinja demo and a cli demo, plus provision and teardown YAML configs. Those demos are the fastest way to see the conventions applied to something realistic.
Install, write a script, deploy, and record what ran
The quick start is four steps, and the first one is a single pip install:
pip install schemachangeStep two creates the migrations directory and the first script, using a heredoc to write the SQL file in one go:
mkdir -p migrations
cat > migrations/V1.0.0__initial_setup.sql << 'EOF'
CREATE SCHEMA IF NOT EXISTS my_app;
CREATE TABLE IF NOT EXISTS my_app.customers (
id INTEGER,
name VARCHAR(100)
);
EOFStep three is the deploy, and the README gives three ways to supply credentials. Option A uses environment variables and is marked as recommended for CI/CD:
export SNOWFLAKE_ACCOUNT="myaccount.us-east-1.aws"
export SNOWFLAKE_USER="my_user"
export SNOWFLAKE_PASSWORD="my_password" # Or use a PAT token
export SNOWFLAKE_ROLE="MY_ROLE"
export SNOWFLAKE_WAREHOUSE="MY_WH"
export SNOWFLAKE_DATABASE="MY_DB"
schemachange deploy -f migrations --create-change-history-tableOption B passes the same values as CLI arguments for quick tests, with the password still coming from the environment:
schemachange deploy \
-f migrations \
--create-change-history-table \
-a myaccount.us-east-1.aws \
-u my_user \
-r MY_ROLE \
-w MY_WH \
-d MY_DBOption C uses a named connection from a connections.toml file in the snowflake config directory, which the README positions for local development:
schemachange deploy -f migrations -C my_connection --create-change-history-tableStep four verifies what happened, first through the tool and then through the Snowflake CLI:
schemachange verify -f migrationssnow sql -q "SELECT * FROM metadata.schemachange.change_history ORDER BY installed_on DESC LIMIT 5;"One detail in the deploy step catches people out, and the README flags it: the create-change-history-table option creates the tracking table automatically on first run, but the target database, which defaults to METADATA, must already exist in Snowflake. The tool creates the schema and the table, not the database.
The change history table is the state of the deployment
Everything schemachange knows about your database comes out of one table. The verification query shows its name and location, metadata.schemachange.change_history, with rows ordered by installed_on. The tip in the quick start says that to store the change history in a different database, you pass the fully qualified name MY_DB.SCHEMACHANGE.CHANGE_HISTORY to the -c flag.
That is a small but consequential design choice. The tool has no other state, so the table is the ledger of which versions ran and when. If two environments point at different databases, they have different ledgers, which is exactly what you want for a pipeline that promotes the same scripts through a promotion path. It also means the table must be treated as production data: a pipeline that drops and recreates the database will also lose the record of what ran, and the tool will then try to apply versioned scripts that have already been applied.
The deploy command, render command and verify command are the three verbs the README documents in its commands section. Render is the one to look at before trusting a deploy, since it shows what the tool would execute without executing it. The presence of a separate verify command also means you can assert the expected state in a pipeline step without running anything.
The change history table is described as a section of the README in its own right, and the changelog is linked from the overview for the complete list of changes made to schemachange, which is the right place to look before an upgrade.
Jinja templating and secret filtering inside scripts
Snowflake objects rarely need to be static, so the scripts support variables and a templating engine. The README has a Using Variables in Scripts section with a sub-section on secrets filtering, and a Jinja templating engine section, plus a gotchas section. A YAML config file is also documented, and the demo directory includes a citibike_jinja demo and a schemachange-config-v2-example.yml, so the templated configuration path is exercised by the examples rather than only described.
The YAML config file is also Jinja-aware, which the table of contents labels as Yaml Jinja support. That matters in CI, where the config file is usually generated, and a templating engine inside the config file lets one file hold environment-specific values without being rewritten by hand.
The secrets filtering sub-section is the part to read before you start passing values through. Scripts that take variables from a config or from the environment are exactly where a credential can end up in a change history row, in a CI log, or in a rendered artifact that gets committed. The presence of a dedicated filtering feature suggests the project has thought about which values should not be echoed into the record.
The gotchas section is a signal in itself. A tool that renders SQL before executing it has a small number of sharp corners, such as quoting behaviour and how Jinja interacts with the SQL parser, and the fact that the README has a dedicated section for them is a point in the project's favour.
Authentication, configuration priority and the required privileges
The authentication section lists five methods: password authentication, external OAuth, external browser authentication, Okta, and private key authentication. The quick start points you at JWT, PATs or SSO instead of passwords for securing a setup, and the demo provision directory suggests there are scripts for setting up a role with the right grants.
Configuration can arrive four ways, and the README is explicit about which one wins. There is a connections.toml file in the snowflake config directory, a YAML config file, and environment variables. The table of contents lists a Configuration Priority section, and the account identifier format has its own section, so an account name like myaccount.us-east-1.aws is a documented format rather than a guess.
The required Snowflake privileges section is the one that will bite during setup. A migration runner needs more than a role that can create tables, because it also writes to the change history table, and because your scripts may create schemas, roles or warehouses. The demo provision and teardown YAML files in the repository are the intended way to see what that grant list looks like, and a reviewer should read them before running a deploy against a shared environment.
Deployment scenarios have their own section with an Out Of Order entry, which tells you the tool has a defined behaviour when versions arrive out of sequence, and there is a whole section on upgrading to 4.1.0, which suggests version transitions have needed attention.
What it is not, and what to check before you rely on it
The limitation that gets stated in the README itself is the important one. schemachange is a community-developed tool, not an official Snowflake offering, and it comes with no support or warranty. For a team that has to answer questions about a production migration at short notice, that is a procurement fact rather than a technical one, and it belongs in the evaluation rather than in a footnote.
The technical limitation follows from the imperative model. Because the tool executes your scripts in order and records that they ran, it cannot tell you that the live schema no longer matches what your scripts assume, and it has no way to reverse a change. Rollback is not mentioned anywhere in the documented commands, which are deploy, render and verify. If a script drops a column, undoing it means writing and applying a new script.
A second constraint is the scope of the platform. This is a Snowflake tool, not a general database migration tool, and the readme's emphasis is entirely on Snowflake objects, Snowflake authentication and Snowflake privileges. If your estate includes a Postgres or SQL Server database that also needs migrating, you will be running a second tool alongside it.
The alternative approach is the declarative one, and the difference is worth being concrete about. A declarative migration tool computes the diff between the live schema and a declared target, so drift is visible and the desired state is the single source of truth. schemachange instead takes your ordered SQL as the source of truth and executes what you wrote, which gives you exact control over Snowflake-specific statements and lets a script do work, such as a data backfill, that a schema diff cannot express. The Flyway comparison in the README points at the same trade-off in a different ecosystem.
On the mechanics of the project itself, the package is built with hatchling, requires Python 3.10 or later, and declares classifiers for Python 3.10 through 3.13. The pyproject.toml version reads 4.4.0 while the newest tagged release in the release history is v4.3.3, dated 2026-04-20, so the main branch is ahead of the last tag. The licence is Apache-2.0, with a LICENSE, a NOTICE and a separate LEGAL.md in the repository root. The last push was on 2026-10-19, and the repository is not archived. A Dockerfile and a Dockerfile-src are both present, with the first simply installing the package from PyPI on a python:3.12 base and setting schemachange as the entrypoint.
Editorial conclusion
Adopt schemachange if you run Snowflake and want database changes reviewed in version control and applied through a pipeline, and if you are willing to write your migrations as ordered SQL scripts rather than as a declarative description of the schema you want. Do not adopt it if you need a tool that computes the difference between two states, because the approach is imperative and the ordering of your scripts is the source of truth, and do not treat it as a Snowflake product, since the README states it is community developed and comes with no support or warranty. Verify first that the database you target already exists, because the tool creates the schema and the tracking table but not the database, that the role you use has the privileges the deploy needs, and that the version you install matches the 4.4.0 in pyproject.toml rather than assuming the v4.3.3 tag is the latest code.
Frequently asked questions
How do I install schemachange and run my first migration?
Install it with pip install schemachange, create a migrations directory with a versioned script such as V1.0.0__initial_setup.sql, then run schemachange deploy -f migrations --create-change-history-table. The target database must already exist in Snowflake, because the tool creates the schema and the tracking table, not the database.
What is the difference between versioned, repeatable and always scripts?
A versioned script runs exactly once and is recorded in the change history table, a repeatable script runs on every deployment, and an always script also runs every time but is treated differently in relation to the version ledger. The README's own next steps point at the Change Scripts section first.
Is schemachange an official Snowflake tool?
No. The README states that schemachange is a community-developed tool, not an official Snowflake offering, and that it comes with no support or warranty. It is published on PyPI under the Apache-2.0 licence.
Which authentication methods and configuration sources does schemachange support?
The documented authentication methods are password, external OAuth, external browser, Okta and private key, and the quick start recommends JWT, PATs or SSO for a secured setup. Configuration can come from a connections.toml file, a YAML config file or environment variables, with the README documenting the priority order between them.
Does schemachange support rollback of a deployment?
The documented commands are deploy, render and verify, and no rollback command is listed. Since the tool follows an imperative approach and records executed versions in the change history table, undoing a change means writing and applying a further script.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/snowflake-labs-schemachange)Was this useful?