Как я, не-программист, собрал десктопный .exe за вечера
Маркетолог без опыта программирования собрал UTM-конструктор: .exe с инсталлером и автообновлениями. История двух попыток — провал 2024-го и успех 2026-го, где всё изменилось.
Я маркетолог, а не разработчик: код читаю, но сложные системы с нуля не пишу. И всё равно за серию вечеров собрал себе настоящее десктопное приложение для UTM-ссылок — двойной клик по иконке, окно открылось, работаешь. Со своим инсталлером и автообновлениями, без единого облачного сервиса. Самое интересное тут не приложение (оно простое), а то, что эту же идею я пробовал собрать ещё в 2024-м — и провалился. За полтора года поменялся не я, а инструменты. Вот про это и расскажу.
Слушай, я тут собрал себе программу
Давай по порядку. Я digital-маркетолог. По образованию инженер, код читаю спокойно, в репозиториях и коммитах ориентируюсь, SQL понимаю. Но писать сложные штуки с нуля — это не ко мне. Я не разработчик, и это важно держать в голове, пока ты читаешь дальше.
И вот этот не-разработчик за несколько вечеров собрал себе настоящее десктопное приложение. Не скрипт «работает у меня на ноутбуке, если запустить из консоли», а нормальный .exe: двойной клик по иконке — открылось окно — работаешь. С инсталлером, который ставит программу как любую другую. С автообновлениями, которые сами прилетают. И без единого облачного сервиса — все мои данные лежат у меня на диске.
Я не рассказываю это как «смотри, какой я молодец». Если бы я подавал это как образец крутой инженерии — было бы смешно с моей стороны. Стек там простой, решения прямолинейные, кода — на пару вечеров. Интересно другое: «довести до конца» — до инсталлера, до автообновлений — раньше было отдельной профессией. А теперь это вечерняя задача для человека, который умеет внятно объяснить, чего он хочет, и прочитать чужой код.
И ещё одна штука, ради которой я вообще сел писать. Этот проект я собирал дважды. Первый раз — в конце 2024-го, и он закончился ничем. Второй — в начале 2026-го, и вот он доехал до результата. Между этими двумя точками изменился не я. Изменились инструменты. Об этом, собственно, весь рассказ.
Откуда вообще взялась идея
Я много вожусь с UTM-метками — это рабочая рутина любого маркетолога. Если коротко и для тех, кто не в теме: UTM — это хвостик, который цепляешь к ссылке, чтобы потом в аналитике видеть, откуда пришёл человек. Из рассылки, из поста, из рекламы. Без них в отчёте каша.
Для сборки этих ссылок есть онлайн-конструкторы. Заходишь, собираешь ссылку, сохраняешь шаблон. Удобно — ровно до того момента, пока сервис не становится платным по подписке, не требует завести аккаунт и не держит всю твою историю ссылок где-то у себя на сервере.
Но дело было даже не в подписке. Я хотел одну штуку, в которой собран весь мой UTM-цикл сразу: и конструктор ссылок, и история, и шаблоны под разные каналы, и сокращалка длинных ссылок, и генерация QR-кода. По отдельности это всё где-то есть — один сервис делает конструктор, другой сокращает, третий рисует QR. А всё вместе, в одном окне и без аккаунтов, — не нашёл. Каждый раз приходилось скакать между тремя вкладками и держать параметры в голове.
В какой-то момент надоело. Не драматичная история — просто очередное «зарегистрируйтесь, чтобы продолжить» и мысль: а ведь мои наборы UTM-параметров живут на чужом сервере, к которому я завтра могу потерять доступ. UTM-конструктор — это же буквально несколько полей и строка на выходе. Не хотел я за это платить подписку, не хотел отдавать данные и не хотел собирать свой рабочий процесс из чужих кусков.
Так и появилась мысль: соберу себе сам. Локально, без облака, без аккаунтов, и сразу со всем набором в одном окне. Двойной клик — окно — работаешь. Вся история и шаблоны — в файле у меня на диске.
Был и второй мотив, чисто исследовательский. Когда нейросети подросли настолько, что начали реально помогать с кодом, мне захотелось проверить на себе: а смогу ли я, не-программист, собрать с их помощью настоящую программу? Не скрипт на коленке, а штуку, которую ставят двойным кликом. UTM-конструктор был идеальным полигоном: задача понятная, объём небольшой, и пользоваться результатом я буду сам каждый день.
Первый заход, 2024: закрыл папку и забыл
Первую попытку я сделал ещё в конце 2024-го. Под рукой тогда были модели прошлого поколения и связка «нейросеть подсказывает код плюс облачная база данных». Идея — та же самая. А вот реальность оказалась другой.
Споткнулся я на двух вещах.
Первая — база данных. Я хотел, чтобы история и шаблоны где-то надёжно лежали. И вот тут началось: то структуру данных не так разложил, то правила доступа настроил не так, то синхронизация ведёт себя не как ожидаешь. Для человека, который не пишет бэкенд каждый день, это был сплошной тупик. Я тонул в настройках облачной базы ради утилиты, которой эта облачная база была вообще не нужна.
Вторая — сами модели. Они тогда заметно хуже держали контекст. Помогали написать кусок — а как только проект разрастался, начинали путаться: предлагали несовместимые правки, ломали то, что вчера работало. Ты чинишь одно — разваливается другое.
В итоге я потратил несколько вечеров, получил полусобранный прототип, который всё время разваливался, и забросил. Честно забросил — без красивого финала. Просто закрыл папку и вернулся к таблицам и онлайн-конструкторам.
Я рассказываю про этот провал не для драматизма. Он важен, потому что показывает планку. В 2024-м идея «соберу себе десктопное приложение» упёрлась не в мою лень — а в потолок инструментов. Облачная база была избыточно сложной для такой мелочи, а модели не вытягивали проект целиком. Полтора года спустя обе эти стенки просто исчезли.
Второй заход, 2026: и тут пошло
К проекту я вернулся в начале 2026-го. История репозитория честно фиксирует дату — первый коммит 8 января 2026 года. И в этот раз всё пошло иначе. Не потому, что я поумнел, а потому, что инструменты стали другими.
Сначала за дело взялся Cursor — это редактор, в котором ИИ-агент видит весь проект целиком, а не один файл за раз. Вот это «видит целиком» и было главным отличием от 2024-го. Агент держал в голове всю структуру приложения, не терял связи между файлами и собрал мне рабочий прототип, а потом и первые версии — то самое окно, которое уже можно открывать и которым уже можно пользоваться. То, что в 2024-м рассыпалось на третьем экране, теперь собиралось и держалось.
Отдельно поменялось решение про базу. Никакого облака. Обычный локальный файл рядом с приложением — и всё. Та самая «возня с базами», на которой я застрял в 2024-м, тут просто испарилась: данные лежат в одном файле на диске, никаких правил доступа, никаких синхронизаций. Для личного инструмента это оказалось ровно тем, что нужно. А я-то полтора года назад был уверен, что «серьёзное приложение» обязано иметь облачный бэкенд. Не обязано.
Финальную, самую муторную часть я добивал уже с другим агентом — Claude. И это была не «напиши мне функцию», а долгая доводка до состояния «не стыдно отдать людям»: корректная работа с базой, сборки сразу под две платформы — Windows и macOS, автообновления. Версии, которые в итоге уехали в релизы, — 2.2.0 под Windows и 2.2.1 под macOS. Именно на этом этапе пет-проект перестал быть «работает у меня на ноутбуке» и стал продуктом — с инсталлером и версиями.
Если свести разницу между двумя заходами к одной фразе: в 2024-м я воевал с инструментами, в 2026-м инструменты воевали за меня. Сам я за это время не стал программистом. Как был маркетологом, читающим код, так и остался. Поменялось то, на что этот навык опирается.
Почему десктоп, а не свой сайт
Логичный вопрос: ты же маркетолог, ну подними веб-версию, это же проще. Но веб-версия — это снова сервер, снова домен, снова база где-то в облаке. А значит — снова «данные не у меня» и снова расходы на хостинг. Мне же был нужен именно личный инструмент: чтобы данные физически лежали на моей машине и работали без интернета. И, если честно, после опыта 2024-го с облачной базой желания опять связываться с серверной частью у меня сильно поубавилось.
Десктоп закрывает это целиком. Приложение работает у меня на компьютере, база данных — обычный файл рядом. Единственное, что вообще ходит в сеть, — это сокращение ссылок, и оно опциональное. Всё остальное работает хоть в самолёте.
И тут — отдельный кайф для меня как для маркетолога. Интерфейс приложения — это обычная веб-страница. Те же HTML, CSS и немного JavaScript, что и на любом лендинге. Я умею верстать веб куда увереннее, чем городить нативные оконные приложения, и здесь это знание просто переиспользуется. Не надо учить отдельный UI-фреймворк — достаточно того, что я уже умею. Порог входа в «десктоп» за счёт этого резко падает.
Что эта штука умеет
Если коротко — закрывает весь мой UTM-цикл, чтобы не возвращаться к онлайн-сервисам. Ради «всё в одном окне» я в эту историю и полез, так что пройдусь по функциям чуть подробнее. Тот самый набор, который я нигде не нашёл собранным вместе.
Базовый сценарий — конструктор. Вводишь URL, заполняешь поля параметров, видишь готовую ссылку, копируешь в один клик. Есть подсказки по значениям — чтобы не плодить в одной кампании зоопарк из fb, facebook и FB. Звучит мелочью, но именно этот зоопарк потом превращает отчёт по источникам в кашу. Подсказки тут экономят больше нервов, чем кажется.
Поверх этого — сокращение ссылки (та самая единственная сетевая функция) и генерация QR-кода прямо в окне. QR рисуется на месте, без обращения к чужому генератору — удобно, когда ссылку с UTM надо быстро увести в офлайн: на листовку, на стенд, на упаковку.
Дальше — то, ради чего я и хотел свой инструмент: шаблоны. Собрал удачный набор параметров под канал — сохранил, навесил тег и цветную метку, потом находишь его поиском и применяешь в один клик. Для повторяющихся кампаний это снимает половину ручной работы: не надо каждый раз вспоминать, как у тебя называется источник для рассылки или для Telegram-канала. Выбрал шаблон, поправил одно поле, забрал ссылку. Теги и цветные метки тут не украшение, а навигация: когда шаблонов набирается несколько десятков, без них список превращается в свалку.
История всех собранных ссылок сохраняется сама, её можно сортировать и фильтровать — удобно, когда через месяц надо вспомнить, какую метку ты вешал на конкретную рассылку. Хранится локально, до 500 записей, чтобы база не пухла бесконечно. А чтобы данные не были заперты внутри приложения, шаблоны и историю можно выгрузить и загрузить обратно файлом. Это закрывает два сценария, которые для личного инструмента важнее, чем кажется: переезд на другую машину без потери накопленного — и передача готового набора шаблонов коллеге. Выгрузил файл, человек загрузил себе, и у него сразу те же заготовки, что у тебя. Ну и мелочи быта: переключение языка RU/EN и светлая/тёмная тема.
Ничего из этого по отдельности не впечатляет. Это нормальная утилита, и каждую функцию в отрыве делает десяток сервисов. Ценность ровно в том, что они собраны в одном окне, работают без интернета и хранят данные у меня. А самое интересное началось, когда я захотел не «запускать у себя из консоли», а отдать людям нормальный .exe, который ставится двойным кликом и сам обновляется.
Вот тут и проходит граница
На мой взгляд, именно здесь граница между «скриптом для себя» и «продуктом». Скрипт запускается из консоли. Продукт — устанавливается, имеет версию, обновляется и не разваливается, когда этих версий становится пять.
Первое, что я сделал, — завёл одно-единственное место, где живёт номер версии. Одна строчка в одном файле. Звучит банально, но это, наверное, главное решение всей сборки. Потому что версия нужна не в одном месте, а сразу в трёх: в самом коде (чтобы проверять обновления), в скрипте инсталлера и в свойствах .exe-файла, которые Windows показывает в «Свойствах». Если держать её в трёх местах руками — рано или поздно соберёшь инсталлер одной версии поверх кода другой и потом полдня будешь искать, почему обновление «не видит само себя».
Поэтому скрипт сборки сначала читает версию из этого одного файла, а потом сам подставляет её во все нужные места. Я меняю одну строчку — остальное синхронизируется автоматически при каждой сборке. Это тот случай, когда несколько строк автоматизации экономят тебе целый класс будущих багов.
А дальше — буквально две команды, и из проекта получается дистрибутив. Первая собирает приложение в исполняемый файл, тянет за ним всё нужное — интерфейс, логотип, примеры шаблонов — и выкидывает лишнее, чтобы сборка не раздувалась. На выходе — папка с .exe, который уже запускается. Вторая команда собирает из этой папки нормальный Windows-инсталлер — тот самый UTMka-Setup, который ставит приложение, создаёт ярлыки, регистрирует его в системе. Итоговый установщик весит порядка 30 МБ — для приложения, которое несёт в себе целый Python внутри, это вполне скромно.
Где меня поцарапало
Чтобы не выглядело «сел и всё сразу заработало», расскажу про два места, которые отняли больше времени, чем сам код.
Первое — пути к файлам. Пока приложение запускается как обычный скрипт, оно знает, где лежат интерфейс, логотип, примеры шаблонов: рядом, в папке проекта. А после упаковки всё распаковывается во временную папку при запуске, и привычные пути ломаются. Приложение собиралось, открывалось — и показывало пустое белое окно, потому что не находило свои же файлы. Лечится это одной функцией, которая понимает, откуда запущена программа, и считает пути от правильного места. Звучит просто — когда уже знаешь. А в первый раз я честно потратил вечер, тыкаясь в белый экран и не понимая, почему «у меня же в режиме разработки всё работает».
Второе — рассинхрон версий. Ровно тот, против которого я потом и завёл единый источник. До того как я это сделал, у меня в коде была одна версия, а в скрипте инсталлера — другая, я просто забыл поправить вторую. Собрал, поставил, запустил — приложение считает себя старее, чем оно есть, и начинает предлагать «обновиться» на версию, которая уже стоит. Безобидно, но абсурдно. Вот после этого я и вынес версию в один файл — не из любви к красивой архитектуре, а чтобы больше не наступать на эти грабли.
Оба случая — про одно и то же. Код пишется быстро. А «довести до продукта» — это вот эти мелкие, неочевидные стыки, которые всплывают только когда отдаёшь штуку наружу.
Автообновления — чтобы не рассылать .exe руками
Можно было на инсталлере и остановиться: собрал, выложил, кинул людям ссылку. Но это значит, что при каждой новой версии надо снова всех обходить и просить переустановить. Для личного инструмента, которым пользуешься ты и пара коллег, это терпимо ровно один раз.
Поэтому я добавил автообновления. И работают они на удивление прямолинейно, без всякого своего сервера. При запуске приложение делает один запрос: какая там последняя опубликованная версия? Сравнивает с собственной. Если новая больше — и под твою систему есть готовый установщик — показывает аккуратное окошко: вышла новая версия, обновить?
Соглашаешься — приложение само скачивает установщик и тихо запускает его: без окон, закрыть старую версию, после установки перезапуститься. Жмёшь одну кнопку, приложение на секунду исчезает и открывается уже обновлённым. Никаких «скачайте новую версию с сайта».
А если сети нет или сервер отвечает ошибкой — проверка просто молча проваливается, и приложение работает дальше как ни в чём не бывало. Обновления не должны мешать работать оффлайн. А оффлайн тут — основной режим.
Бонусом — и под macOS
Раз уж зашла речь про две платформы, отдельно про Mac — потому что это был отдельный маленький квест.
Под Windows на выходе привычный .exe-инсталлер. Под macOS логика другая: приложение упаковывается в образ — отдельно под старые интеловские маки и отдельно под новые на Apple Silicon. Проверка обновлений работает так же. А вот финальный жест другой: вместо тихой установки приложение просто открывает скачанный образ, а дальше ты по традиции Mac перетаскиваешь иконку в «Программы».
И есть нюанс, на который наткнётся любой, кто раздаёт неподписанное приложение под Mac. У меня нет платного сертификата разработчика Apple, поэтому система при первом запуске честно ругается: «от неустановленного разработчика», открыть нельзя. Лечится известным обходом: правый клик по приложению → «Открыть» → подтвердить один раз, и дальше оно запускается как родное. Для личного инструмента — терпимо. Но в инструкцию для людей про этот шаг приходится писать прямым текстом, иначе человек упрётся в окно отказа и решит, что приложение сломано.
Что бы я сделал иначе
Раз уж пишу про закулисье — честно про то, что в следующий раз сделал бы по-другому.
Во-первых, раньше завёл бы единый источник версии. Я пришёл к нему через грабли с рассинхроном, а надо было закладывать с первого дня. Это пять строк, которые экономят целый класс глупых багов. Сейчас для меня это дефолт, а тогда казалось преждевременной аккуратностью.
Во-вторых, подписи приложений. Сейчас и под Windows, и под Mac приложение неподписанное, и человек при установке видит предупреждения системы. Для пары коллег это терпимо, но если бы я с самого начала целился в раздачу более широкому кругу — стоило бы заранее разобраться с сертификатами. Это не про код, а про доверие на старте.
А в-третьих — главный урок. Я бы не лез в облачную базу в 2024-м. Тогда мне казалось, что «серьёзное приложение» обязано иметь облачный бэкенд, и именно эта установка меня и утопила. Локального файла хватало с самого начала — я просто не верил, что так можно. Для личного инструмента простое локальное решение почти всегда правильнее «взрослого» облачного, к которому тебя подталкивает насмотренность на чужие большие проекты.
Главное — вообще не про UTM
И вот теперь то, ради чего я сел писать.
Эта история не про то, что маркетолог может заменить разработчика. Не может — и я первый об этом скажу. Там, где нужна настоящая инженерная глубина, нужен инженер. Я не пишу distributed-системы, не вылизываю архитектуру и не закладываюсь на нагрузку, которой у личной утилиты нет.
Она про другое. И разница между двумя моими заходами говорит об этом лучше всего. В 2024-м у меня была ровно та же идея и примерно тот же я — но инструменты не дотягивали, и проект умер. В 2026-м идея и я не изменились — изменились агенты, и проект доехал до инсталлера с автообновлениями.
Раньше, чтобы довести идею «хочу свою утилиту» до состояния «двойной клик, инсталлер, обновления сами прилетают», нужно было быть разработчиком. Или нанять его. А сегодня хватает другого: умения внятно поставить задачу, прочитать чужой код и не бросить дело на середине. В связке с ИИ-агентом это даёт законченный продукт. Не прототип «работает у меня на ноутбуке», а штуку с версиями, инсталлером и автообновлениями, которую не стыдно отдать другим.
Вот это смещение для меня важнее, чем сам UTM-конструктор. Готовый продукт перестал быть привилегией тех, кто пишет код профессионально. Он стал доступен тем, кто понимает, что должно получиться, и умеет довести агента до результата. Про это и есть разработка под ключ: ценность не в том, чтобы написать каждую строчку самому — а в том, чтобы собрать рабочее целое из готовых блоков и агентской помощи, и не бросить на середине.
Что в итоге
Пользуюсь каждый день. Ни одного аккаунта, ни одной подписки, ни одной строки моих данных на чужом сервере. История ссылок и шаблоны — в файле у меня на диске. Новая версия прилетает сама. Вся «сложная» часть — единый источник версии, две команды сборки и один запрос за обновлениями — уместилась в несколько вечеров. Тех самых вечеров, которые полтора года назад закончились бы ничем, а теперь дали готовый продукт.
И знаешь, что я из этого вынес? Не то, что я стал круче. А то, что планка «собрать себе рабочую штуку» опустилась так низко, что под неё теперь пролезает куда больше людей, чем принято думать. Я просто оказался одним из них.
P.S.
Если хочешь сам так научиться — не писать каждую строчку руками, а доводить ИИ-агента до готового результата — у меня на сайте лежит бесплатный курс Prompt.exe. 22 урока, без регистраций и «премиум-разблокировок». Я собирал его в первую очередь для мамы и друзей без техбэкграунда, но и маркетологам заходит.
А если хочешь не учиться, а сразу получить рабочее решение под свою задачу — пиши мне в личку, разберёмся. Я как раз про это: собрать из готовых блоков и агентской помощи работающее целое — и не бросить на середине.