Dbmate одна утилита для миграций во всех ваших проектах
Знакомая ситуация: половина сервисов в компании на Python, один на Go, старый биллинг на Ruby, и у каждого свой инструмент для миграций базы. У кого-то Django ORM, у кого-то самописные скрипты, а один сервис вообще живёт без версионирования схемы. Я недавно наткнулся на проект, который решает именно эту боль, и честно удивлён, что не использую его раньше.
Что это такое
Dbmate — консольная утилита для миграций базы данных, написанная на Go. Авторы позиционируют её как инструмент, независимый от языка и фреймворка: один бинарник, который работает с PostgreSQL, MySQL, MariaDB, SQLite, ClickHouse, BigQuery и Spanner. Проект существует с 2015 года, у него больше 7 000 звёзд на GitHub и лицензия MIT.
Идея простая до безобразия. Миграции пишутся на обычном SQL в плоских файлах, а dbmate отслеживает, что уже применено. Никаких ORM, никакого кода на конкретном языке, никакого конфигурационного файла.
Почему это удобно
Миграции на чистом SQL
Создать новую миграцию — одна команда:
dbmate new create_users_table
Получаем файл вида 20151127184807_create_users_table.sql с двумя секциями:
-- migrate:up
create table users (
id integer,
name varchar(255),
email varchar(255) not null
);
-- migrate:down
drop table users;
Имена файлов версируются таймстампом, а не порядковым номером. Если два разработчика работают параллельно, конфликтов версий не будет. Кстати, переименовать файл можно спокойно — в базу записывается только номер версии.
Дамп схемы для git
Вещь, которой не хватает многим аналогам: после каждой миграции dbmate пишет полный дамп схемы в schema.sql. Кладёте его в git — и в code review видно, во что превратится база после мержа PR, одним диффом. Для тестов файл тоже пригоден: вместо прогонки сотен миграций можно быстро залить схему целиком.
Ожидание базы и работа с Docker
Если у вас docker-compose, то знакомо: контейнер с приложением стартует раньше Postgres, миграции падают с connection refused. Dbmate решает это командой wait, которая стучится в базу раз в секунду до 60 секунд:
dbmate --wait up
Под капотом есть и create/drop для базы, что удобно для тестового окружения: снёс базу, пересоздал, прогнал миграции.
Подключение через переменные окружения
Dbmate берёт строку подключения из DATABASE_URL (или любой переменной через флаг -e) и сам читает .env-файл из текущей директории. В twelve-factor приложении это ровно то, что нужно. Пример для тестов:
dbmate -e TEST_DATABASE_URL drop
dbmate -e TEST_DATABASE_URL --no-dump-schema up
Что внутри
Утилита собрана в один самодостаточный бинарник, поэтому ставить её можно куда угодно: через Homebrew, NPM, Scoop, Docker или просто скачав файл с релизов. Для CI это плюс — не тянете зависимости языка.
Есть и ещё один режим: dbmate можно подключить как библиотеку в Go-проект и даже вшить миграции в бинарник через go:embed. Приложение при первом запуске само накатит схему на SQLite или другую базу. Для утилит, которые распространяются одним файлом, это изящное решение.
Где пригодится
Первый и главный сценарий — полиглот-команда, где несколько сервисов на разных языках ходят в одну или несколько баз. Один инструмент, одинаковый процесс миграций для всех.
Второй — микросервисы с ClickHouse. Поддержка ClickHouse в миграционных инструментах встречается редко, а тут есть и нативный протокол, и HTTP, и даже параметры для кластеров с ZooKeeper (on_cluster, cluster_macro).
Третий — CI и локальная разработка. dbmate wait в entrypoint-скрипте убирает целую категорию флейков.
Честно о минусах
По умолчанию dbmate не умеет откатывать миграции, если вы не написали секцию migrate:down. В некоторых инструментах (тот же Flyway) подобное поведение тоже встречается, но в ORM-мире к этому не все готовы. Ещё для дампа схемы нужны установленные в системе pg_dump, mysqldump или sqlite3 — без них dbmate молча пропустит этот шаг. А если миграции коммитятся из долгоживущих веток, они могут примениться не по порядку, есть только флаг --strict, который это запрещает.
Если у вас один сервис на одном фреймворке со встроенными миграциями — возможно, dbmate вам ничего не даст. Но как только в проекте появляется второй язык, отдельный воркер или аналитическая база на ClickHouse, унифицированный инструмент миграций экономит ощутимо времени.
Начать просто: установите бинарник, положите DATABASE_URL в .env, выполните dbmate new — и через пять минут у вас первая миграция. Репозиторий живой, коммиты свежие, документация подробная, с таблицей сравнения с goose, golang-migrate и Flyway. Рекомендую хотя бы попробовать в pet-проекте.
