С телефона в Альпах в Microsoft Store за две недели: нативный GUI для PostgreSQL через облачный Claude Code. .NET.. .NET. Avalonia.. .NET. Avalonia. Claude.. .NET. Avalonia. Claude. dotnet.. .NET. Avalonia. Claude. dotnet. GUI.. .NET. Avalonia. Claude. dotnet. GUI. microsoft store.. .NET. Avalonia. Claude. dotnet. GUI. microsoft store. nativeaot.. .NET. Avalonia. Claude. dotnet. GUI. microsoft store. nativeaot. Open source.. .NET. Avalonia. Claude. dotnet. GUI. microsoft store. nativeaot. Open source. PostgreSQL.. .NET. Avalonia. Claude. dotnet. GUI. microsoft store. nativeaot. Open source. PostgreSQL. агентная разработка.. .NET. Avalonia. Claude. dotnet. GUI. microsoft store. nativeaot. Open source. PostgreSQL. агентная разработка. бенчмарки.. .NET. Avalonia. Claude. dotnet. GUI. microsoft store. nativeaot. Open source. PostgreSQL. агентная разработка. бенчмарки. Ненормальное программирование.

С телефона в Альпах — в Microsoft Store за две недели: нативный GUI для PostgreSQL через облачный Claude Code

Я больше десяти лет пишу коммерческий код под .NET, живу в Праге, PostgreSQL — мой ежедневный рабочий инструмент. В начале июля я сидел в отпуске в Альпах — без ноутбука, только с телефоном — и мне было банально скучно: весь ютуб к тому моменту был досмотрен. Зато у меня обнаружились неиспользованные лимиты Claude Code и хорошо выдержанная рабочая фрустрация: pgAdmin, которому нужно секунд тридцать, чтобы запуститься, когда надо быстро что-то проверить, и вечная лотерея с кавычками — где двойные, где одинарные, я не угадывал никогда. Сложив скуку, лимиты и фрустрацию, я решил, что пора сделать что-нибудь полезное.

За выходные, целиком с телефона, не написав руками ни строчки кода, я получил первую рабочую версию клиента для Postgres: подключение к базе, дерево схемы, редактор с подсветкой, стриминг результатов. Потом были две недели вечеров после работы — полировка, ручное тестирование, 17 релизов. Сегодня pgNimbus опубликован в Microsoft Store, есть macOS-бета и Linux-пакеты, MSI весит 19 МБ, а от клика до окна проходит около 100 миллисекунд.

Это мой первый публичный опенсорс-проект, и это статья о том, как на самом деле выглядит разработка, когда IDE — это чат: что агент делает сам, где без человека никак, и какие грабли по дороге были настоящими, а не из презентаций.

1. Выходные с телефона

Идея «быстрого клиента для Postgres» не нова, ниша давно описана: pgAdmin и DBeaver мощные, но тяжёлые; TablePlus быстрый и красивый, но закрытый и платный; HeidiSQL быстрый и открытый, но родом из другой эпохи и MySQL-first. Дырка «по-настоящему быстрый + опенсорс + современный UI» так и стоит незанятая — в неё я и целился.

Разработка с телефона выглядит менее героически, чем звучит. Облачный Claude Code — это веб-клиент: агент работает в песочнице с полноценным репозиторием, а ты пишешь ему задачи в чат. Читать диффы с экрана телефона — занятие для сильных духом, так что моя «среда разработки» в те выходные свелась к трём вещам: скриншоты приложения, которые агент присылал из облака, обновляющийся README и его собственные рассуждения по ходу работы. Первый коммит — 4 июля: скаффолд решения на .NET и Avalonia, два проекта — PgNimbus.Core (движок, зависит только от Npgsql) и PgNimbus.App (весь UI). В тот же день появились дерево схемы, менеджер подключений с хранением паролей в DPAPI и инлайн-редактирование в гриде. Не потому что я такой быстрый — потому что каждая из этих задач для агента — это час-полтора работы, а от меня требовались постановка и «нет, переделай вот это».

Честно признаюсь: это затягивает почище любого сериала. Наблюдать за ходом мысли модели — как она разворачивает себе окружение с нуля (в голой песочнице нет ни .NET, ни дисплея, ни Postgres — она ставила всё через apt, поднимала Xvfb, локальный Postgres с тестовыми данными), запускает приложение под виртуальным дисплеем, скриншотит его и по этим же скриншотам дебажит — оторваться невозможно. Смотришь на очередной скриншот приложения, которого утром ещё не существовало, и ловишь себя на том, что обновляешь чат чаще, чем ленту. Тесты она гоняла сама и собственные регрессии тоже находила сама.

Где приходилось направлять: во всём, что касается вкуса и приоритетов. Агент с одинаковым энтузиазмом сделает и нужную фичу, и ненужную, и на каждую предложит кнопку в тулбаре. Половина моих реплик в те выходные — это «нет», «проще», «убери кнопку, пусть будет в палитре команд». Из этого потом выросли письменные правила проекта, о них ниже.

Что не получалось: всё, что упирается в живой пиксель. Классика жанра — иконка приложения. Агент честно сгенерировал скриптом все размеры из одного мастера, и на панели задач Windows это выглядело как мутное пятно; в итоге в репозитории лежат отрисованные отдельно мастера под каждый размер (16, 24, 32, 48 px — свои, не даунскейл), а скрипт их только собирает. Или иконка в таскбаре Windows 11, которую Avalonia через Window.Icon обновляет в заголовке окна, но не на панели задач — пришлось разбираться и слать WM_SETICON напрямую через P/Invoke. Такие вещи агент не увидит сам — их видит человек, который запустил приложение на настоящей машине.

Поэтому домой из отпуска я, чего уж там, спешил: очень хотелось наконец запустить собранное приложение на настоящей машине, а не разглядывать его через скриншоты. Первый запуск оправдал ожидания в обе стороны: недоделок и косяков нашлась сразу куча — но база оказалась более чем рабочей, и её можно было начинать полировать.

Главное окно pgNimbus: редактор запросов, результаты, дерево схемы

Главное окно pgNimbus: редактор запросов, результаты, дерево схемы

Главное окно: редактор, стриминговый грид результатов, дерево схемы из pg_catalog.

2. Две недели вечеров и CLAUDE.md как архитектурный надзор

Так проект переехал в режим «вечера после работы». С 6 по 18 июля — 17 тегов, от v0.1.0 до v0.7.5. Это не 17 больших релизов, конечно: часть — минорные фиксы, найденные при ручном прогоне. Но каждый тег — это полный прогон релизного пайплайна, включая бенчмарки, так что дисциплина «main всегда собирается и измеряется» держалась с первой недели.

Главный инструмент управления агентом оказался неожиданно скучным: файл CLAUDE.md в корне репозитория. Это память проекта, которую агент читает в начале каждой сессии. Туда я складывал не документацию, а правила: «PgNimbus.Core не имеет UI-зависимостей», «стриминг и отмена запросов не обсуждаются», «каждая новая всегда видимая кнопка по умолчанию отклоняется — второстепенные действия живут в палитре команд», «двойной клик по элементу списка выполняет его основное действие». Звучит как банальности, но эффект накопительный: агент без таких правил каждый раз изобретает архитектуру заново, а с ними — держит линию через десятки сессий, между которыми у него нет никакой памяти.

Разделение ролей за эти две недели устоялось такое: агент пишет код, тесты и CI; я пишу правила, ревьюю каждый PR, руками прогоняю сборку на живой базе и отвечаю за то, что уходит в релиз. Это не «AI сделал всё за меня» — это скорее работа тимлидом у очень быстрого, очень исполнительного джуна с энциклопедическими знаниями и полным отсутствием мнения о том, что важно. Мнение — моя работа.

Ручное тестирование при этом никуда не делось, и именно оно ловило самое интересное. Скролл результатов на macOS, где двигался скроллбар, но не контент (тач-обработчик колеса писал в ScrollBar.Value напрямую, а DataGrid реагирует только на пользовательские события скролла). Гейткиперский диалог «приложение повреждено» на неподписанной macOS-бете. Ни одну из этих вещей агент не нашёл бы в песочнице — там нет ни macOS, ни настоящего тачпада.

3. Почему быстро И маленько: NativeAOT

Тезис проекта — скорость, и здесь всю тяжёлую работу делает NativeAOT: .NET-код компилируется сразу в машинный, без JIT и без рантайма в комплекте. Разница ощущается кожей: около 100 мс от клика до отрисованного окна против ~700 мс у обычной JIT-сборки того же кода. И размер: MSI — 19 МБ, потому что не надо тащить рантайм.

Холодный старт pgNimbus: от запуска процесса до готового окна

Холодный старт pgNimbus: от запуска процесса до готового окна

Холодный старт NativeAOT-бинарника. Гифка длиннее, чем сам старт.

Публикация выглядит буднично:

dotnet publish PgNimbus.App -c Release -r win-x64 -p:PublishAot=true

Но AOT — это контракт: никакой рефлексии в рантайме, никакой динамической генерации кода. Три грабли из этого репозитория, на которые стоит показать пальцем, если вы соберётесь повторить путь:

Сателлитные сборки + InvariantGlobalization. Сочетание InvariantGlobalization=true с culture-named сателлитной сборкой роняло Avalonia под AOT прямо на старте — причём с абсолютно сбивающей с толку ошибкой «avares://… not found», как будто потерялся ресурс. Лечится одной строкой в csproj, но чтобы её написать, надо сначала понять, что резолвер ассетов тут ни при чём:

<InvariantGlobalization>true</InvariantGlobalization>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>

Рефлексия в биндингах грида. Биндинг колонок результатов через индексаторный путь "[i]" — это рефлексия, под AOT она молча не работает. Колонки в pgNimbus биндятся через конвертер (RowIndexConverter), а на уровне проекта включён AvaloniaUseCompiledBindingsByDefault, чтобы рефлексионный биндинг не пролез случайно.

Сериализация настроек. System.Text.Json с рефлексией под AOT тоже не жилец — все сторы (настройки, воркспейс, история запросов) ходят через source-generated контексты:

[JsonSourceGenerationOptions(WriteIndented = true)]
internal sealed partial class WorkspaceJsonContext : JsonSerializerContext;

Отдельная приятная деталь: агент про все три граблины знал — не в смысле «угадал», а в смысле «наступил, диагностировал по симптомам и починил» в той же песочнице, где собирал linux-x64 AOT-публикацию для проверки времени старта.

Стартовая гонка: pgNimbus против pgAdmin с холодного старта

Стартовая гонка: pgNimbus против pgAdmin с холодного старта

Та самая гонка, с которой всё началось: pgNimbus уже показывает результаты запроса, пока pgAdmin ещё думает над сплеш-скрином.

4. Кроссплатформенность почти бесплатно: Avalonia

UI написан на Avalonia 12 — один XAML-код на Windows, macOS и Linux, рендеринг через Skia. «Почти бесплатно», потому что 95% кода действительно общие, а оставшиеся 5% — это платформенные мелочи, съедающие непропорционально много времени: заголовок окна на macOS (слить командную панель с тайтлбаром, отступ под «светофор»), та самая иконка таскбара на Windows, Cmd вместо Ctrl в хоткеях (с одним осознанным исключением: автокомплит остался на Ctrl+Space, потому что Cmd+Space — это Spotlight).

Релизный пайплайн (release.yml) с одного тега собирает всё: Windows — AOT-публикация плюс per-user MSI через WiX; macOS — osx-arm64 на раннере macos-14, завёрнутый в .app и .dmg (Intel-маков среди раннеров GitHub больше нет — прощай, osx-x64); Linux — x64 и arm64 (arm64-нога собирается нативно на бесплатных ubuntu-24.04-arm раннерах, без кросс-компиляции), каждый в трёх форматах: AppImage, .deb и tar.gz. Плюс MSIX для Store и сгенерированные winget-манифесты. Один тег — девять устанавливаемых артефактов.

Из платформенных засад дороже всего обошлась macOS: Xcode 26 сломал Swift-автолинковку так, что NativeAOT перестал статически линковать системную криптобиблиотеку («symbol(s) not found for architecture arm64»), и апстрим закрыл issue как «not planned». Решение — прибить в CI новейший Xcode ниже 26-й мажорной версии и оставить комментарий потомкам.

5. «Быстрый» — это измеряемо

Слово «быстрый» в README ничего не стоит, поэтому в проекте оно измеряется на каждом релизе. Бенчмарк-пайплайн — это отдельный workflow, который запускается из релизного: поднимает Postgres в сервис-контейнере, собирает и JIT-, и AOT-варианты и меряет четыре вещи — время от старта процесса до первого отрисованного кадра (специальный probe-режим: приложение печатает window_ms и RSS и выходит), холодное подключение к базе, round-trip SELECT 1, время до первой пачки строк и полный стриминг 100 000 строк через тот же API, которым пользуется UI. Плюс размер: и AOT-бинарник отдельно, и весь публикуемый набор файлов — второе честнее, потому что нативные библиотеки Skia рядом с экзешником весят больше него самого.

Результаты каждого тега уходят в публичную историю: https://shman4ik.github.io/pgNimbus/dev/bench/. Смысл не в абсолютных числах (они зависят от железа раннера), а в тренде: если какой-то коммит сделал старт на 30% медленнее, это будет видимая ступенька на графике, привязанная к конкретному релизу. Регрессию производительности нельзя «не заметить» — она опубликована.

Стриминг — вторая половина тезиса о скорости. Результаты запроса не материализуются целиком, а текут в UI пачками примерно по 200 строк, так что первый экран данных виден задолго до конца запроса, а Esc действительно останавливает выполнение на середине, а не «после того как дочитаем»:

public sealed record ResultSet : StatementResult
{
    public required IReadOnlyList<ColumnInfo> Columns { get; init; }
    public required IAsyncEnumerable<RowBatch> Batches { get; init; }
}

6. PostgreSQL-first, а не наименьший общий знаменатель

Большинство универсальных клиентов читают схему из information_schema — стандартного, но обедняющего представления. pgNimbus ходит прямо в pg_catalog, поэтому дерево схемы видит материализованные представления, партиционированные таблицы и настоящие флаги первичных ключей из pg_constraint. Кроссплатформенность по базам данных не планируется вовсе: это клиент для Postgres, и точка.

Из этого растут фичи, которые в универсальном клиенте сделать трудно. Автокомплит знает о внешних ключах: после JOIN первыми предлагаются таблицы, связанные FK с уже упомянутыми, а после ON первой строчкой стоит готовое условие целиком — oi.order_id = o.id, одно нажатие. EXPLAIN и EXPLAIN ANALYZE рисуются деревом с per-node стоимостью и временем, а не простынёй текста. Есть live-монитор LISTEN/NOTIFY и окно активности сервера поверх pg_stat_activity — с кнопками cancel и terminate для чужого зависшего запроса.

Автокомплит: FK-ranked таблицы после JOIN и готовое условие после ON

Автокомплит: FK-ranked таблицы после JOIN и готовое условие после ON

После JOIN — таблицы, связанные внешним ключом, после ON — готовое условие соединения.

Дерево плана EXPLAIN ANALYZE рядом с исходным текстом

Дерево плана EXPLAIN ANALYZE рядом с исходным текстом

EXPLAIN ANALYZE деревом: стоимость и фактическое время на каждом узле.

Отдельно — safe mode, фича, выросшая из личного страха инлайн-редактирования на проде. Правки ячеек, вставки и удаления по умолчанию не летят в базу сразу: они копятся локально (изменённые строки подсвечены), а кнопка «Review & commit» показывает точный сгенерированный SQL и применяет всё одной транзакцией. Или отменяет — и тогда в базу не ушло вообще ничего. Режим включён по умолчанию: SafeModeEdits = true в настройках.

И про телеметрию: её нет. Совсем — ни аналитики, ни crash-репортов, ни проверок обновлений. Единственные сетевые соединения, которые открывает приложение, — те, что вы настроили сами: ваши серверы Postgres и, если используете, SSH-туннели. Для инструмента, через который проходят продакшн-креды и продакшн-данные, мне это кажется не фичей, а гигиеническим минимумом; проверяется чтением кода, благо он открыт.

7. Дистрибуция без бюджета: Microsoft Store за $0

У бесплатного опенсорс-проекта нет денег на Authenticode-сертификат, а без подписи прямой MSI встречает пользователя SmartScreen-предупреждением. Классическая развилка: платить несколько сотен долларов в год за сертификат — или найти обходной путь.

Обходной путь — Microsoft Store, и он бесплатный. Хитрость в том, что Store переподписывает загруженный MSIX собственным доверенным сертификатом при сертификации: пакету нужен лишь одноразовый self-signed серт, чтобы пройти требование загрузки, — не купленный. В CI это выглядит так: скрипт пакует AOT-публикацию в MSIX, подписывает эфемерным сертификатом из New-SelfSignedCertificate (и тут же удаляет его из хранилища), артефакт вручную уходит в Partner Center. Первая же отправка прошла сертификацию, листинг живой: ID 9N6SZT42XJ24. Бонусом Store-приложения автоматически видны в winget через источник msstorewinget install pgNimbus --source msstore работает без отдельного PR в winget-pkgs.

Самая же неожиданная возня была не с подписью, а с иконками. Положить в MSIX по одному PNG на логотип недостаточно: Windows молча подкладывает под иконку цветную плашку и мылит её на таскбаре и в диалоге установки, если не находит файл нужного размера с нужным квалификатором. Нужен набор scale-100/125/150/200/400 плюс отдельные «unplated» варианты для таскбара — и всё это ещё надо скомпилировать в resources.pri утилитой makepri, иначе квалифицированные имена файлов просто лежат в пакете мёртвым грузом. Инструкции, почему у вашего MSIX «иконка какая-то не такая», собирались по крупицам — теперь они живут в памяти проекта, чтобы следующая сессия агента не открывала это заново.

Прямой MSI при этом остаётся — неподписанным, per-user (ставится в профиль, без прав администратора: неподписанный плюс UAC — совсем плохое сочетание). Это осознанное решение, а не долг: Store даёт доверие и автообновления за ноль, платный сертификат не даёт ничего сверх этого.

8. Честные выводы

Что агентная разработка делает хорошо. Объём: за две недели вечеров сделано то, на что у меня одного ушли бы месяцы, — не потому что агент пишет код лучше меня, а потому что он пишет его параллельно со мной спящим и не устаёт от рутины вроде CI-пайплайнов, упаковки в пять форматов и правки тестов. Инфраструктуру — CI, бенчмарки, скрипты сборки под три ОС — агент делает едва ли не лучше, чем фичи: там меньше вкусовщины и больше проверяемых критериев успеха.

Где без человека никак. Вкус и приоритеты: что не делать — целиком человеческое решение, и это половина продукта. Живое железо: настоящий тачпад, настоящий таскбар, настоящая продакшн-база — песочница этого не заменяет, и самые неприятные баги были найдены руками. Ответственность: под релизом стоит моё имя, а не имя модели; каждый PR прочитан, каждый релиз прощёлкан. И письменные правила — оказалось, что главный навык работы с агентом — это не промптинг, а способность превратить своё «мне не нравится» в правило, записанное так, что оно переживёт сотню сессий.

pgNimbus сейчас — это версия 0.7.5: работающий, быстрый, ежедневно используемый мной клиент, но всё ещё молодой проект. Роадмап открыт — он лежит прямо в README, от таблично-индексной статистики до PostGIS-вьювера. И мне правда нужен фидбек: я один, вечеров в неделе пять, и порядок, в котором фичи будут появляться, стоит определять не моими предпочтениями.

Что делать следующим?

  1. ER-диаграммы — автоматически разложенный граф внешних ключей схемы, с экспортом в SVG.

  2. Дифф EXPLAIN-планов — прогнать запрос до и после индекса и сравнить деревья планов узел к узлу.

  3. UI для бэкапов — оркестрация pg_dump/pg_restore с прогрессом.

  4. Приватный AI-ассистент (BYOK) — свой ключ или локальная модель, явный opt-in, ничего не покидает машину.

Голосуйте в опросе под статьёй, а если есть что сказать развёрнуто — жду в Discussions. Ну и ⭐ на GitHub, если идея «быстрый + открытый + современный» вам близка — для проекта без бюджета на маркетинг звёзды и есть маркетинг.


Автор: Shman4ik

Источник