ptah.run
ptah.run

В июне прошлого года я занимался разработкой проекта для собственных нужд (inventario), где заодно игрался с новыми фичами go, архитектурой кода и агентной разработкой. Благодаря этому проекту, кстати, я смог найти и зарепортить баг с имплементацией дженериков в go.

Изначально простой проект разросся. Росло количество моделей и связей между ними. В какой-то момент я подумал, что неплохо бы озаботиться миграциями для моей системы. Я посмотрел, что имеется в наличии, и мне не зашло ничего. А хотелось мне странного: чтобы модели и были дефиницией моей схемы. Я писал на go, а в go есть паттерн директивных комментариев (//vendor:some:thing без пробела после //). Такие комментарии не имеют какого-то специального значения (за исключением директив компилятору и линкеру), но могут быть обработаны сторонним инструментом (обычно так же написанным на go).

Немного подумав, я завёл новый проект: github.com/stokaro/ptah

Не буду скрывать, проект писался с помощью AI. Однако, если кто-то на этом решил данную страницу, выдохнув: "ууу, очередной ИИ-слоп", то я бы попросил не торопиться с выводами. Дело в том, что да, ИИ может для вас написать код, но инженерные и архитектурные решения ИИ за вас не сделает. Да и различные идеи по ходу разработки тоже не от ИИ исходят. Кроме того любой заинтересовавшийся может посмотреть на активность разработки (на момент написания заметки в основном репозитории было 2414 коммитов, а ещё есть репозиторий с Kubernetes оператором, который достоин отдельной истории). В общем, ИИ можно сильно по-разному использовать.

Изначально я вёл разработку так: сделал что-то в Ptah, пошел применять в Inventario. Нашел баги и пробелы -- повторить итерацию. И так, пока не добился стабильности и безбажности задуманного flow. Но на тот момент Ptah был проект чисто по фану, для собственных нужд. И на какое-то время, после того, как добился нужной мне функциональности и стабильности я его забросил.

Начало разработки

Первый коммит в репозиторий был сделан 3 июня 2025 года (а до этого несколько дней эксперимент жил напрямую в inventario). Всё лето и до начала сентября 2025 инструмент постепенно из эксперимента стал пригладным Go-first экспериментом.

Вот так выглядела примитивная декларация схема на тот момент:

//migrator:schema:table name="users"
type User struct {
    //migrator:schema:field name="id" type="SERIAL" primary="true"
    ID int64

    //migrator:schema:field name="email" type="VARCHAR(255)" not_null="true" unique="true"
    Email string

    //migrator:schema:field name="created_at" type="TIMESTAMP" not_null="true" default_fn="NOW()"
    CreatedAt time.Time
}

Однако, я быстро столкнулся с проблемой embedded структур, поэтому парсеру пришлось научиться работать с AST go (stdlib go такой функционал даёт из коробки):

type Base struct {
    //migrator:schema:field name="created_at" type="TIMESTAMP" not_null="true"
    CreatedAt time.Time

    //migrator:schema:field name="updated_at" type="TIMESTAMP" not_null="true"
    UpdatedAt time.Time
}

//migrator:schema:table name="users"
type User struct {
    // собственные поля User ...

    Base
}

goschema при обходе User видел embedded Base, и мерджил описание схемы для таблицы users. Поэтому концептуальный результат был:

User
├── ID
├── Email
└── Base
    ├── CreatedAt
    └── UpdatedAt

          ↓ flatten

users
├── id
├── email
├── created_at
└── updated_at

В последствии у Ptah появилась собственная schema-семантика для embedded structures. В более позднем коде появились embedded директивы, обработка embedded FK и всё это было, конечно же, покрыто тестами.

Хочу заметить, что это именно schema definition, а не очередной ORM

На первом этапе получился примерно такой пайплайн для создания и обработки миграций:

Go source
   ↓
goschema
   ↓
desired schema
   ↓
schemadiff ← dbschema ← live DB
   ↓
planner
   ↓
AST
   ↓
renderer
   ↓
SQL
   ↓
generator / migrator

Стоит отметить, что изначальная архитектура (frontend → model → diff → plan → render → execute) получилась весьма удачной и дожила с некоторыми изменениями до нынешнего состояния проекта. Более того, я изначально ставил себе целью разрабатывать качественный внешний API-контракт, чтобы библиотеку можно было использовать из go-приложений. В частности, на мой взгляд, AST-слой вышел очень идиоматичным как с точки зрения Go, так и с точки зрения юзабилити.

Стагнация проекта

С середины сентября 2025 года по конец апреля 2026 года наступает длительная пауза в развитии функционала. Лишь изредка прилетают небольшие багфиксы и бампы зависимостей. Всего с 6 сентября 2025 по 30 апреля 2026 года было лишь 24 коммита.

Второе дыхание Ptah

К 7 мая 2026 года функциональная работа плотно возобновляется. PR #118 исправляет молчаливое игнорирование неизвестных атрибутов Go-аннотаций. Конкретный пример: default_fn="NOW()": аннотация могла быть принята, но DEFAULT в SQL не появлялся. Ошибка обнаруживалась уже при применении миграции к непустой таблице. В PR отдельно зафиксирована проверка на реальных моделях Inventario.

Далее появляются и расширения: 17 мая добавляется ClickHouse, 18 мая -- передача context.Context в подключение к БД и ограничение времени подключения. На этой стадии Ptah всё ещё развивается вокруг прежнего движка и Go-аннотаций.

В этот период я уже начал активно думать о том, что я хочу заниматься Ptah больше. Но надо было понять, куда двигаться.

12–21 июля 2026, главный продуктовый перелом: я принял решение взять Atlas как ориентир, с которым мне захотелось добиться feature parity. И хотя кто-то скажет -- Atlas же open source Apache 2.0, бери и форкай, но нет: моей целью была собственная разработка и собственная архитектура, а не клон существующего решения. За всё время работы над Ptah я ни разу не смотрел в их код. 12 июля 2026 года открыт issue #250:

Atlas compatibility: migrate-from vs. native “atlas mode” — decide the strategy.

Это не обычный feature request, а выбор между двумя разными продуктами: помощником для разового перехода с Atlas и инструментом, который способен работать с существующим Atlas-проектом без предварительной полной переделки. Я принял решение сделать совместимость на уровне command-line surface (т.е. заменив бинарь, можно было продолжить пользоваться новым инструментом, не переписывая существующие интеграции).

Претензии со стороны Atlas

Вплоть до 1 августа 2026 года я пользовался исключительно документацией Atlas для имплементации совместимого функционала. В тот день я решил попробовать сделать автоматизированный прогон бинаря ИИ-агентом с целью сбора информации по внешнему контуру -- какие параметры есть, какие ошибки выдаёт и так далее. Но я столкнулся с тем, что бинарь, который устанавливается способом, который указан в официальной документации, требует для некоторого функционала авторизацию на их портале (для CE версии бинарь не дают вообще, насколько мне известно -- мол, собирайте сами). Я зарегистрировался и увидел, что дают триал на месяц. Моё решение было таким - попробую уложиться в месяц, а если не уложусь, возьму на минимальном тарифе.

На следующий день я замечаю, что бинарь более не работает. И агент остановил сбор информации. А в емейле у меня было грозное послание со стороны компании Atlas, мол, прекратите нарушать лицензию (аккаунт мне, конечно же заблокировали).

Как я потом выяснил, Атлас имеет неотключаемую телеметрию, которая собирает неприлично много информации -- они знают о каждом запуске, о локации, где вы запускаете, некоторую информацию о вашем окружении (часть из которой я бы назвал чувствительной). Но это я узнал потом, и является темой для другой статьи.

После пары переписок, я сослался на Articles 5(3) and 8 of Directive 2009/24/EC (а я могу это сделать, потому что я проживаю в ЕС). Но тем не менее я согласился более не прикасаться к проприетарному бинарнику Atlas. На этом конфликт был исчерпан. Я не имею претензий к ним, и они -- я полагаю -- более не имеют претензий ко мне. Сейчас я использую self-built Atlas CE в пайплайнах для автоматизированных тестов паритета и сбора информации о поверхности cli.

Что нас не убивает, ускоряет roadmap.

Инцидент с Atlas меня заставил задуматься сразу о нескольких вещах:

  • Компания с капиталом посчитала меня -- разработчика-одиночку -- угрозой их бизнесу, что в каком-то смысле является комплиментом.

  • Я пересмотрел своё отношение к тому, что я использую -- компонентам, инструментам, сервисам. Прежде, чем что-то добавлять в стек, я теперь смотрю, а какие лицензионные правила у данного инструмента.

  • Я оформил и переписал statement продукта, что я не использую никакие части Atlas, и что разработка ведётся по чешским законам и европейским директивам.

  • Я тщательно слежу за тем, чтобы ничто в моём проекте не выходило за пределы MIT лицензии, иными словами -- моя цель создать лицензионно-чистый MIT продукт.

  • "Наезд" со стороны Atlas побудил меня ускориться и более агрессивно имплементировать паритет по функционалу (в т.ч. по pro-features).

Где Ptah сейчас

Если посмотреть на Ptah конца 2025-го года и на то, что получилось сейчас, это уже довольно разные инструменты.

Go-аннотации никуда не делись и по-прежнему являются полноценным способом описания схемы, но Go перестал быть обязательным. Более того, я осознал, что через аннотации невозможно выразить некоторые конструкции, поскольку аннотации пишутся строго в одну строку, и строка может стать неприлично длинной. Плюс включение переносов строк в выражения (хранимые процедуры, функции), становится проблематичной. Поэтому сейчас desired schema может приходить из SQL, YAML, HCL (Atlas-совместимый с Ptah-расширенями), DBML, Go-аннотаций, внешнего loader'а или непосредственно из другой базы данных. Все эти источники сходятся в одну внутреннюю модель, после чего работают одни и те же diff, planner и renderer.

У Ptah сейчас есть два основных workflow.

Первый -- привычные versioned migrations: сравнить desired state с текущей базой, построить diff, проверить потенциально опасные изменения и сгенерировать SQL, который можно коммитить в git вместе с остальным кодом.

Второй -- direct schema management: построить и показать план, а затем привести базу непосредственно к желаемому состоянию. Здесь SQL-файлы миграций вообще не обязаны быть частью контракта.

SVG Schema Visualization
SVG Schema Visualization

При этом Ptah уже давно занимается не только CREATE TABLE и ALTER TABLE. Среди поддерживаемых возможностей есть:

  • PostgreSQL, MySQL/MariaDB, SQLite, SQL Server, ClickHouse и ряд PostgreSQL-compatible СУБД;

  • declarative schemas из нескольких форматов;

  • introspection, schema diff и drift detection;

  • версионированные и direct миграции;

  • анализ деструктивных изменений и линтинг миграций;

  • декларативные reference data;

  • PostgreSQL-специфичные объекты, включая extensions, functions и RLS;

  • Atlas-совместимые каталоги миграций, atlas.sum, HCL и часть command-line surface;

  • визуализация и экспорт схемы;

  • OCI artifacts:

    • SQL-миграции

    • Декларативные схемы БД

    • Результаты линтинга и анализа

    • Планы миграций

    • Отчёты о применении миграций (deployment reports)

  • библиотечный Go API;

  • отдельный Kubernetes operator;

  • persistent inference migrations, в том числе управляемая смена поколений embeddings с backfill, verification, cutover и rollback.

Schema Drift Visualization
Schema Drift Visualization

Последний пункт стоит особняком и ярко показывает, насколько далеко проект ушёл от первоначальной идеи «прочитать комментарии над Go-структурой и сделать SQL».

При этом Ptah всё ещё pre-GA. Это важно понимать при принятии решений: для продакшн БД я бы не рекомендовал использовать инструмент на данный момент. Если вы это будете делать, то вы действуете на свой страх и риск.

А Atlas-то мы уже превзошли?

Нет.

Atlas существует значительно дольше, имеет команду разработчиков, пользователей, документацию и годы эксплуатации на реальных системах. Утверждать, что небольшой pre-GA проект уже просто «лучше Atlas», было бы как минимум самоуверенно.

Но есть несколько направлений, где Ptah уже получился именно таким, каким я хотел бы его видеть.

Во-первых, у Ptah нет разделения на искусственно урезанный community binary и функциональность, которую локальный инструмент разрешает использовать только после обращения к внешнему сервису. Один open-source продукт, MIT, без каких-либо аккаунтов где-либо (за исключением OCI, но и это лишь ваш выбор).

Во-вторых, compatibility для меня является входом, а не архитектурой продукта. Поэтому Atlas HCL или каталог Atlas migrations -- это такие же источники данных, как SQL, DBML или Go-аннотации. Внутри Ptah они превращаются в собственную модель, а дальше работают обычные механизмы Ptah.

Именно поэтому со временем появился отдельный ptah-compat: совместимость с существующими интеграциями не должна определять архитектуру native CLI.

В-третьих, возможности, которые в других продуктах оказываются коммерческими или облачными, я сознательно хочу оставлять в open-source. Например, declarative reference data для меня не выглядит enterprise-feature: это нормальная часть управления состоянием базы.

Наконец, я стараюсь делать проверяемость частью архитектуры. Для compatibility недостаточно написать в README слово «compatible»: есть conformance tests. Для СУБД недостаточно иметь renderer: есть integration tests и capability matrix. Для Kubernetes operator недостаточно получить успешный exit code из Job: после применения оператор снова наблюдает базу и проверяет convergence.

При этом есть области, где Atlas объективно впереди: зрелость, накопленный production experience, некоторые поддерживаемые объекты и сценарии, документация вокруг edge cases и просто количество реальных пользователей. Именно реальные пользователи сейчас являются для Ptah гораздо более дефицитным ресурсом, чем ещё несколько тысяч строк кода.

Куда дальше

Моя ближайшая цель -- не добавить ещё сто фич ради красивого feature matrix.

Перед GA мне хочется стабилизировать публичные контракты, продолжить закрывать обнаруживаемые conformance gaps, расширять реальные integration-сценарии и, главное, прогонять Ptah не только на придуманных мной fixtures, но и на чужих проектах.

Отдельное направление -- Kubernetes operator. Сейчас это уже самостоятельный control plane с reconciliation, immutable OCI artifacts, approval model и восстановлением после неудачного или неопределённого выполнения. Про его устройство -- если будет спрос -- стоит написать отдельную статью.

Есть и более экспериментальные направления. Одно из них -- persistent inference state: embeddings и другие производные данные тоже приходится мигрировать, только обычной транзакции с ALTER TABLE для этого уже недостаточно. Другое -- AI assistant вокруг Ptah, которому можно дать доступ к структурированной модели схемы, планам и диагностике вместо того, чтобы просить LLM угадывать состояние базы по нескольким SQL-файлам.

Но сейчас мне наиболее критично выяснить, где существующий Ptah ломается в реальных условиях. И где он не решает проблему пользователя.

Поэтому мне нужны люди

Если вы дочитали до этого места и работаете с PostgreSQL, MySQL/MariaDB или просто регулярно имеете дело с миграциями -- попробуйте Ptah.

Мне не обязательно нужны контрибьюторы, готовые сразу писать код. Мне нужны люди, которые возьмут существующий проект и скажут:

  • «Вот здесь я вообще не понял, что от меня хотят»;

  • «Вот эту схему Ptah разобрал неправильно»;

  • «Вот такой migration workflow у нас используется десять лет, а ты его не предусмотрел»;

  • «Здесь Atlas делает удобнее»;

  • «Эта команда называется странно»;

  • «Вот это потенциально снесёт production».

Такая обратная связь сейчас для проекта ценнее ещё одной реализованной мной фичи.

Если же вы хотите поконтрибьютить, я буду только рад. Единственное, что прежде, чем начинать работу над фичой, стоит это обсудить в разделе для дискуссий.

Не хочется заморачиваться с установкой? Не проблема: попробовать Ptah можно даже без установки -- на play.ptah.run работает настоящий Ptah вместе с SQLite, собранные в WebAssembly. Документация и информация по локальной CLI установке находится на docs.ptah.run, исходники -- github.com/stokaro/ptah. Kubernetes operator находится в отдельном репо: github.com/stokaro/ptah-operator.

Ptah Playground
Ptah Playground

Буду рад любой конструктивной критике. Если вы убеждены, что вся затея не стоила свеч -- прошу в комментарии, но, пожалуйста, аргументированно :-)