Version 4 keeps migration execution explicit
Migrate has one focused job: read numbered migration files and apply them in order. The CLI supports up, down, goto, step, drop, and version repair, while the version 4 Go package puts the same engine inside an application. Drivers avoid correcting user input behind the scenes. That restraint makes deployment behavior easier to reason about, provided your team already knows how to write safe database changes.
The current README lists 21 database-driver entries, covering PostgreSQL and PGX, MySQL or MariaDB, SQLite, MongoDB, Cassandra or ScyllaDB, SQL Server, and several cloud or distributed systems. Ten source entries load migrations from local files, Go filesystems, embedded bundles, Git hosts, S3, or Google Cloud Storage. The CLI can therefore remain constant while storage and database choices differ across services.
Two files define each reversible migration
A logical migration normally has an up file and a down file with matching integer or timestamp versions. Migrate sends their contents to the selected database driver with little format checking, so there is no private migration language to learn. The same simplicity leaves SQL quality, reversibility, and destructive-operation review with the application team. Irreversible changes may use a comment-only down file, while a zero-byte file can cause trouble because the tool attempts an empty query.
The getting-started guide recommends running every change up, down, and up again before committing it. Several commands within one migration need an explicit transaction when the database supports one. That advice is more than ceremony: database engines disagree about transactional schema changes, and migrate does not flatten those differences into a universal promise. Teams should test against the same database family and version they deploy, especially for changes that rewrite data or lock large tables.
What happened when we ran it
Our run installed 457 packages in 68 seconds and completed the build in 174 seconds. The measurement used commit 504568a in a fresh unprivileged Debian container with 3 CPUs, 8 GB of RAM, and Go 1.24. The checkout had 405 files, roughly 23,476 source lines, and occupied 0.8 MB before dependency installation. Those numbers describe the repository build, not the much simpler download of a released binary.
The test command failed with exit code 1 after 48 seconds. Go test produced 50 passing package results and 18 failures out of 68. The supplied log tail showed GitHub, GitLab, Google Cloud Storage, HTTP filesystem, embedded-source, and other source packages passing, then ended with FAIL. It did not include the earlier error messages for the 18 failed packages, so we cannot name a cause from that tail.
The repository had 2 CI workflow files and a Dockerfile. It had no top-level tests directory, which is normal for a Go project whose test files can live beside packages. Our Go 1.24 image is also older than the Go 1.25 and 1.26 versions named by the current README badge. That mismatch is relevant when reproducing the run, but the available log does not establish that it caused any failure.
A dirty version needs manual repair before work can continue
Migrate marks a database dirty before it executes a change. If execution fails, later migrations stop until an operator determines whether the change was applied fully, partly, or not at all. The force command then records the version that matches reality without rerunning migration SQL. This is a sound safety stop, though it requires a person who can inspect the database instead of treating deployment as a retry loop.
Concurrent execution also depends on the selected driver. The FAQ names MySQL GET_LOCK and PostgreSQL advisory locks as examples, while the getting-started guide warns that only some drivers supply locking. A deployment with 2 or more application instances should nominate one migrator or verify the driver's lock behavior. Migrate can stop gracefully on SIGINT, but process control does not replace database-level exclusion.
One batch cannot combine 2 migration sources
The source abstraction is broad but deliberately singular for a run. The FAQ says a batch cannot mix multiple sources, so a release cannot draw some migrations from an embedded filesystem and the rest from S3 in one ordered sequence. Teams with split ownership need to consolidate the files or run separate, carefully ordered migration jobs. A source URL may also carry credentials, which means command construction and log redaction deserve review.
Version 4.20.1 fixed release delivery the same day
GitHub shows v4.20.1 published on September 9, 2026, minutes after v4.20.0. The earlier tag's release workflow failed to publish artifacts to Docker and other registries, so maintainers issued v4.20.1 with a corrected GoReleaser matrix instead of moving the existing tag. The repository was also pushed that day. This is current maintenance and a transparent repair, even though consumers had to skip the broken distribution tag.
Issue search on September 10 returned 311 open issues and 177 open pull requests. The queue includes items updated that week, and one PR was updated on September 10, so the large total is active rather than an abandoned archive. It is still a lot for users to search when a driver behaves unexpectedly. The 18,906 stars show reach, while the backlog argues for reading issues for the exact database and source combination you plan to use.
Choose Migrate when the SQL already belongs to your team
Our 174-second build and 18 failed package results keep this checkout from earning an unconditional recommendation for contributors. Binary users should trial v4.20.1 against a disposable copy of their actual database, including an intentional failed migration and recovery with force. If the team expects the tool to design or lint schema changes, Atlas is the closer match; if it needs Go migration functions, Goose is easier to justify.

